#include "config.h"
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <dirent.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <sys/msg.h>
#include <signal.h>

#include <limits.h>

#if defined(__DARWIN__)
#include <sys/syslimits.h>
#endif

#include "saphire.h"
#include "saphire_inner.h"

//////////////////////////////////////////////////////////
// 宣言
//////////////////////////////////////////////////////////
string_obj* gAncestalMemoryPipeIn;
    // トップレベルの内部コマンド用のパイプ
string_obj* gAncestalMemoryPipeOut;
    // トップレベルの内部コマンド用のパイプ

char gTmpDir[PATH_MAX];
    // 一時ファイルの置き場

string_obj* gErrMsg;
    // エラーメッセージ
vector_obj* gSaphireProgCompletions;
    // 補完で使うプログラムの名前 vector of string
BOOL gKitutukiExit;
    // exitコマンドが実行されたらTRUE

enum eLineField gLineField = kLF;
    // saphireが文字列処理の内部コマンドで使う改行コード
enum eKanjiCode gKanjiCode = kUtf8;
    // saphireが文字列処理の内部コマンドや配列の添え字
    // で使う漢字コード


char* gStatmentKindStrs[kCommand+1] = {
    "subshell", "msleep", "true",  "false", "[",  
    "index", 
    "rindex", 
    "length",
    "lc",  "uc", "chomp", "substr", "eval", "fg", 
    "bg", "cpg", "jobs", 
    "rehash", 
    "kanjicode", "linefield", "var", "global", "calc", 
    "export", "print",  "puts",
    "load",
    "while", "break", "exit", "if", "split", 
    "x", "join", "select", "lines", "rows",
    "ary_new", "ary_add", "ary_erase", 
    "hash_new", "hash_add", "hash_erase",
    "match", "scan", "erase", "sub",  
    "read",
    "cd", 
    "add", "del",  "selector", "max", "min", "extname", 
    "parentname", "noextname", 
    "compile", "raise",
    "popd", "pushd",
    "++",
    "--",
    "+",
    "-",
    "*",
    "/",
    "mod",
    "pow",
    "sort",
    "range",
    "printf",
    "pomch",
    "def",
    "ptee",
    "pcat",
    "return",
    "minitscr",
    "mendwin",
    "mclear",
    "mclear_immediately",
    "mrefresh",
    "mmove",
    "mmove_immediately",
    "mprintw",
    "sweep",
    "show",
    "yeild",
    "each",
    "foreach",
    "abs",
    "fabs",
    "p",
    "try",
//    "parse",
    "inner_command",
    "command"
};

hash_obj* gFuncs;
    // ユーザー関数 sFunction*が入っている
    // キーは関数名
hash_obj* gGlobals;
    // グローバル変数 キーは変数名 内容はstring_obj*
hash_obj* gArrays;
    // グローバル変数の配列 キーは変数名
    // 内容は配列(vector_obj*) 配列の中にはstring_obj*
    // が入っている
hash_obj* gHashs;
    // グローバル変数の配列 キーは変数名
    // 内容はハッシュ(hash_obj*) ハッシュの中にはstring_obj*
    // が入っている

vector_obj* gStackFrame;
    // ローカル変数のスタック
    // 内容はhash_obj*
    // これのキーはローカル変数名
    // 内容はstring_obj*
enum eAppType gAppType;
    // アプリケーションの種類
BOOL gJobControl;
    // ジョブのコントロールをするかどうか
vector_obj* gJobs;
    // ジョブ sJob*の配列

char* gMagicNumber = "SAPHIRE_OBJ";  
    // オブジェクトファイルのマジックナンバー

hash_obj* gReadBuffers;
    // キー ファイル名
    // 内容 sRFd*
    // read用のファイルのリードバッファー

vector_obj* gGlobalPipes;
    // スタッカブルグローバルパイプのデータ

vector_obj* gQGlobalPipes;
    // キューグローバルパイプの読み込みんだデータ

string_obj* gGlobalPipe;
    // グローバルパイプの読み込みんだデータ

string_obj* gGlobalPipeNum[10];
    // ナンバーグローバルパイプの読み込みんだデータ

hash_obj* gRegexs;
    // 正規表現のキャッシュ
    // キーは正規表現の文字列
    // 内容はregex_t
hash_obj* gRegexsI;
    // 正規表現のキャッシュ(ignore case)
    // キーは正規表現の文字列
    // 内容はregex_t
hash_obj* gRegexsM;
    // 正規表現のキャッシュ(multi line)
    // キーは正規表現の文字列
    // 内容はregex_t
hash_obj* gRegexsIM;
    // 正規表現のキャッシュ(ignore case|multi line)
    // キーは正規表現の文字列
    // 内容はregex_t
hash_obj* gFReadBuffers;
    // 読み込みバッファ
    // キーはファイルディスクリプタ番号の文字列
    // 内容は読み込んだファイルの内容
hash_obj* gFWriteBuffers;
    // 書き込みバッファ
    // キーはファイルディスクリプタ番号の文字列
    // 内容は書き込んだファイルの内容

hash_obj* gInnerCommands;
    // 外部登録の内部コマンド
    // saphireを組み込んだアプリケーションで
    // 定義された内部コマンドのデータ
    // キーはコマンド名
    // 内容はsInnerCommand

vector_obj* gDirStack;
    // popd, pushdで使われるディレクトリスタック
    // 内容は string_obj*

vector_obj* gPSubTmpFiles;
    // プロセス置換で使われる一時ファイルを行末で消すために
    // 保存しておく配列

vector_obj* gStackTraceFName;
vector_obj* gStackTraceLineNum;
    // スタックの記録

BOOL gSaphireBackground;
    // Saphireがバックグラウンドで動いているかどうか

//////////////////////////////////////////////////////////
// 内部関数
//////////////////////////////////////////////////////////

// 引数がラインフィールドをポイントしているか
BOOL is_line_field(char* p)
{
    if(gLineField == kLF && *p == '\n') {
        return TRUE;
    }
    else if(gLineField == kCR && *p == '\r') {
        return TRUE;
    }
    else if(gLineField == kCRLF && *p == '\r' && *(p+1) == '\n') {
        return TRUE;
    }

    return FALSE;
}

// 引数がラインフィールドをポイントしているか2
BOOL is_line_field2(enum eLineField lf, char* p)
{
    if(lf == kLF && *p == '\n') {
        return TRUE;
    }
    else if(lf == kCR && *p == '\r') {
        return TRUE;
    }
    else if(lf == kCRLF && *p == '\r' && *(p+1) == '\n') {
        return TRUE;
    }

    return FALSE;
}

// 改行コードを取り除く
BOOL string_chomp(string_obj* str)
{
    char* s = string_c_str(str);
    const int len = strlen(s);

    if(len >= 2 && s[len-2] == '\r' && s[len-1] == '\n')
    {
        string_trunc(str, len-2);

        return TRUE;
    }
    else if(len >= 1) {
        if(s[len-1] == '\r' || s[len-1] == '\n') {
            string_trunc(str, len-1);
            return TRUE;
        }
    }

    return FALSE;
}

// 改行コードが無かったらつける(\nに統一)
void string_ponch(string_obj* str)
{
    string_chomp(str);
    string_push_back2(str, '\n');
}

// 改行コードを付ける(改行コードそのまま)
void string_pomch(string_obj* str, enum eLineField lf)
{
    char* s = string_c_str(str);
    const int len = strlen(s);

    if(len >= 2 && s[len-2] == '\r' && s[len-1] == '\n') {
    }
    else if(len >= 1 && (s[len-1] == '\r' || s[len-1] == '\n')) {
    }
    else {
        if(lf == kCR) {
            string_push_back2(str, '\r');
        }
        else if(lf == kCRLF) {
            string_push_back(str, "\r\n");
        }
        else {
            string_push_back2(str, '\n');
        }
    }
}

// closeするときにそのファイルディスクリプタに
// gFReadBuffersのバッファがあるがあれば削除
// gFWriteBUffersのバッファがあればバッファを書き込んで削除
BOOL xclose(int fd) 
{
    char buf[32];
    snprintf(buf, 32, "%d", fd);

    string_obj* str = hash_item(gFReadBuffers, buf);
    if(str) {
        string_delete(str);
        hash_erase(gFReadBuffers, buf);
    }

    close(fd);
    return TRUE;
}

// 配列変数にまつわる変数(_size)を正しい値に更新
void update_ary_env(char* key)
{
    vector_obj* v = hash_item(gArrays, key);

    if(v) {
        char name[128];

        snprintf(name, 128, "%s_size", key);
        char number[128];
        snprintf(number, 128, "%d", vector_size(v));

        string_obj* str = hash_item(gGlobals, name);
        if(str) string_delete(str);
        hash_put(gGlobals, name, STRING_NEW(number));
    }
    else {
        char name[128];
        snprintf(name, 128, "%s_size", key);
        string_obj* str = hash_item(gGlobals, name);
        if(str) {
            string_delete(str);
            hash_erase(gGlobals, name);
        }
    }
}

// 配列変数にまつわる変数を消す
void clear_ary_env(char* key)
{
    char name[128];

    snprintf(name, 128, "%s_size", key);

    string_obj* str = hash_item(gGlobals, name);
    if(str) string_delete(str);
    hash_erase(gGlobals, name);
}

// エラーメッセージを設定
void err_msg(char* msg, char* sname, int line)
{
    char* tmp = MALLOC(strlen(sname) + 128 + strlen(msg));
    snprintf(tmp, strlen(sname) + 128 + strlen(msg), "%s %d: %s\n", sname,  line, msg);

    string_put(gErrMsg, tmp);

    FREE(tmp);
}

// コマンド名からコマンドIDを探す
int get_command_kind(char* arg0)
{
    int i;
    for(i=0; i<kInnerCommand; i++) {
        if(strcmp(arg0, gStatmentKindStrs[i]) == 0) {
            return i;
        }
    }

    return kCommand;
}

// gotoのラベルを通すための関数
void null_fun()
{
}

// ポインターが差す文字列がリダイレクトかどうか
BOOL parse_is_redirect(char* p)
{
    if(*p == '1' && *(p+1) == '>') {
        return TRUE;
    }
    else if(*p == '2' && *(p+1) == '>') {
        return TRUE;
    }
    else if(*p == '>') {
        return TRUE;
    }
    else if(*p == '<') {
        return TRUE;
    }

    return FALSE;
}

// ポインタが差す文字列がリダイレクトかどうか2
BOOL parse_is_redirect2(char* p)
{
    return *p == '2' && *(p+1) == '>' && *(p+2) == '&' && *(p+3) == '1'
        || *p == '&' && *(p+1) == '>';
}

// 変数で使える文字かどうか
BOOL is_env_name_char(char c)
{
    return c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z' 
            || c >= '0' && c <= '9'
            || c == '_';
}

// グロブの区切り文字かどうか
BOOL is_glob_terminated_char(char* p)
{
    return *p=='\n' || *p=='\r' || *p==' ' || *p=='\t' || *p=='>' 
    || *p=='&' || *p =='$' || *p=='(' || *p==')'
    || *p=='\\' || *p=='|' || *p=='{' || *p=='}' 
    || *p==';'
    || *p=='\'' || *p=='"' || *p=='<' || *p=='>' || *p==':' 
    || *p=='!';
}

// 
BOOL is_terminated_char(char* p)
{
    return *p=='\'' || *p=='"' || *p == '$' || *p == '%' || *p== '~' || *p=='*'
    || *p=='?' || *p=='[' || *p=='<' || *p=='>' || *p==' ' || *p=='\t' 
    || *p==':'
    || *p=='{' || *p=='|' || *p=='&' || *p==';' || *p=='\n' || *p=='\r' 
    || *p=='#' || *p==0
    || *p=='(' || parse_is_redirect(p) || parse_is_redirect2(p);
}

// 文字列をクォートして返す
void saphire_get_quoted_fname(char* fname, string_obj* quoted_fname)
{
    char* p = fname;
    while(*p) {
        if(*p==' ' || *p=='*' || *p=='>' || *p=='&' || *p=='~'
                || *p=='#' || *p =='$' || *p=='(' || *p==')'
                || *p=='\\' || *p=='|' || *p=='[' || *p==']' || *p=='{' 
                || *p=='}' || *p==';'
                || *p=='\'' || *p=='"' || *p=='<' || *p=='>' || *p=='?' 
                || *p=='%' || *p=='@' || *p=='\n' || *p=='\t' || *p=='\r')
        {
            string_push_back2(quoted_fname, '\\');
            string_push_back2(quoted_fname, *p++);
        }
        else {
            string_push_back2(quoted_fname, *p++);
        }
    }
}


void get_quoted_linefield(char* str, string_obj* out)
{
    char* p = str;
    while(*p) {
        if(*p == '\n') {
            string_push_back(out, "\\n");
            p++;
        }
        else if(*p == '\r') {
            string_push_back(out, "\\r");
            p++;
        }
        else {
            string_push_back2(out, *p++);
        }
    }
}

/////////////////////////////////////////////////////////
// ローカル変数、グローバル変数に参照、代入
/////////////////////////////////////////////////////////
void saphire_set_local_var(char* name, char* value)
{
    if(vector_size(gStackFrame) == 0) {
        string_obj* var = hash_item(gGlobals, name);

        if(var) {
            string_put(var, value);
        }
        else {
            hash_put(gGlobals, name, STRING_NEW(value));
        }
    }
    else {
        hash_obj* top_stack 
            = vector_item(gStackFrame, vector_size(gStackFrame)-1);

        string_obj* var = hash_item(top_stack, name);

        if(var) {
            string_put(var, value);
        }
        else {
            hash_put(top_stack , name, STRING_NEW(value));
        }
    }
}

void saphire_set_global_var(char* name, char* value)
{
    saphire_delete_same_name_var(name);
    hash_put(gGlobals, name, STRING_NEW(value));
}

void saphire_delete_same_name_var(char* name)
{
    string_obj* global = hash_item(gGlobals, name);

    if(global) {
        string_delete(global);
        hash_erase(gGlobals, name);
    }

    vector_obj* v = hash_item(gArrays, name);

    if(v) {
        int i;
        for(i=0; i<vector_size(v); i++) {
            string_delete(vector_item(v, i));
        }
        vector_delete(v);
        hash_erase(gArrays, name);
        update_ary_env(name);
    }

    hash_obj* h = hash_item(gHashs, name);

    if(h) {
        hash_it* it = hash_loop_begin(h);
        while(it != NULL) {
            string_delete(hash_loop_item(it));
            it = hash_loop_next(it);
        }
        hash_delete(h);
        hash_erase(gHashs, name);
    }

    char* env = getenv(name);
    if(env) {
        unsetenv(name);
    }
}

char* saphire_get_global_var(char* name)
{
    string_obj* global = hash_item(gGlobals, name);

    if(global) {
        return string_c_str(global);
    }
    else {
        return NULL;
    }
}

hash_obj* saphire_get_hash(char* name)
{
    return hash_item(gHashs, name);
}

vector_obj* saphire_get_array(char* name)
{
    return hash_item(gArrays, name);
}

void saphire_set_array(char* name, vector_obj* array)
{
    saphire_delete_same_name_var(name);
    hash_put(gArrays, name, array);
    update_ary_env(name);
}

char* saphire_get_local_var(char* name)
{
    if(vector_size(gStackFrame) == 0) {
        string_obj* var = hash_item(gGlobals, name);
        if(var) 
            return string_c_str(var);
        else 
            return NULL;
    }
    else {
        hash_obj* top_stack 
            = vector_item(gStackFrame, vector_size(gStackFrame)-1);

        string_obj* var = hash_item(top_stack, name);

        if(var) {
            return string_c_str(var);
        }
        else {
            return NULL;
        }
    }
}

void saphire_delete_local_var(char* name)
{
    if(vector_size(gStackFrame) == 0) {
        string_obj* var = hash_item(gGlobals, name);
        if(var) {
            string_delete(var);
            hash_erase(gGlobals, name);
        }

        char name_size[1024];
        snprintf(name_size, 1024, "%s_size", name);

        string_obj* var_size = hash_item(gGlobals, name_size);

        if(var_size) {
            string_delete(var_size);
            hash_erase(gGlobals, name_size);
        }
    }
    else {
        hash_obj* top_stack 
            = vector_item(gStackFrame, vector_size(gStackFrame)-1);

        string_obj* var = hash_item(top_stack, name);

        if(var) {
            string_delete(var);
            hash_erase(top_stack, name);
        }

        char name_size[1024];
        snprintf(name_size, 1024, "%s_size", name);

        string_obj* var_size = hash_item(gGlobals, name_size);

        if(var_size) {
            string_delete(var_size);
            hash_erase(gGlobals, name_size);
        }
    }
}

//////////////////////////////////////////////////////////
// シグナル処理
//////////////////////////////////////////////////////////
volatile BOOL gKitutukiSigInt = FALSE;
volatile BOOL gKitutukiSigTstp = FALSE;
volatile BOOL gKitutukiSigCont = FALSE;
volatile BOOL gSigUser = FALSE;

void sig_int()
{
    gKitutukiSigInt = TRUE; 

#ifdef MDEBUG
fprintf(stderr, "SIGINT!!\n");
#endif
}

void sig_int_optc()
{
    sigchld_block(1);
    gKitutukiSigInt = TRUE; 
    sigchld_block(0);
}

void sig_tstp()
{
    gKitutukiSigInt = TRUE; 
#ifdef MDEBUG
fprintf(stderr, "SIGTSTP!!\n");
#endif
}

void sig_tstp_optc()
{
    sigchld_block(1);
    gKitutukiSigTstp = TRUE;
    kill(getpid(), SIGSTOP);
    sigchld_block(0);
}

void sig_cont_optc()
{
    sigchld_block(1);
    gKitutukiSigCont = TRUE;
//    killpg(getpgid(), SIGCONT);
    sigchld_block(0);
}

void sig_user()
{
    gSigUser = TRUE;
}

void sigchld_action(int signum, siginfo_t* info, void* ctx)
{
}

void sigchld_block(int block)
{
    sigset_t sigset;

    sigemptyset(&sigset);
    sigaddset(&sigset, SIGCHLD);

    if(sigprocmask(block?SIG_BLOCK:SIG_UNBLOCK, &sigset, NULL) != 0)
    {
        fprintf(stderr, "error\n");
        exit(1);
    }
}

void sigttou_block(int block)
{
    sigset_t sigset;

    sigemptyset(&sigset);
    sigaddset(&sigset, SIGTTOU);

    if(sigprocmask(block?SIG_BLOCK:SIG_UNBLOCK, &sigset, NULL) != 0)
    {
        fprintf(stderr, "error\n");
        exit(1);
    }
}

void saphire_restore_signal_default()
{
    struct sigaction sa;

    memset(&sa, 0, sizeof(sa));
    sa.sa_handler = SIG_DFL;
    if(sigaction(SIGPIPE, &sa, NULL) < 0) {
        perror("sigaction1");
        exit(1);
    }
    if(sigaction(SIGCHLD, &sa, NULL) < 0) {
        perror("sigaction1");
        exit(1);
    }
    if(sigaction(SIGINT, &sa, NULL) < 0) {
        perror("sigaction2");
        exit(1);
    }
    if(sigaction(SIGCONT, &sa, NULL) < 0) {
        perror("sigaction3");
        exit(1);
    }
    if(sigaction(SIGWINCH, &sa, NULL) < 0) {
        perror("sigaction4");
        exit(1);
    }

    if(sigaction(SIGPROF, &sa, NULL) < 0) {
        perror("sigaction5");
        exit(1);
    }

    if(sigaction(SIGTTIN, &sa, NULL) < 0) {
        perror("sigaction6");
        exit(1);
    }
    if(sigaction(SIGTTOU, &sa, NULL) < 0) {
        perror("sigaction7");
        exit(1);
    }
    if(sigaction(SIGTSTP, &sa, NULL) < 0) {
        perror("sigaction8");
        exit(1);
    }
    if(sigaction(SIGQUIT, &sa, NULL) < 0) {
        perror("sigaction9");
        exit(1);
    }
    if(sigaction(SIGUSR1, &sa, NULL) < 0) {
        perror("sigaction10");
        exit(1);
    }
}

void (*saphire_set_signal_other)() = NULL;

void saphire_set_signal()
{
    struct sigaction sa;
    memset(&sa, 0, sizeof(sa));
    sa.sa_sigaction = sigchld_action;
    sa.sa_flags = SA_RESTART|SA_SIGINFO;
    if(sigaction(SIGCHLD, &sa, NULL) < 0) {
        perror("sigaction1");
        exit(1);
    }

    memset(&sa, 0, sizeof(sa));
    sa.sa_flags = SA_SIGINFO;
    sa.sa_handler = sig_int;
    if(sigaction(SIGINT, &sa, NULL) < 0) {
        perror("sigaction2");
        exit(1);
    }
    sa.sa_flags = SA_SIGINFO;

    memset(&sa, 0, sizeof(sa));
    if(sigaction(SIGCONT, &sa, NULL) < 0) {
        perror("sigaction3");
        exit(1);
    }

    memset(&sa, 0, sizeof(sa));
    sa.sa_handler = SIG_IGN;
    if(sigaction(SIGWINCH, &sa, NULL) < 0) {
        perror("sigaction4");
        exit(1);
    }

    memset(&sa, 0, sizeof(sa));
    sa.sa_handler = SIG_IGN;
    sa.sa_flags = 0;
    if(sigaction(SIGTTOU, &sa, NULL) < 0) {
        perror("sigaction5");
        exit(1);
    }

    memset(&sa, 0, sizeof(sa));
    sa.sa_handler = SIG_IGN;
    sa.sa_flags = 0;
    if(sigaction(SIGTTIN, &sa, NULL) < 0) {
        perror("sigaction6");
        exit(1);
    }

    memset(&sa, 0, sizeof(sa));
    sa.sa_handler = sig_tstp;
    sa.sa_flags = 0;
    if(sigaction(SIGTSTP, &sa, NULL) < 0) {
        perror("sigaction7");
        exit(1);
    }

    memset(&sa, 0, sizeof(sa));
    sa.sa_handler = SIG_IGN;
    sa.sa_flags = 0;
    if(sigaction(SIGQUIT, &sa, NULL) < 0) {
        perror("sigaction8");
        exit(1);
    }

    memset(&sa, 0, sizeof(sa));
    sa.sa_handler = sig_user;
    sa.sa_flags = 0;
    if(sigaction(SIGUSR1, &sa, NULL) < 0) {
        perror("sigaction9");
        exit(1);
    }
    memset(&sa, 0, sizeof(sa));
    sa.sa_handler = SIG_IGN;
    sa.sa_flags = 0;
    if(sigaction(SIGPIPE, &sa, NULL) < 0) {
        perror("sigaction10");
        exit(1);
    }

    if(saphire_set_signal_other) saphire_set_signal_other();
}

void saphire_set_signal_optc()
{
    struct sigaction sa;
    memset(&sa, 0, sizeof(sa));
    sa.sa_sigaction = sigchld_action;
    sa.sa_flags = SA_SIGINFO|SA_RESTART;
    if(sigaction(SIGCHLD, &sa, NULL) < 0) {
        perror("sigaction1");
        exit(1);
    }

    memset(&sa, 0, sizeof(sa));
    sa.sa_flags = SA_SIGINFO;
    sa.sa_handler = sig_int_optc;
    //sa.sa_handler = SIG_IGN; //sig_int_optc;
    if(sigaction(SIGINT, &sa, NULL) < 0) {
        perror("sigaction2");
        exit(1);
    }
    sa.sa_flags = SA_SIGINFO;

    memset(&sa, 0, sizeof(sa));
    sa.sa_handler = sig_cont_optc;
    if(sigaction(SIGCONT, &sa, NULL) < 0) {
        perror("sigaction3");
        exit(1);
    }

    memset(&sa, 0, sizeof(sa));
    sa.sa_handler = SIG_IGN;
    if(sigaction(SIGWINCH, &sa, NULL) < 0) {
        perror("sigaction4");
        exit(1);
    }

    memset(&sa, 0, sizeof(sa));
    sa.sa_handler = SIG_IGN;
    sa.sa_flags = 0;
    if(sigaction(SIGTTOU, &sa, NULL) < 0) {
        perror("sigaction5");
        exit(1);
    }

    memset(&sa, 0, sizeof(sa));
    sa.sa_handler = SIG_IGN;
    sa.sa_flags = 0;
    if(sigaction(SIGTTIN, &sa, NULL) < 0) {
        perror("sigaction6");
        exit(1);
    }

    memset(&sa, 0, sizeof(sa));
    sa.sa_handler = sig_tstp_optc;
    sa.sa_flags = 0;
    if(sigaction(SIGTSTP, &sa, NULL) < 0) {
        perror("sigaction7");
        exit(1);
    }

    memset(&sa, 0, sizeof(sa));
    sa.sa_handler = SIG_IGN;
    sa.sa_flags = 0;
    if(sigaction(SIGQUIT, &sa, NULL) < 0) {
        perror("sigaction8");
        exit(1);
    }

    memset(&sa, 0, sizeof(sa));
    sa.sa_handler = sig_user;
    sa.sa_flags = 0;
    if(sigaction(SIGUSR1, &sa, NULL) < 0) {
        perror("sigaction9");
        exit(1);
    }
    memset(&sa, 0, sizeof(sa));
    sa.sa_handler = SIG_IGN;
    sa.sa_flags = 0;
    if(sigaction(SIGPIPE, &sa, NULL) < 0) {
        perror("sigaction10");
        exit(1);
    }

    if(saphire_set_signal_other) saphire_set_signal_other();
}

//////////////////////////////////////////////////////////
// ジョブ
//////////////////////////////////////////////////////////
sJob* sJob_new() 
{
    sJob* self = (sJob*)MALLOC(sizeof(sJob));
    
    self->mName = STRING_NEW("");
    self->mPGroup = 0;
    self->mPIDs = VECTOR_NEW(30);
    self->mTty = NULL;

    return self;
}

void sJob_delete(sJob* self)
{
    string_delete(self->mName);
    vector_delete(self->mPIDs);
    if(self->mTty) FREE(self->mTty);

    FREE(self);
}

/////////////////////////////////////////////////////////
// リダイレクト
/////////////////////////////////////////////////////////
sRedirect* sRedirect_new(enum eRedirect type, int fd, char* fname)
{
    sRedirect* self = (sRedirect*)MALLOC(sizeof(sRedirect));
    self->mType = type;
    self->mFd = fd;
    self->mFName = STRING_NEW(fname);
    return self;
}

sRedirect* sRedirect_new2(sRedirect* redirect)
{
    sRedirect* self = (sRedirect*)MALLOC(sizeof(sRedirect));
    self->mType = redirect->mType;
    self->mFd = redirect->mFd;
    self->mFName = STRING_NEW(string_c_str(redirect->mFName));
    return self;
}

void sRedirect_delete(sRedirect* self)
{
    string_delete(self->mFName);
    FREE(self);
}

void sRedirect_save(sRedirect* self, int fd)
{
    unsigned char uc;
    int n;
    
    uc = self->mType;
    if(write(fd, &uc, sizeof(unsigned char)) < 0) {
        perror("write");
        exit(1);
    }
    
    n = self->mFd;
    if(write(fd, &n, sizeof(int)) < 0) {
        perror("write");
        exit(1);
    }
    
    n = string_size(self->mFName);
    if(write(fd, &n, sizeof(int)) < 0) {
        perror("write");
        exit(1);
    }
    
    if(write(fd, string_c_str(self->mFName)
                , string_size(self->mFName)) < 0) 
    {
        perror("write");
        exit(1);
    }
}

void sRedirect_view(sRedirect* self)
{
    printf("fd %d file %s\n", self->mFd, string_c_str(self->mFName));
}

sRedirect* sRedirect_load(int fd)
{
    unsigned char uc;
    int n;
    
    sRedirect* self = (sRedirect*)MALLOC(sizeof(sRedirect));
    
    if(read(fd, &uc, sizeof(unsigned char)) < 0) {
        perror("read");
        exit(1);
    }
    self->mType = uc;
    
    if(read(fd, &n, sizeof(int)) < 0) {
        perror("read");
        exit(1);
    }
    self->mFd = n;
    
    if(read(fd, &n, sizeof(int)) < 0) {
        perror("read");
        exit(1);
    }
    
    char* buf = MALLOC(n+1);
    if(read(fd, buf, n) < 0) {
        perror("read");
        exit(1);
    }
    buf[n] = 0;
    self->mFName = STRING_NEW(buf);
    FREE(buf);
    
    return self;
}


sRedirectTmp* sRedirectTmp_new(enum eRedirect type, int fd)
{
    sRedirectTmp* self = (sRedirectTmp*)MALLOC(sizeof(sRedirectTmp));
    self->mType = type;
    self->mFd = fd;

    return self;
}

void sRedirectTmp_delete(sRedirectTmp* self)
{
    FREE(self);
}

sVar* sVar_new(MANAGED string_obj* name, MANAGED string_obj* index, MANAGED string_obj* index2, MANAGED string_obj* delimiter)
{
    sVar* self = MALLOC(sizeof(sVar));
    self->mName = name;
    self->mIndex = index;
    self->mIndex2 = index2;
    self->mDelimiter = delimiter;
    return self;
}

sVar* sVar_new2(sVar* var)
{
    sVar* self = MALLOC(sizeof(sVar));
    self->mName = STRING_NEW(string_c_str(var->mName));
    if(var->mIndex) {
        self->mIndex = STRING_NEW(string_c_str(var->mIndex));
    }
    else {
        self->mIndex = NULL;
    }
    if(var->mIndex2) {
        self->mIndex2 = STRING_NEW(string_c_str(var->mIndex2));
    }
    else {
        self->mIndex2 = NULL;
    }
    self->mDelimiter = STRING_NEW(string_c_str(var->mDelimiter));
    return self;
}

void sVar_delete(sVar* self)
{
    string_delete(self->mName);
    if(self->mIndex) string_delete(self->mIndex);
    if(self->mIndex2) string_delete(self->mIndex2);
    string_delete(self->mDelimiter);

    FREE(self);
}

sVar* sVar_load(int fd)
{
    sVar* self = MALLOC(sizeof(sVar));
    int n;
    char* buf;

    if(read(fd, &n, sizeof(int)) < 0) {
        perror("read");
        exit(1);
    }
    
    buf = MALLOC(n+1);
    if(read(fd, buf, n) < 0) {
        perror("read");
        exit(1);
    }
    buf[n] = 0;
    self->mName = STRING_NEW(buf);
    FREE(buf);

    if(read(fd, &n, sizeof(int)) < 0) {
        perror("read");
        exit(1);
    }
    
    buf = MALLOC(n+1);
    if(read(fd, buf, n) < 0) {
        perror("read");
        exit(1);
    }
    buf[n] = 0;
    self->mIndex = STRING_NEW(buf);
    FREE(buf);

    if(read(fd, &n, sizeof(int)) < 0) {
        perror("read");
        exit(1);
    }
    
    buf = MALLOC(n+1);
    if(read(fd, buf, n) < 0) {
        perror("read");
        exit(1);
    }
    buf[n] = 0;
    self->mIndex2 = STRING_NEW(buf);
    FREE(buf);

    if(read(fd, &n, sizeof(int)) < 0) {
        perror("read");
        exit(1);
    }
    
    buf = MALLOC(n+1);
    if(read(fd, buf, n) < 0) {
        perror("read");
        exit(1);
    }
    buf[n] = 0;
    self->mDelimiter = STRING_NEW(buf);
    FREE(buf);

    return self;
}

void sVar_save(sVar* self, int fd)
{
    string_obj* str = self->mName;
    int n = string_size(str);

    if(write(fd, &n, sizeof(int)) < 0) {
        perror("write");
        exit(1);
    }

    if(write(fd, string_c_str(str), n) < 0) {
        perror("write");
        exit(1);
    }

    str = self->mIndex;
    n = string_size(str);

    if(write(fd, &n, sizeof(int)) < 0) {
        perror("write");
        exit(1);
    }
    if(write(fd, string_c_str(str), n) < 0) {
        perror("write");
        exit(1);
    }

    str = self->mIndex2;
    n = string_size(str);

    if(write(fd, &n, sizeof(int)) < 0) {
        perror("write");
        exit(1);
    }

    if(write(fd, string_c_str(str), n) < 0) {
        perror("write");
        exit(1);
    }

    str = self->mDelimiter;
    n = string_size(str);

    if(write(fd, &n, sizeof(int)) < 0) {
        perror("write");
        exit(1);
    }

    if(write(fd, string_c_str(str), n) < 0) {
        perror("write");
        exit(1);
    }
}

sArg* sArg_new(int kind, void* body)
{
    sArg* self = MALLOC(sizeof(sArg));

    self->mKind = kind;
    self->mBody = body;

    return self;
}

sArg* sArg_new2(sArg* arg)
{
    sArg* self = MALLOC(sizeof(sArg));

    self->mKind = arg->mKind;
    switch(self->mKind) {
    case 0:
        self->mBody = STRING_NEW(string_c_str(arg->mBody));
        break;

    case 1:
        self->mBody = sVar_new2(arg->mBody);
        break;

    case 2:
        self->mBody = sStatments_new2(arg->mBody);
        break;

    case 3:
        self->mBody = sVar_new2(arg->mBody);
        break;

    case 4:
        self->mBody = sStatments_new2(arg->mBody);
        break;

    case 5:
        self->mBody = sBlock_new2(arg->mBody);
        break;
    }

    return self;
}

void sArg_delete(sArg* self)
{
    switch(self->mKind) {
    case 0:
        string_delete(self->mBody);
        break;

    case 1:
        sVar_delete(self->mBody);
        break;

    case 2:
        sStatments_delete(self->mBody);
        break;

    case 3:
        sVar_delete(self->mBody);
        break;

    case 4:
        sStatments_delete(self->mBody);
        break;

    case 5:
        sBlock_delete(self->mBody);
        break;
    }

    FREE(self);
}

void sArg_save(sArg* self, int fd)
{
    int n = self->mKind;
    if(write(fd, &n,sizeof(int)) < 0) {
        perror("write");
        exit(1);
    }

    switch(self->mKind) {
        case 0: {
            string_obj* str = self->mBody;
            int n = string_size(str);

            if(write(fd, &n, sizeof(int)) < 0) {
                perror("write");
                exit(1);
            }

            if(write(fd, string_c_str(str), n) < 0) {
                perror("write");
                exit(1);
            }
            }
            break;

        case 1:
            sVar_save(self->mBody, fd);
            break;

        case 2:
            sStatments_save(self->mBody, fd);
            break;

        case 3:
            sVar_save(self->mBody, fd);
            break;

        case 4:
            sStatments_save(self->mBody, fd);
            break;

        case 5:
            sBlock_save(self->mBody, fd);
            break;
    }
}

sArg* sArg_load(int fd)
{
    sArg* self = MALLOC(sizeof(sArg));

    int n;
    if(read(fd, &n, sizeof(int)) < 0) {
        perror("read");
        exit(1);
    }
    self->mKind = n;

    switch(self->mKind) {
    case 0: {
        int m;
        if(read(fd, &m, sizeof(int)) < 0) {
            perror("read");
            exit(1);
        }
        char* buf = MALLOC(sizeof(char)*(m+1));
        if(read(fd, buf, m) < 0) {
            perror("read");
            exit(1);
        }
        buf[m] = 0;

        self->mBody = STRING_NEW(buf);
        FREE(buf);
        }
        break;

    case 1:
        self->mBody = sVar_load(fd);
        break;

    case 2:
        self->mBody = sStatments_load(fd);
        break;

    case 3:
        self->mBody = sVar_load(fd);
        break;

    case 4:
        self->mBody = sStatments_load(fd);
        break;

    case 5:
        self->mBody = sBlock_load(fd);
        break;
    }

    return self;
}

//////////////////////////////////////////////////////////
// 変数
//////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////
// コマンドライン引数
//////////////////////////////////////////////////////////
sArgTmp* sArgTmp_new(int kind, void* body)
{
    sArgTmp* self = MALLOC(sizeof(sArgTmp));

    self->mKind = kind;
    self->mBody = body;

    return self;
}

void sArgTmp_delete(sArgTmp* self)
{
    switch(self->mKind)
    {
    case 0:
        string_delete(self->mBody);
        break;

    case 1:
        if(self->mBody) sVar_delete(self->mBody);
        break;

    case 2:
        sRedirectTmp_delete(self->mBody);
        break;

    case 3:
        if(self->mBody) sStatments_delete(self->mBody);
        break;

    case 4:
        if(self->mBody) sVar_delete(self->mBody);
        break;

    case 5:
        if(self->mBody) sStatments_delete(self->mBody);
        break;

    case 6:
        if(self->mBody) sBlock_delete(self->mBody);
        break;
    }

    FREE(self);
}

//////////////////////////////////////////////////////////
// sSubshell
//////////////////////////////////////////////////////////
sSubshell* sSubshell_new(MANAGED sStatments* contents)
{
    sSubshell* self = MALLOC(sizeof(sSubshell));

    self->contents = contents;

    return self;
}

sSubshell* sSubshell_new2(sSubshell* subshell)
{
    sSubshell* self = MALLOC(sizeof(sSubshell));

    self->contents = sStatments_new2(subshell->contents);

    return self;
}

void sSubshell_delete(sSubshell* self)
{
    sStatments_delete(self->contents);

    FREE(self);
}

void sSubshell_save(sSubshell* self, int fd)
{
    sStatments_save(self->contents, fd);
}

sSubshell* sSubshell_load(int fd)
{
    sSubshell* self = MALLOC(sizeof(sSubshell));
    
    self->contents = sStatments_load(fd);
    
    return self;
}

void sSubshell_view(sSubshell* self)
{
    sStatments_view(self->contents);
}

//////////////////////////////////////////////////////////
// sTry
//////////////////////////////////////////////////////////
sTry* sTry_new(MANAGED sStatments* loops, MANAGED sStatments* loops2)
{
    sTry* self = MALLOC(sizeof(sTry));

    self->loops = loops;
    self->loops2 = loops2;

    return self;
}

sTry* sTry_new2(sTry* try_)
{
    sTry* self = MALLOC(sizeof(sTry));

    self->loops = sStatments_new2(try_->loops);
    self->loops2 = sStatments_new2(try_->loops2);

    return self;
}

void sTry_delete(sTry* self)
{
    sStatments_delete(self->loops);
    sStatments_delete(self->loops2);

    FREE(self);
}

void sTry_view(sTry* self)
{
    sStatments_view(self->loops);
    sStatments_view(self->loops2);
}

void sTry_save(sTry* self, int fd)
{
    sStatments_save(self->loops, fd);
    sStatments_save(self->loops2, fd);
}

sTry* sTry_load(int fd)
{
    sTry* self = MALLOC(sizeof(sTry));
    
    self->loops = sStatments_load(fd);
    self->loops2 = sStatments_load(fd);
    
    return self;
}

//////////////////////////////////////////////////////////
// sWhile
//////////////////////////////////////////////////////////
sWhile* sWhile_new(MANAGED sStatments* joukensiki, MANAGED sStatments* contents)
{
    sWhile* self = MALLOC(sizeof(sWhile));

    self->joukensiki = joukensiki;
    self->contents = contents;

    return self;
}

sWhile* sWhile_new2(sWhile* while_)
{
    sWhile* self = MALLOC(sizeof(sWhile));

    self->joukensiki = sStatments_new2(while_->joukensiki);
    self->contents = sStatments_new2(while_->contents);

    return self;
}

void sWhile_delete(sWhile* self)
{
    sStatments_delete(self->joukensiki);
    sStatments_delete(self->contents);

    FREE(self);
}

void sWhile_view(sWhile* self)
{
    sStatments_view(self->joukensiki);
    sStatments_view(self->contents);
}

void sWhile_save(sWhile* self, int fd)
{
    sStatments_save(self->joukensiki, fd);
    sStatments_save(self->contents, fd);
}

sWhile* sWhile_load(int fd)
{
    sWhile* self = MALLOC(sizeof(sWhile));
    
    self->joukensiki = sStatments_load(fd);
    self->contents = sStatments_load(fd);
    
    return self;
}

//////////////////////////////////////////////////////////
// sIf
//////////////////////////////////////////////////////////
sIf* sIf_new(MANAGED vector_obj* joukensiki_list, MANAGED vector_obj* contents_list)
{
    sIf* self = MALLOC(sizeof(sIf));
    
    self->joukensiki_list = joukensiki_list;
    self->contents_list = contents_list;

    return self;
}

sIf* sIf_new2(sIf* if_)
{
    sIf* self = MALLOC(sizeof(sIf));
    
    self->joukensiki_list = VECTOR_NEW(10);
    self->contents_list = VECTOR_NEW(10);

    int i;
    for(i=0; i<vector_size(if_->joukensiki_list); i++) {
        vector_add(self->joukensiki_list, sStatments_new2(vector_item(if_->joukensiki_list, i)));
    }
    for(i=0; i<vector_size(if_->contents_list); i++) {
        vector_add(self->contents_list, sStatments_new2(vector_item(if_->contents_list, i)));
    }

    return self;
}

void sIf_delete(sIf* self)
{
    int i;
    for(i=0; i<vector_size(self->joukensiki_list); i++) {
        sStatments_delete(vector_item(self->joukensiki_list, i));
    }
    vector_delete(self->joukensiki_list);

    for(i=0; i<vector_size(self->contents_list); i++) {
        sStatments_delete(vector_item(self->contents_list, i));
    }
    vector_delete(self->contents_list);

    FREE(self);
}

void sIf_view(sIf* self)
{
    int i;
    for(i=0; i<vector_size(self->joukensiki_list); i++) {
        sStatments_view(vector_item(self->joukensiki_list, i));
    }

    for(i=0; i<vector_size(self->contents_list); i++) {
        sStatments_view(vector_item(self->contents_list, i));
    }
}

void sIf_save(sIf* self, int fd)
{
    int n;
    unsigned char c;
    int i;
    
    n = vector_size(self->joukensiki_list);
    if(write(fd, &n, sizeof(int)) < 0) {
        perror("write");
        exit(1);
    }
    for(i=0; i<n; i++) {
        sStatments_save(vector_item(self->joukensiki_list, i), fd);
    }
    
    n = vector_size(self->contents_list);
    if(write(fd, &n, sizeof(int)) < 0) {
        perror("write");
        exit(1);
    }
    for(i=0; i<n; i++) {
        sStatments_save(vector_item(self->contents_list, i), fd);
    }
}

sIf* sIf_load(int fd)
{
    int n;
    int i;

    sIf* self = MALLOC(sizeof(sIf));
    
    self->joukensiki_list = VECTOR_NEW(50);
    if(read(fd, &n, sizeof(int)) < 0) {
        perror("read");
        exit(1);
    }
    for(i=0; i<n; i++) {
        vector_add(self->joukensiki_list, sStatments_load(fd));
    }

    self->contents_list = VECTOR_NEW(50);
    if(read(fd, &n, sizeof(int)) < 0) {
        perror("read");
        exit(1);
    }
    for(i=0; i<n; i++) {
        vector_add(self->contents_list, sStatments_load(fd));
    }
    
    return self;
}

/////////////////////////////////////////////////////////
// コマンド ls -al
/////////////////////////////////////////////////////////
#ifdef MDEBUG 

sCommand* sCommand_new(const char* file, int line, const char* function)
{
    sCommand* self = CheckMemLeak_Malloc(sizeof(sCommand), file, line, "COMMAND_NEW");

    self->mKind = kCommand;
    self->mArgTmps = VECTOR_NEW(50);
    self->mArgs = VECTOR_NEW(50);

    self->mRedirects = VECTOR_NEW(10);

    self->mExtra = NULL;

    return self;
}

#define COMMAND_NEW() sCommand_new(__FILE__, __LINE__, __FUNCTION__)

#else

sCommand* sCommand_new()
{
    sCommand* self = MALLOC(sizeof(sCommand));

    self->mKind = kCommand;
    self->mArgTmps = VECTOR_NEW(50);
    self->mArgs = VECTOR_NEW(50);

    self->mRedirects = VECTOR_NEW(10);

    self->mExtra = NULL;

    return self;
}

#define COMMAND_NEW() sCommand_new()

#endif

sCommand* sCommand_new2(sCommand* command)
{
    sCommand* self = MALLOC(sizeof(sCommand));

    self->mKind = command->mKind;
    self->mArgTmps = VECTOR_NEW(50);

    self->mArgs = VECTOR_NEW(50);
    int i;
    for(i=0; i<vector_size(command->mArgs); i++) {
        vector_add(self->mArgs, sArg_new2(vector_item(command->mArgs, i)));
    }
    self->mRedirects = VECTOR_NEW(10);
    for(i=0; i<vector_size(command->mRedirects); i++) {
        vector_add(self->mRedirects, sRedirect_new2(vector_item(command->mRedirects, i)));
    }
    /// mExtra ///
    switch(command->mKind) {
    case kIf:
        self->mExtra = sIf_new2(command->mExtra);
        break;
        
    case kWhile:
        self->mExtra = sWhile_new2(command->mExtra);
        break;
        
    case kTry:
        self->mExtra = sTry_new2(command->mExtra);
        break;

    case kSubshell:
        self->mExtra = sSubshell_new2(command->mExtra);
        break;

    case kDef:
        self->mExtra = sFunction_new2(command->mExtra);
        break;

    default:
        self->mExtra = NULL;
        break;
    }

    return self;
}


void sCommand_view(sCommand* self)
{
    printf("self->mKind %s\n", gStatmentKindStrs[self->mKind]);

    int i;
    for(i=0; i<vector_size(self->mArgs); i++) {
        sArg* arg = vector_item(self->mArgs, i);
        if(arg->mKind == 0)
            printf("self->mArgs[%d] %s\n", i, string_c_str(arg->mBody));
    }
    for(i=0; i<vector_size(self->mRedirects); i++) {
        printf("self->mRedirects[%d]\n", i);
        sRedirect_view(vector_item(self->mRedirects, i));
    }

    /// mExtra ///
    switch(self->mKind) {
    case kIf:
        sIf_view(self->mExtra);
        break;
        
    case kWhile:
        sWhile_view(self->mExtra);
        break;
        
    case kTry:
        sTry_view(self->mExtra);
        break;

    case kSubshell:
        sSubshell_view(self->mExtra);
        break;

    case kDef:
        sFunction_view(self->mExtra);
        break;
    }
}

void sCommand_save(sCommand* self, int fd)
{
    int i;
    unsigned char uc;
    int n;
    
    /// mKind ///
    uc = self->mKind;
    if(write(fd, &uc, sizeof(unsigned char)) < 0) {
        perror("write");
        exit(1);
    }
    
    /// mArgs ///
    n = vector_size(self->mArgs);
    if(write(fd, &n, sizeof(int)) < 0) {
        perror("write");
        exit(1);
    }
    
    for(i=0; i<vector_size(self->mArgs); i++) {
        sArg* arg = vector_item(self->mArgs, i);
        sArg_save(arg, fd);
    }

    /// mRedirects ///
    n = vector_size(self->mRedirects);
    if(write(fd, &n, sizeof(int)) < 0) {
        perror("write");
        exit(1);
    }

    for(i=0; i<n; i++) {
        sRedirect_save(vector_item(self->mRedirects, i), fd);
    }

    /// mExtra ///
    switch(self->mKind) {
    case kIf:
        sIf_save(self->mExtra, fd);
        break;
        
    case kWhile:
        sWhile_save(self->mExtra, fd);
        break;

    case kTry:
        sTry_save(self->mExtra, fd);
        break;
        
    case kSubshell:
        sSubshell_save(self->mExtra, fd);
        break;
        
    case kDef:
        sFunction_save(self->mExtra, fd);
        break;
    }
}

sCommand* sCommand_load(int fd)
{
    int n, m;
    unsigned char uc;
    int i;
    
    sCommand* self = MALLOC(sizeof(sCommand));
    self->mArgTmps = VECTOR_NEW(30);
    
    /// mKind ///
    if(read(fd, &uc, sizeof(unsigned char)) < 0) {
        perror("read");
        exit(1);
    }
    self->mKind = uc;
    
    /// mArgs ///
    self->mArgs = VECTOR_NEW(30);
    if(read(fd, &n, sizeof(int)) < 0) {
        perror("read");
        exit(1);
    }
    for(i=0; i<n; i++) {
        vector_add(self->mArgs, sArg_load(fd));
    }

    /// mRedirects ///
    self->mRedirects = VECTOR_NEW(10);
    if(read(fd, &n, sizeof(int)) < 0) {
        perror("read");
        exit(1);
    }

    for(i=0; i<n; i++) {
        vector_add(self->mRedirects, sRedirect_load(fd));
    }

    
    /// mExtra ///
    switch(self->mKind) {
    case kIf:
        self->mExtra = sIf_load(fd);
        break;
        
    case kWhile:
        self->mExtra = sWhile_load(fd);
        break;

    case kTry:
        self->mExtra = sTry_load(fd);
        break;
        
    case kSubshell:
        self->mExtra = sSubshell_load(fd);
        break;
        
    case kDef:
        self->mExtra = sFunction_load(fd);
        break;
        
    default:
        self->mExtra = NULL;
    }
    
    return self;
}

void sCommand_delete(sCommand* self)
{
    int i;

    for(i=0; i<vector_size(self->mArgTmps); i++) {
        sArgTmp_delete(vector_item(self->mArgTmps, i));
    }
    vector_delete(self->mArgTmps);

    for(i=0; i<vector_size(self->mArgs); i++) {
        sArg_delete(vector_item(self->mArgs, i));
    }
    vector_delete(self->mArgs);

    for(i=0; i<vector_size(self->mRedirects); i++) {
        sRedirect_delete(vector_item(self->mRedirects, i));
    }
    vector_delete(self->mRedirects);

    switch(self->mKind) {
    case kIf:
        sIf_delete(self->mExtra);
        break;

    case kWhile:
        sWhile_delete(self->mExtra);
        break;

    case kTry:
        sTry_delete(self->mExtra);
        break;

    case kSubshell:
        sSubshell_delete(self->mExtra);
        break;

    case kDef:
        sFunction_delete(self->mExtra);
        break;
    }

    FREE(self);
}

/////////////////////////////////////////////////////////
// 文 ls -al | grep aaa
/////////////////////////////////////////////////////////
#ifdef MDEBUG

sStatment* sStatment_new(const char* file, int line, const char* function)
{
    sStatment* self = CheckMemLeak_Malloc(sizeof(sStatment), file, line, "STATMENT_NEW");

    self->mCommands = VECTOR_NEW(30);
    self->mTerminated = kTNormal;
    self->mFName = STRING_NEW("");
    self->mLine = 0;
    self->mTitle = STRING_NEW("");
    self->mNotEvaled = NULL;
    self->mBackground = FALSE;
    self->mRCodeReverse = FALSE;

    self->mGlobalPipeIn = FALSE;
    self->mGlobalPipeOut = FALSE;

    return self;
}

#define STATMENT_NEW() sStatment_new(__FILE__, __LINE__, __FUNCTION__)

#else

sStatment* sStatment_new()
{
    sStatment* self = MALLOC(sizeof(sStatment));

    self->mCommands = VECTOR_NEW(30);
    self->mTerminated = kTNormal;
    self->mFName = STRING_NEW("");
    self->mLine = 0;
    self->mTitle = STRING_NEW("");
    self->mNotEvaled = NULL;
    self->mBackground = FALSE;
    self->mRCodeReverse = FALSE;

    self->mGlobalPipeIn = FALSE;
    self->mGlobalPipeOut = FALSE;

    return self;
}

#define STATMENT_NEW() sStatment_new()

#endif

sStatment* sStatment_new2(sStatment* statment)
{
    sStatment* self = MALLOC(sizeof(sStatment));

    self->mCommands = VECTOR_NEW(30);
    int i;
    for(i=0; i<vector_size(statment->mCommands); i++) {
        vector_add(self->mCommands, sCommand_new2(vector_item(statment->mCommands, i)));
    }
    self->mTerminated = statment->mTerminated;
    self->mFName = STRING_NEW(string_c_str(statment->mFName));
    self->mLine = statment->mLine;
    self->mTitle = STRING_NEW(string_c_str(statment->mTitle));
    if(statment->mNotEvaled) {
        self->mNotEvaled = STRING_NEW(string_c_str(statment->mNotEvaled));
    }
    else {
        self->mNotEvaled = NULL;
    }
    self->mBackground = statment->mBackground;
    self->mRCodeReverse = statment->mRCodeReverse;

    self->mGlobalPipeIn = statment->mGlobalPipeIn;
    self->mGlobalPipeOut = statment->mGlobalPipeOut;

    return self;
}


void sStatment_view(sStatment* self)
{
    printf("command number %d\n", vector_size(self->mCommands));
    int i;
    for(i=0; i<vector_size(self->mCommands); i++) {
        printf("command %d ---\n", i);
        sCommand_view(vector_item(self->mCommands, i));
    }

    if(self->mTerminated == kTNormal)
        printf("self->mTerminated kTNormal\n");
    else if(self->mTerminated == kTOrOr)
        printf("self->mTerminated kTOrOr\n");
    else if(self->mTerminated == kTAndAnd)
        printf("self->mTerminated kTAndAnd\n");

    printf("self->mFName %s\n", string_c_str(self->mFName));
    printf("self->mLine %d\n", self->mLine);
    printf("self->mTitle %s\n", string_c_str(self->mTitle));

    if(self->mNotEvaled) 
        printf("self->mNotEvaled %s\n", string_c_str(self->mNotEvaled));
    else
        printf("self->mNotEvaled NULL\n");

    if(self->mBackground) 
        printf("self->mBackground true\n");
    else
        printf("self->mBackground false\n");

    printf("self->mGlobalPipeIn %d\n", self->mGlobalPipeIn);
    printf("self->mGlobalPipeOut %d\n", self->mGlobalPipeOut);
}

void sStatment_delete(sStatment* self)
{
    int i;
    for(i=0; i<vector_size(self->mCommands); i++) {
        sCommand_delete(vector_item(self->mCommands, i));
    }
    vector_delete(self->mCommands);

    string_delete(self->mFName);
    string_delete(self->mTitle);

    if(self->mNotEvaled) { string_delete(self->mNotEvaled); }

    FREE(self);
}

void sStatment_save(sStatment* self, int fd)
{
    int n;
    unsigned char c;
    int i;
    
    n = vector_size(self->mCommands);
    if(write(fd, &n, sizeof(int)) < 0) {
        perror("write");
        exit(1);
    }
    
    for(i=0; i<n; i++) {
        sCommand* item = vector_item(self->mCommands, i);
        sCommand_save(item, fd);
    }
    
    c = self->mTerminated;
    if(write(fd, &c, sizeof(unsigned char)) < 0) {
        perror("write");
        exit(1);
    }
    
    n = string_size(self->mFName);
    if(write(fd, &n, sizeof(int)) < 0) {
        perror("write");
        exit(1);
    }
    int wret = write(fd, string_c_str(self->mFName), n);
    
    n = self->mLine;
    if(write(fd, &n, sizeof(int)) < 0) {
        perror("write");
        exit(1);
    }
    
    n = string_size(self->mTitle);
    if(write(fd, &n, sizeof(int)) < 0) {
        perror("write");
        exit(1);
    }
    if(write(fd, string_c_str(self->mTitle), n) < 0) {
        perror("write");
        exit(1);
    }
    
    if(self->mNotEvaled) {
        n = 1;
        if(write(fd, &n, sizeof(int)) < 0) {
            perror("write");
            exit(1);
        }
        
        n = string_size(self->mNotEvaled);
        if(write(fd, &n, sizeof(int)) < 0) {
            perror("write");
            exit(1);
        }
        if(write(fd, string_c_str(self->mNotEvaled), n) < 0) {
            perror("write");
            exit(1);
        }
    }
    else {
        n = 0;
        if(write(fd, &n, sizeof(int)) < 0) {
            perror("write");
            exit(1);
        }
    }
    
    n = self->mBackground;
    if(write(fd, &n, sizeof(int)) < 0) {
        perror("write");
        exit(1);
    }

    // mGlobalPipeIn //
    n = self->mGlobalPipeIn;
    if(write(fd, &n, sizeof(int)) < 0) {
        perror("write");
        exit(1);
    }

    // mGlobalPipeOut //
    n = self->mGlobalPipeOut;
    if(write(fd, &n, sizeof(int)) < 0) {
        perror("write");
        exit(1);
    }

    // mGlobalPipeInNum //
    n = self->mGlobalPipeInNum;
    if(write(fd, &n, sizeof(int)) < 0) {
        perror("write");
        exit(1);
    }

    // mGlobalPipeOutNum //
    n = self->mGlobalPipeOutNum;
    if(write(fd, &n, sizeof(int)) < 0) {
        perror("write");
        exit(1);
    }
}

sStatment* sStatment_load(int fd)
{
    int n;
    unsigned char c;
    int i;
    
    sStatment* self = MALLOC(sizeof(sStatment));
    
    self->mCommands = VECTOR_NEW(30);
    self->mRCodeReverse = FALSE;
    
    if(read(fd, &n, sizeof(int)) < 0) {
        perror("read");
        exit(1);
    }
    for(i=0; i<n; i++) {
        vector_add(self->mCommands, sCommand_load(fd));
    }
    
    if(read(fd, &c, sizeof(unsigned char)) < 0) {
        perror("read");
        exit(1);
    }
    self->mTerminated = c;
    
    if(read(fd, &n, sizeof(int)) < 0) {
        perror("read");
        exit(1);
    }
    char* buf = MALLOC(n+1);
    if(read(fd, buf, n) < 0) {
        perror("read");
        exit(1);
    }
    buf[n] = 0;
    self->mFName = STRING_NEW(buf);
    FREE(buf);
    
    if(read(fd, &n, sizeof(int)) < 0) {
        perror("read");
        exit(1);
    }
    self->mLine = n;

    if(read(fd, &n, sizeof(int)) < 0) {
        perror("read");
        exit(1);
    }
    buf = MALLOC(n+1);
    if(read(fd, buf, n) < 0) {
        perror("read");
        exit(1);
    }
    buf[n] = 0;
    self->mTitle = STRING_NEW(buf);
    FREE(buf);
    
    if(read(fd, &n, sizeof(int)) < 0) {
        perror("read");
        exit(1);
    }
    if(n == 0) {
        self->mNotEvaled = NULL;
    }
    else {
        if(read(fd, &n, sizeof(int)) < 0) {
            perror("read");
            exit(1);
        }
        buf = MALLOC(n+1);
        if(read(fd, buf, n) < 0) {
            perror("read");
            exit(1);
        }
        buf[n] = 0;
        self->mNotEvaled = STRING_NEW(buf);
        FREE(buf);
    }
    
    if(read(fd, &n, sizeof(int)) < 0) {
        perror("read");
        exit(1);
    }
    self->mBackground = n;

    if(read(fd, &n, sizeof(int)) < 0) {
        perror("read");
        exit(1);
    }
    self->mGlobalPipeIn = n;

    if(read(fd, &n, sizeof(int)) < 0) {
        perror("read");
        exit(1);
    }
    self->mGlobalPipeOut = n;

    if(read(fd, &n, sizeof(int)) < 0) {
        perror("read");
        exit(1);
    }
    self->mGlobalPipeInNum = n;

    if(read(fd, &n, sizeof(int)) < 0) {
        perror("read");
        exit(1);
    }
    self->mGlobalPipeOutNum = n;

    return self;
}

/////////////////////////////////////////////////////////
// 複文 ls -al | grep aaa; pwd; cd aaa
/////////////////////////////////////////////////////////
#ifdef MDEBUG
sStatments* sStatments_new(const char* file, int line, const char* function)
{
    sStatments* self = CheckMemLeak_Malloc(sizeof(sStatments)
                            , file, line, "STATMENTSLIST_NEW");

    self->mStatments = VECTOR_NEW(30);

    return self;
}

#define STATMENTSLIST_NEW() sStatments_new(__FILE__, __LINE__, __FUNCTION__)

#else

sStatments* sStatments_new()
{
    sStatments* self = MALLOC(sizeof(sStatments));

    self->mStatments = VECTOR_NEW(30);

    return self;
}

#define STATMENTSLIST_NEW() sStatments_new()

#endif

sStatments* sStatments_new2(sStatments* statments)
{
    sStatments* self = MALLOC(sizeof(sStatments));

    self->mStatments = VECTOR_NEW(30);
    int i;
    for(i=0; i < vector_size(statments->mStatments); i++) {
        vector_add(self->mStatments, sStatment_new2(vector_item(statments->mStatments, i)));
    }

    return self;
}
void sStatments_view(sStatments* self)
{

    int i;
    for(i=0; i<vector_size(self->mStatments); i++)
        sStatment_view(vector_item(self->mStatments, i));
}

void sStatments_save(sStatments* self, int fd)
{
    int i;
    int n;
    
    n = vector_size(self->mStatments);
    
    if(write(fd, &n, sizeof(int)) < 0) {
        perror("write");
        exit(1);
    }
    
    for(i=0; i<vector_size(self->mStatments); i++) {
        sStatment_save(vector_item(self->mStatments, i), fd);
    }
}

sStatments* sStatments_load(int fd)
{
    int n;
    int i;
    sStatments* self = MALLOC(sizeof(sStatments));
    
    self->mStatments = VECTOR_NEW(30);
    
    if(read(fd, &n, sizeof(int)) < 0) {
        perror("read");
        exit(1);
    }
    
    for(i=0; i<n; i++) {
        vector_add(self->mStatments, sStatment_load(fd));
    }
    
    return self;
}

void sStatments_delete(sStatments* self)
{
    int i;
    for(i=0; i<vector_size(self->mStatments); i++) {
        sStatment_delete(vector_item(self->mStatments, i));
    }
    vector_delete(self->mStatments);

    FREE(self);
}

/////////////////////////////////////////////////////////
// 関数
/////////////////////////////////////////////////////////
sFunction* sFunction_new(char* name, MANAGED sStatments* statments, char* arg_name)
{

    sFunction* self = MALLOC(sizeof(sFunction));
    self->name = STRING_NEW(name);
    self->arg_name = STRING_NEW(arg_name);
    
    self->statments = statments;

    return self;
}

sFunction* sFunction_new2(sFunction* func)
{

    sFunction* self = MALLOC(sizeof(sFunction));
    self->name = STRING_NEW(string_c_str(func->name));
    self->arg_name = STRING_NEW(string_c_str(func->arg_name));
    
    if(func->statments) {
        self->statments = sStatments_new2(func->statments);
    }
    else {
        self->statments = NULL;
    }

    return self;
}

void sFunction_delete(sFunction* self)
{
    string_delete(self->name);
    string_delete(self->arg_name);

    if(self->statments) sStatments_delete(self->statments);
        // NULLのときはdeleteしない
        // NULLになるときはgFuncsに登録したとき
        // statmentsはgFuncsの中に入りこっちではdeleteしない

    FREE(self);
}

void sFunction_save(sFunction* self, int fd)
{
    int n = string_length(self->name);
    if(write(fd, &n, sizeof(int)) < 0) {
        perror("write");
        exit(1);
    }
    if(write(fd, string_c_str(self->name), sizeof(char)*n) < 0) {
        perror("write");
        exit(1);
    }
    n = string_length(self->arg_name);
    if(write(fd, &n, sizeof(int)) < 0) {
        perror("write");
        exit(1);
    }
    if(write(fd, string_c_str(self->arg_name), sizeof(char)*n) < 0) {
        perror("write");
        exit(1);
    }

    sStatments_save(self->statments, fd);
}

sFunction* sFunction_load(int fd)
{
    sFunction* self = MALLOC(sizeof(sFunction));
    
    int n;
    if(read(fd, &n, sizeof(int)) < 0) {
        perror("read");
        exit(1);
    }

    char* buf = MALLOC(sizeof(char)*(n+1));
    if(read(fd, buf, sizeof(char)*n) < 0) {
        perror("read");
        exit(1);
    }
    buf[n] = 0;

    self->name = STRING_NEW(buf);
    FREE(buf);

    if(read(fd, &n, sizeof(int)) < 0) {
        perror("read");
        exit(1);
    }

    buf = MALLOC(sizeof(char)*(n+1));
    if(read(fd, buf, sizeof(char)*n) < 0) {
        perror("read");
        exit(1);
    }
    buf[n] = 0;

    self->arg_name = STRING_NEW(buf);
    FREE(buf);

    self->statments = sStatments_load(fd);

    return self;
}

void sFunction_view(sFunction* self)
{
    printf("name %s\n", string_c_str(self->name));
    printf("arg_name %s\n", string_c_str(self->arg_name));
}


/////////////////////////////////////////////////////////
// run_wait_cprog で使う
/////////////////////////////////////////////////////////
vector_obj* gStrayCProgs;

sStrayCProg* sStrayCprog_new(int pid, int status, int pgroup)
{
    sStrayCProg* self = MALLOC(sizeof(sStrayCProg));
    
    self->mPid = pid;
    self->mStatus = status;
    self->mPGroup = pgroup;
    
    return self;
}

void sStrayCProg_delete(sStrayCProg* self)
{
    FREE(self);
}

///////////////////////////////////////////////////////
// 子プロセス
///////////////////////////////////////////////////////
sChildProgram* sChildProgram_new(pid_t pid, char* title, BOOL writer_process)
{
    sChildProgram* self = (sChildProgram*)MALLOC(sizeof(sChildProgram));

    self->mPid = pid;
    self->mTitle = STRING_NEW(title);
    self->mWriterProcess = writer_process;

    return self;
}

void sChildProgram_delete(sChildProgram* self)
{
    string_delete(self->mTitle);

    FREE(self);
}

//////////////////////////////////////////////////////
// saphireが組み込まれたアプリケーションから
// 登録された内部コマンド
/////////////////////////////////////////////////////
sInnerCommand* sInnerCommand_new(char* name, fInnerCommand fun)
{
    sInnerCommand* self = MALLOC(sizeof(sInnerCommand));

    self->mName = STRING_NEW(name);
    self->mFun = fun;

    return self;
}

void sInnerCommand_delete(sInnerCommand* self)
{
    string_delete(self->mName);
    FREE(self);
}

/////////////////////////////////////////////////////////
// プログラム名補完候補を再読み込み
/////////////////////////////////////////////////////////
static BOOL name_sort(void* left, void* right)
{
    string_obj* l = left;
    string_obj* r = right;

    return strcmp(string_c_str(l), string_c_str(r)) < 0;
}

static void saphire_rehash_without_user_fun()
{
    hash_obj* check = HASH_NEW(3000);

    int i;
    for(i=0; i<vector_size(gSaphireProgCompletions); i++) {
        string_delete(vector_item(gSaphireProgCompletions, i));
    }
    vector_clear(gSaphireProgCompletions);

    /// 内部コマンド ///
    for(i=0; i<kCommand; i++) {
        vector_add(gSaphireProgCompletions, STRING_NEW(gStatmentKindStrs[i]));
    }

    /// 外部登録の内部コマンド ///
    hash_it* it = hash_loop_begin(gInnerCommands);
    while(it != NULL) {
        sInnerCommand* command = hash_loop_item(it);
        vector_add(gSaphireProgCompletions, STRING_NEW(string_c_str(command->mName)));
        it = hash_loop_next(it);
    }

    /// $PATHにあるファイルを全てgSaphireProgCompletionsに入れていく ///
    char* path = getenv("PATH");
    if(path == NULL) {
        fprintf(stderr, "$PATH is NULL\n");
        exit(1);
    }

    char* p = path;
    char buf[4096];
    char* p2 = buf;

    while(*p) {
        if(*p != ':') {
            *p2++ = *p;
        }
        else {
            *p2 = 0;

            DIR* dir = opendir(buf);
            if(dir) {
                struct dirent* entry;
                while(entry = readdir(dir)) {
                    char path2[PATH_MAX];
                    snprintf(path2, PATH_MAX, "%s/%s", buf, entry->d_name);
                    
                    struct stat stat_;
                    memset(&stat_, 0, sizeof(struct stat));
                    stat(path2, &stat_);

                    if(strcmp(entry->d_name, ".") != 0
                            && strcmp(entry->d_name, "..") != 0
                            &&
                            (stat_.st_mode & S_IXUSR
                             ||stat_.st_mode & S_IXGRP
                             ||stat_.st_mode & S_IXOTH))
                    {
                        if(hash_item(check, entry->d_name) == NULL) {
                            hash_put(check, entry->d_name, (void*)1);
#if defined(__CYGWIN__)
                            if(strstr(entry->d_name, ".exe") 
                                == entry->d_name + strlen(entry->d_name) 
                                    -4) 
                            {
                                char buf[128];
                                memcpy(buf, entry->d_name
                                        , strlen(entry->d_name) -4);
                                buf[strlen(entry->d_name) -4] = 0;
                                vector_add(gSaphireProgCompletions, STRING_NEW(buf));
                            }
                            else {
                                vector_add(gSaphireProgCompletions
                                        , STRING_NEW(entry->d_name));
                            }
                            
#else
                            vector_add(gSaphireProgCompletions
                                        , STRING_NEW(entry->d_name));
#endif
                        }
                    }
                }

                closedir(dir);
            }

            p2 = buf;
        }

        p++;
    }
    *p2 = 0;

    DIR* dir = opendir(buf);
    if(dir) {
        struct dirent* entry;
        while(entry = readdir(dir)) {
            if(strcmp(entry->d_name, ".") != 0
                    && strcmp(entry->d_name, "..") != 0)
            {
                vector_add(gSaphireProgCompletions, STRING_NEW(entry->d_name));
            }
        }

        closedir(dir);
    }

    (void)vector_sort(gSaphireProgCompletions, name_sort);

    hash_delete(check);

}

static void saphire_rehash_user_fun()
{
    /// ユーザーオリジナル関数 ///
    hash_it* it = hash_loop_begin(gFuncs);
    while(it != NULL) {
        sFunction* fun = hash_loop_item(it);
        vector_add(gSaphireProgCompletions, STRING_NEW(string_c_str(fun->name)));
        it = hash_loop_next(it);
    }
}

void saphire_rehash()
{
    saphire_rehash_without_user_fun();
    saphire_rehash_user_fun();
}

///////////////////////////////////////////////////////
// ランタイムスクリプト実行
///////////////////////////////////////////////////////
static void read_rc_file(BOOL run_user_runtimescript)
{
    char rc_fname[PATH_MAX];
    snprintf(rc_fname, PATH_MAX,"%s/saphire.sa", SYSCONFDIR);

    if(access(rc_fname, R_OK) == 0) {
        sWFd* pipeout = sWFd_new(STDOUT_FILENO);
        sRFd* pipein = sRFd_new(STDIN_FILENO);
        if(saphire_load(rc_fname
           , pipeout, pipein, STDERR_FILENO) < 0)
        {
            fprintf(stderr, "%s", string_c_str(gErrMsg));
            exit(1);
        }
        sRFd_delete(pipein);
        if(!sWFd_flash(pipeout)) {
            fprintf(stderr, "can't flash rc");
            exit(1);
        }
        sWFd_delete(pipeout);
    }
    else {
        fprintf(stderr, "can't find %s file\n", rc_fname);
        exit(1);
    }

    /// ユーザーのランタイムスクリプト ////
    if(run_user_runtimescript) {
        char* user_rc_fname = getenv("SAPHIRE_USERRC");
        if(user_rc_fname) {
            if(access(user_rc_fname, R_OK) == 0) {
                sWFd* pipeout = sWFd_new(STDOUT_FILENO);
                sRFd* pipein = sRFd_new(STDIN_FILENO);
                if(saphire_load(user_rc_fname
                    , pipeout, pipein, STDERR_FILENO) < 0)
                {
                    fprintf(stderr, "%s", string_c_str(gErrMsg));
                    exit(1);
                }
                if(!sWFd_flash(pipeout)) {
                    fprintf(stderr, "can't flash rc");
                    exit(1);
                }
                sRFd_delete(pipein);

                sWFd_delete(pipeout);
            }
        }
    }
}

static void read_rc_obj_file(BOOL run_user_runtimescript)
{
    char rc_fname[PATH_MAX];
    snprintf(rc_fname, PATH_MAX, "%s/saphire.sao", SYSCONFDIR);

    if(access(rc_fname, R_OK) == 0) {
        sWFd* pipeout = sWFd_new(STDOUT_FILENO);
        sRFd* pipein = sRFd_new(STDIN_FILENO);
        if(saphire_load_obj(rc_fname
           , pipeout, pipein, STDERR_FILENO, NULL) < 0)
        {
            fprintf(stderr, "%s", string_c_str(gErrMsg));
            exit(1);
        }
        if(!sWFd_flash(pipeout)) {
            fprintf(stderr, "can't flash rc");
            exit(1);
        }
        sRFd_delete(pipein);
        sWFd_delete(pipeout);
    }
    else {
        fprintf(stderr, "can't find %s file\n", rc_fname);
        exit(1);
    }

    /// ユーザーのランタイムスクリプト ////
    if(run_user_runtimescript) {
        char* user_rc_fname = getenv("SAPHIRE_USERRC");
        if(user_rc_fname) {
            if(access(user_rc_fname, R_OK) == 0) {
                sWFd* pipeout = sWFd_new(STDOUT_FILENO);
                sRFd* pipein = sRFd_new(STDIN_FILENO);
                if(saphire_load_obj(user_rc_fname
                    , pipeout, pipein, STDERR_FILENO, NULL) < 0)
                {
                    fprintf(stderr, "%s", string_c_str(gErrMsg));
                    exit(1);
                }
                sWFd_flash(pipeout);
                sWFd_delete(pipeout);
                sRFd_delete(pipein);
            }
        }
    }
}

////////////////////////////////////////////////////////
// 初期化
////////////////////////////////////////////////////////
void saphire_init(enum eAppType app_type, BOOL job_control, enum eRuntimeScript runtime_script, BOOL run_user_runtimescript)
{
    gGlobalPipes = VECTOR_NEW(10);
    gQGlobalPipes = VECTOR_NEW(10);
    gGlobalPipe = STRING_NEW("");
    int i;
    for(i=0; i<10; i++) {
        gGlobalPipeNum[i] = STRING_NEW("");
    }

    // ナンバーグローバルパイプの読み込みんだデータ

    gAncestalMemoryPipeIn = STRING_NEW("");
    gAncestalMemoryPipeOut = STRING_NEW("");
    gAppType = app_type;
    gJobControl = job_control;

    gArrays = HASH_NEW(100);
    gHashs = HASH_NEW(100);
    gFuncs = HASH_NEW(50);

    gJobs = VECTOR_NEW(50);

    gGlobals = HASH_NEW(100);

    gReadBuffers = HASH_NEW(100);

    setenv("SAPHIRE_VERSION", "1.4.0", 1);
    setenv("SAPHIRE_PROMPT", "> ", 1);
    setenv("SAPHIRE_DOCDIR", DOCDIR, 1);
    char* home = getenv("HOME");
    if(home) {
        char path[PATH_MAX];
        snprintf(path, PATH_MAX, "%s/.saphire_history", home);
        setenv("SAPHIRE_HISTFILE", path, 1);
    }
    setenv("SAPHIRE_HISTSIZE", "1000", 1);
    char* user_rc_fname = getenv("SAPHIRE_USERRC");
    if(home) {
        char path[PATH_MAX];
        snprintf(path, PATH_MAX, "%s/.saphire.sao", home);
        setenv("SAPHIRE_USERRC", path, 1);
    }
    
    gSaphireProgCompletions = VECTOR_NEW(100);

    gRegexs = HASH_NEW(100);
    gRegexsI = HASH_NEW(100);
    gRegexsM = HASH_NEW(100);
    gRegexsIM = HASH_NEW(100);
    gFReadBuffers = HASH_NEW(100);
    gFWriteBuffers = HASH_NEW(512);
    
    gStrayCProgs = VECTOR_NEW(10);

    gStackFrame = VECTOR_NEW(5);
    //vector_add(gStackFrame, HASH_NEW(30));

    gInnerCommands = HASH_NEW(50);
    gKitutukiExit = -1;

    gPSubTmpFiles = VECTOR_NEW(10);
    gDirStack = VECTOR_NEW(10);
    gErrMsg = STRING_NEW("");


//    saphire_rehash();

    /// ランタイムスクリプト読み込み ///
    switch(runtime_script) {
        case kRSNoRead:
            break;

        case kRSSource:
            read_rc_file(run_user_runtimescript);
            break;

        case kRSObject:
            read_rc_obj_file(run_user_runtimescript);
            break;
    }
    if(tcgetpgrp(0) == getpgid(0)) {
        gSaphireBackground = FALSE;
        //mreset_tty();
    }
    else {
        gSaphireBackground = TRUE;
    }

    char* env = getenv("SAPHIRE_TMPDIR");
    if(env) 
        xstrncpy(gTmpDir, env, PATH_MAX);
    else 
        xstrncpy(gTmpDir, "/tmp", PATH_MAX);

    gStackTraceFName = VECTOR_NEW(30);
    gStackTraceLineNum = VECTOR_NEW(30);

    tmpfile_all_sweep();
    gSaphireVarCompletions = VECTOR_NEW(100);
}

void saphire_final()
{
    int i;
    for(i=0; i<vector_size(gStackTraceFName); i++) {
        string_delete(vector_item(gStackTraceFName, i));
    }
    vector_delete(gStackTraceFName);
    vector_delete(gStackTraceLineNum);

    int len = vector_size(gGlobalPipes);
    for(i=0; i<len; i++) {
        string_delete(vector_item(gGlobalPipes, i));
    }
    vector_delete(gGlobalPipes);

    len = vector_size(gQGlobalPipes);
    for(i=0; i<len; i++) {
        string_delete(vector_item(gQGlobalPipes, i));
    }
    vector_delete(gQGlobalPipes);

    string_delete(gGlobalPipe);
    for(i=0; i<10; i++) {
        string_delete(gGlobalPipeNum[i]);
    }

    hash_it* it = hash_loop_begin(gFuncs);
    while(it != NULL) {
        sFunction_delete(hash_loop_item(it));
        it = hash_loop_next(it);
    }
    hash_delete(gFuncs);

    for(i=0; i<vector_size(gStackFrame); i++) {
        hash_obj* hash = vector_item(gStackFrame, i);

        it = hash_loop_begin(hash);
        while(it != NULL) {
            string_delete(hash_loop_item(it));
            it = hash_loop_next(it);
        }
        hash_delete(hash);
    }
    vector_delete(gStackFrame);

    it = hash_loop_begin(gGlobals);
    while(it != NULL) {
        string_delete(hash_loop_item(it));
        it = hash_loop_next(it);
    }
    hash_delete(gGlobals);

    it = hash_loop_begin(gArrays);
    while(it != NULL) {
        vector_obj* v = hash_loop_item(it);

        int i;
        for(i=0; i<vector_size(v); i++) {
            string_delete(vector_item(v, i));
        }
        vector_delete(v);

        it = hash_loop_next(it);
    }
    hash_delete(gArrays);

    it = hash_loop_begin(gHashs);
    while(it != NULL) {
        hash_obj* h = hash_loop_item(it);

        hash_it* it2 = hash_loop_begin(h);
        while(it2 != NULL) {
            string_delete(hash_loop_item(it2));
            it2 = hash_loop_next(it2);
        }
        hash_delete(h);

        it = hash_loop_next(it);
    }
    hash_delete(gHashs);

    for(i=0; i<vector_size(gJobs); i++) {
        sJob_delete(vector_item(gJobs, i));
    }
    vector_delete(gJobs);

    it = hash_loop_begin(gReadBuffers);
    while(it) {
        sRFd* fd = (sRFd*)hash_loop_item(it);
        sRFd_close(fd);
        sRFd_delete(fd);
        it = hash_loop_next(it);
    }
    hash_delete(gReadBuffers);
    
    for(i=0; i<vector_size(gSaphireProgCompletions); i++) {
        string_delete(vector_item(gSaphireProgCompletions, i));
    }
    vector_delete(gSaphireProgCompletions);

    it = hash_loop_begin(gRegexs);
    while(it != NULL) {
        onig_free(hash_loop_item(it));
        it = hash_loop_next(it);
    }
    hash_delete(gRegexs);

    it = hash_loop_begin(gRegexsI);
    while(it != NULL) {
        onig_free(hash_loop_item(it));
        it = hash_loop_next(it);
    }
    hash_delete(gRegexsI);

    it = hash_loop_begin(gRegexsM);
    while(it != NULL) {
        onig_free(hash_loop_item(it));
        it = hash_loop_next(it);
    }
    hash_delete(gRegexsM);

    it = hash_loop_begin(gRegexsIM);
    while(it != NULL) {
        onig_free(hash_loop_item(it));
        it = hash_loop_next(it);
    }
    hash_delete(gRegexsIM);
    
    it = hash_loop_begin(gFReadBuffers);
    while(it != NULL) {
        string_delete(hash_loop_item(it));
        it = hash_loop_next(it);
    }
    hash_delete(gFReadBuffers);
    
    it = hash_loop_begin(gFWriteBuffers);
    while(it != NULL) {
        string_delete(hash_loop_item(it));
        it = hash_loop_next(it);
    }
    hash_delete(gFWriteBuffers);
    
    for(i=0; i<vector_size(gStrayCProgs); i++) {
        sStrayCProg_delete(vector_item(gStrayCProgs, i));
    }
    vector_delete(gStrayCProgs);

    it = hash_loop_begin(gInnerCommands);
    while(it != NULL) {
        sInnerCommand_delete(hash_loop_item(it));
        it = hash_loop_next(it);
    }
    hash_delete(gInnerCommands);

    for(i=0; i<vector_size(gDirStack); i++) {
        string_delete(vector_item(gDirStack, i));
    }
    vector_delete(gDirStack);

    string_delete(gErrMsg);

    /// プロセス置換で使った一時ファイルを掃除 ///
    int k;
    for(k=0; k<vector_size(gPSubTmpFiles); k++) {
        string_obj* fname = vector_item(gPSubTmpFiles, k);

        if(unlink(string_c_str(fname)) < 0) {
            perror("unlink");
            exit(1);
        }

        string_delete(fname);
    }
    vector_delete(gPSubTmpFiles);

    string_delete(gAncestalMemoryPipeIn);
    string_delete(gAncestalMemoryPipeOut);

    for(i=0; i<vector_size(gSaphireVarCompletions); i++) {
        string_delete(vector_item(gSaphireVarCompletions, i));
    }
    vector_delete(gSaphireVarCompletions);
}

/////////////////////////////////////////////////////////////////////////
// ファイルディスクリプタ
/////////////////////////////////////////////////////////////////////////
sRFd* sRFd_new(int fd)
{
    sRFd* self = (sRFd*)MALLOC(sizeof(sRFd));

    self->mBuffer = STRING_NEW("");
    self->mFd = fd;

    return self;
}

sRFd* sRFd_new2(int fd, char* str)
{
    sRFd* self = (sRFd*)MALLOC(sizeof(sRFd));

    self->mBuffer = STRING_NEW(str);
    self->mFd = fd;

    return self;
}

void sRFd_delete(sRFd* self)
{
    string_delete(self->mBuffer);
    FREE(self);
}

BOOL sRFd_close(sRFd* self)
{
    if(self->mFd == -1) {
    }
    else {
        return close(self->mFd) == 0;
    }
}

// 0:正常終了 1:EOF -1:エラー
int sRFd_read_all(sRFd* self, string_obj* str)
{
    if(self->mFd == -1) {
        string_put(str, string_c_str(self->mBuffer));
        string_put(self->mBuffer, "");
    }
    else {
        /// 先に読まれたバッファを書き出しておく
        string_put(str, string_c_str(self->mBuffer));
        string_put(self->mBuffer, "");

        /// 読み込む
        while(1) {
            char buf[BUFSIZ+1];
            int r = read(self->mFd, buf, BUFSIZ);

            if(r < 0) {
                return -1;
            }
            if(r == 0) {
                if(string_length(str) == 0) {
                    return 1;
                }
                break;
            }
            buf[r] = 0;

            if(gKitutukiSigInt) {
                gKitutukiSigInt = FALSE;
                return -1;
            }

            string_push_back(str, buf);
        }
    }

    return 0;
}

// 0:正常終了 1:EOF -1:エラー
int sRFd_read_all_preserve(sRFd* self, string_obj* str)
{
    if(self->mFd == -1) {
        string_put(str, string_c_str(self->mBuffer));
    }
    else {
        /// 読み込む
        while(1) {
            char buf[BUFSIZ+1];
            int r = read(self->mFd, buf, BUFSIZ);

            if(r < 0) {
                return -1;
            }
            if(r == 0) {
                if(string_length(self->mBuffer) == 0) {
                    return 1;
                }
                break;
            }
            buf[r] = 0;

            if(gKitutukiSigInt) {
                gKitutukiSigInt = FALSE;
                return -1;
            }

            string_push_back(self->mBuffer, buf);
        }

        /// 読み込んだバッファを書き込む
        string_put(str, string_c_str(self->mBuffer));
    }

    return 0;
}

// 0:正常終了 1:EOF -1:エラー
int sRFd_read_all_to_buffer(sRFd* self)
{
    if(self->mFd == -1) {
    }
    else {
        /// 読み込む
        while(1) {
            char buf[BUFSIZ+1];
            int r = read(self->mFd, buf, BUFSIZ);

            if(r < 0) {
                return -1;
            }
            if(r == 0) {
                if(string_length(self->mBuffer) == 0) {
                    return 1;
                }
                break;
            }
            buf[r] = 0;

            if(gKitutukiSigInt) {
                gKitutukiSigInt = FALSE;
                return -1;
            }

            string_push_back(self->mBuffer, buf);
        }
    }

    return 0;
}

// 0: 正常終了 1: EOF -1:エラー
int sRFd_read_oneline(sRFd* self, string_obj* str)
{
    if(self->mFd == -1) {
        string_obj* buffer = self->mBuffer;
        if(gKitutukiSigInt) {
            gKitutukiSigInt = FALSE;
            return -1;
        }

        /// バッファーが無い
        if(strcmp(string_c_str(buffer), "") == 0) {
            return 1;
        }

        /// 取ったバッファから一行を得て返す ///
        char *p;
        int lf_char_size;
        if(gLineField == kLF) {
            p = strstr(string_c_str(buffer), "\n"); 
            lf_char_size = 1;
        }
        else if(gLineField == kCRLF) {
            p = strstr(string_c_str(buffer), "\r\n"); 
            lf_char_size = 2;
        }
        else {
            p = strstr(string_c_str(buffer), "\r"); 
            lf_char_size = 1;
        }

        /// 改行がある
        if(p) {
            int n = p - string_c_str(buffer);
            string_push_back3(str, string_c_str(buffer), n + lf_char_size);
            string_erase(buffer, 0, n + lf_char_size);


            return 0;
        }
        /// 改行が無い
        else {
            string_push_back(str, string_c_str(buffer));

            string_put(buffer,"");

            return 0;
        }
    }
    else {
        while(1) {
            /// 改行が含まれていればbreakする ///
            if(gLineField == kCR) {
                if(strstr(string_c_str(self->mBuffer), "\r")) {
                    break;
                }
            }
            else if(gLineField == kLF) {
                if(strstr(string_c_str(self->mBuffer), "\n")) {
                    break;
                }
            }
            else {
                if(strstr(string_c_str(self->mBuffer), "\r\n")) {
                    break;
                }
            }

            char buf[BUFSIZ+1];

            int r = read(self->mFd, buf, BUFSIZ);

            if(r < 0) {
                return -1;
            }
            if(gKitutukiSigInt) {
                gKitutukiSigInt = FALSE;
                return -1;
            }
            buf[r] = 0;

            /// EOF ///
            if(r == 0) {
                break;
            }
            else {
                string_push_back(self->mBuffer, buf);
            }
        }

        /// バッファーが無い
        if(strcmp(string_c_str(self->mBuffer), "") == 0) {
            return 1;
        }

        /// 取ったバッファから一行を得て返す ///
        char *p;
        int lf_char_size;
        if(gLineField == kLF) {
            p = strstr(string_c_str(self->mBuffer), "\n"); 
            lf_char_size = 1;
        }
        else if(gLineField == kCRLF) {
            p = strstr(string_c_str(self->mBuffer), "\r\n"); 
            lf_char_size = 2;
        }
        else {
            p = strstr(string_c_str(self->mBuffer), "\r"); 
            lf_char_size = 1;
        }

        /// 改行がある
        if(p) {
            int n = p - string_c_str(self->mBuffer);
            string_push_back3(str, string_c_str(self->mBuffer), n + lf_char_size);
            string_erase(self->mBuffer, 0, n + lf_char_size);

            return 0;
        }
        /// 改行が無い
        else {
            string_push_back(str, string_c_str(self->mBuffer));

            string_put(self->mBuffer,"");

            return 0;
        }
    }
}

// 0: 正常終了 1: EOF -1:エラー
int sRFd_read_oneline_preserve(sRFd* self, string_obj* str)
{
    if(self->mFd == -1) {
        string_obj* buffer = self->mBuffer;
        if(gKitutukiSigInt) {
            gKitutukiSigInt = FALSE;
            return -1;
        }

        /// バッファーが無い
        if(strcmp(string_c_str(buffer), "") == 0) {
            return 1;
        }

        /// 取ったバッファから一行を得て返す ///
        char *p;
        int lf_char_size;
        if(gLineField == kLF) {
            p = strstr(string_c_str(buffer), "\n"); 
            lf_char_size = 1;
        }
        else if(gLineField == kCRLF) {
            p = strstr(string_c_str(buffer), "\r\n"); 
            lf_char_size = 2;
        }
        else {
            p = strstr(string_c_str(buffer), "\r"); 
            lf_char_size = 1;
        }

        /// 改行がある
        if(p) {
            int n = p - string_c_str(buffer);
            string_push_back3(str, string_c_str(buffer), n + lf_char_size);
            return 0;
        }
        /// 改行が無い
        else {
            string_push_back(str, string_c_str(buffer));

            return 0;
        }
    }
    else {
        while(1) {
            /// 改行が含まれていればbreakする ///
            if(gLineField == kCR) {
                if(strstr(string_c_str(self->mBuffer), "\r")) {
                    break;
                }
            }
            else if(gLineField == kLF) {
                if(strstr(string_c_str(self->mBuffer), "\n")) {
                    break;
                }
            }
            else {
                if(strstr(string_c_str(self->mBuffer), "\r\n")) {
                    break;
                }
            }

            char buf[BUFSIZ+1];

            int r = read(self->mFd, buf, BUFSIZ);

            if(r < 0) {
                return -1;
            }
            if(gKitutukiSigInt) {
                gKitutukiSigInt = FALSE;
                return -1;
            }
            buf[r] = 0;

            /// EOF ///
            if(r == 0) {
                break;
            }
            else {
                string_push_back(self->mBuffer, buf);
            }
        }

        /// バッファーが無い
        if(strcmp(string_c_str(self->mBuffer), "") == 0) {
            return 1;
        }

        /// 取ったバッファから一行を得て返す ///
        char *p;
        int lf_char_size;
        if(gLineField == kLF) {
            p = strstr(string_c_str(self->mBuffer), "\n"); 
            lf_char_size = 1;
        }
        else if(gLineField == kCRLF) {
            p = strstr(string_c_str(self->mBuffer), "\r\n"); 
            lf_char_size = 2;
        }
        else {
            p = strstr(string_c_str(self->mBuffer), "\r"); 
            lf_char_size = 1;
        }

        /// 改行がある
        if(p) {
            int n = p - string_c_str(self->mBuffer);
            string_push_back3(str, string_c_str(self->mBuffer), n + lf_char_size);

            return 0;
        }
        /// 改行が無い
        else {
            string_push_back(str, string_c_str(self->mBuffer));

            return 0;
        }
    }
}

// 0: 正常終了 1: EOF -1:エラー
int sRFd_read_oneline_preserve_num(sRFd* self, string_obj* str, int line_num)
{
    if(self->mFd == -1) {
        string_obj* buffer = self->mBuffer;
        if(gKitutukiSigInt) {
            gKitutukiSigInt = FALSE;
            return -1;
        }

        /// バッファーが無い
        if(strcmp(string_c_str(buffer), "") == 0) {
            return 1;
        }

        /// 取ったバッファからn行を得て返す ///
        char *p;
        int lf_char_size;
        if(gLineField == kLF) {
            int i;
            p = strstr(string_c_str(buffer), "\n"); 
            for(i=0; i<line_num-1 && p != NULL; i++) {
                p = strstr(p+1, "\n"); 
            }
            lf_char_size = 1;
        }
        else if(gLineField == kCRLF) {
            int i;
            p = strstr(string_c_str(buffer), "\r\n"); 
            for(i=0; i<line_num-1 && p != NULL; i++) {
                p = strstr(p+2, "\r\n"); 
            }
            lf_char_size = 2;
        }
        else {
            int i;
            p = strstr(string_c_str(buffer), "\r"); 
            for(i=0; i<line_num-1 && p != NULL; i++) {
                p = strstr(p+1, "\r"); 
            }
            lf_char_size = 1;
        }

        /// 改行がある
        if(p) {
            int n = p - string_c_str(buffer);
            string_push_back3(str, string_c_str(buffer), n + lf_char_size);
            return 0;
        }
        /// 改行が無い
        else {
            string_push_back(str, string_c_str(buffer));

            return 0;
        }
    }
    else {
        while(1) {
            /// 改行がn個含まれていればbreakする ///
            if(gLineField == kCR) {
                char* p = strstr(string_c_str(self->mBuffer), "\r");
                int n = 0;
                while(p != NULL && n<line_num) {
                    p = strstr(p+1, "\r");
                    n++;
                }
                if(n == line_num) {
                    break;
                }
            }
            else if(gLineField == kLF) {
                char* p = strstr(string_c_str(self->mBuffer), "\n");
                int n = 0;
                while(p != NULL && n<line_num) {
                    p = strstr(p+1, "\n");
                    n++;
                }
                if(n == line_num) {
                    break;
                }
            }
            else {
                char* p = strstr(string_c_str(self->mBuffer), "\r\n");
                int n = 0;
                while(p != NULL && n<line_num) 
                {
                    p = strstr(p+2, "\r\n");
                    n++;
                }
                if(n == line_num) {
                    break;
                }
            }

            char buf[BUFSIZ+1];

            int r = read(self->mFd, buf, BUFSIZ);

            if(r < 0) {
                return -1;
            }
            if(gKitutukiSigInt) {
                gKitutukiSigInt = FALSE;
                return -1;
            }
            buf[r] = 0;

            /// EOF ///
            if(r == 0) {
                break;
            }
            else {
                string_push_back(self->mBuffer, buf);
            }
        }

        /// バッファーが無い
        if(strcmp(string_c_str(self->mBuffer), "") == 0) {
            return 1;
        }

        /// 取ったバッファからn行を得て返す ///
        char *p;
        int lf_char_size;
        if(gLineField == kLF) {
            int i;
            p = strstr(string_c_str(self->mBuffer), "\n"); 
            for(i=0; i<line_num-1 && p != NULL; i++) {
                p = strstr(p+1, "\n"); 
            }
            lf_char_size = 1;
        }
        else if(gLineField == kCRLF) {
            int i;
            p = strstr(string_c_str(self->mBuffer), "\r\n"); 
            for(i=0; i<line_num-1 && p != NULL; i++) {
                p = strstr(p+2, "\r\n"); 
            }
            lf_char_size = 2;
        }
        else {
            int i;
            p = strstr(string_c_str(self->mBuffer), "\r"); 
            for(i=0; i<line_num-1 && p != NULL; i++) {
                p = strstr(p+1, "\r"); 
            }
            lf_char_size = 1;
        }

        /// 改行がある
        if(p) {
            int n = p - string_c_str(self->mBuffer);
            string_push_back3(str, string_c_str(self->mBuffer), n + lf_char_size);

            return 0;
        }
        /// 改行が無い
        else {
            string_push_back(str, string_c_str(self->mBuffer));

            return 0;
        }
    }
}


sWFd* sWFd_new(int fd)
{
    sWFd* self = MALLOC(sizeof(sWFd));

    self->mFd = fd;
    self->mBuffer = MALLOC(1024);
    *self->mBuffer = 0;
    self->mP = self->mBuffer;
    self->mMallocSize = 1024;

    return self;
}

void sWFd_delete(sWFd* self)
{
    FREE(self->mBuffer);
    FREE(self);
}

BOOL sWFd_close(sWFd* self)
{
    if(self->mFd != -1) {
        if(!sWFd_flash(self)) {
            return FALSE;
        }
        if(close(self->mFd) < 0) {
            return FALSE;
        }
    }

    return TRUE;
}

BOOL sWFd_push_back(sWFd* self, char* str)
{
    const int len = strlen(str);
    const int new_size = (self->mP - self->mBuffer) + len;
    if(new_size < self->mMallocSize) {
        while(*str) {
            *(self->mP)++ = *str++;
        }
        *(self->mP) = 0;
    }
    else {
        while(new_size > self->mMallocSize) {
            self->mMallocSize *= 2;
        }

        char* old_buffer = self->mBuffer;
        self->mBuffer = (char*)MALLOC(self->mMallocSize);
        memcpy(self->mBuffer, old_buffer, self->mP - old_buffer);
        self->mP = self->mBuffer + (self->mP - old_buffer);

        while(*str) {
            *self->mP++ = *str++;
        }
        *self->mP = 0;
         
        FREE(old_buffer);
    }

    if(self->mP - self->mBuffer > BUFSIZ) {
        return sWFd_flash(self);
    }

    return TRUE;
}

BOOL sWFd_flash(sWFd* self)
{
    if(self->mFd != -1) {
        char* p = self->mBuffer;
        char* str_size = p + strlen(p);

        while(*p) {
            int size;
            if(str_size - p < BUFSIZ) {
                size = strlen(p);
            }
            else {
                size = BUFSIZ;
            }

            if(write(self->mFd, p, size) < 0) {
                if(errno == EINTR) {
                    self->mP = self->mBuffer;
                    *self->mBuffer = 0;
                    return FALSE;
                }
                else {
                    if(errno == EPIPE) {
                        puts("EPIPE");
                    }
    //printf("self->mFd %d\n", self->mFd);
                    perror("write(sWFd_flash)2");
                    exit(1);
                }
            }

            if(gKitutukiSigInt) {
                gKitutukiSigInt = FALSE;
                self->mP = self->mBuffer;
                *self->mBuffer = 0;
                return FALSE;
            }

            p+=size;
        }

        self->mP = self->mBuffer;
        *self->mBuffer = 0;
    }

    return TRUE;
}

vector_obj* gSaphireVarCompletions;               // 環境変数補完候補

void saphire_make_var_completion_list()
{
    int i;
    for(i=0; i<vector_size(gSaphireVarCompletions); i++) {
        string_delete(vector_item(gSaphireVarCompletions, i));
    }
    vector_clear(gSaphireVarCompletions);

    /// 環境変数 ///
#if HAVE_DECL_ENVIRON
    extern char **environ;

    char** p;
    for(p = environ; *p; p++) {
        char env_name[1024];

        char* p2 = env_name;
        char* p3 = *p;

        while(*p3 != '=') {
            p3++;
        }

        if(p3 - *p < 1024) {
            memcpy(env_name, *p, p3 - *p);
            env_name[p3 - *p] = 0;
        }

        vector_add(gSaphireVarCompletions, STRING_NEW(env_name));
    }
#endif

    hash_it* it = hash_loop_begin(gGlobals);
    while(it) {
        char* key = hash_loop_key(it);

        char env_name[1024];
        xstrncpy(env_name, key, 1024);

        vector_add(gSaphireVarCompletions, STRING_NEW(env_name));

        it = hash_loop_next(it);
    }

    it = hash_loop_begin(gArrays);
    while(it) {
        char* key = hash_loop_key(it);

        char env_name[1024];
        xstrncpy(env_name, key, 1024);

        vector_add(gSaphireVarCompletions, STRING_NEW(env_name));

        it = hash_loop_next(it);
    }

    it = hash_loop_begin(gHashs);
    while(it) {
        char* key = hash_loop_key(it);

        char env_name[1024];
        xstrncpy(env_name, key, 1024);

        vector_add(gSaphireVarCompletions, STRING_NEW(env_name));

        it = hash_loop_next(it);
    }

    (void)vector_sort(gSaphireVarCompletions, name_sort);
}

sBlock* sBlock_new(sStatments* statments, vector_obj* args)
{
    sBlock* self = MALLOC(sizeof(sBlock));

    self->mStatments = statments;
    self->mArgs = args;

    return self;
}

sBlock* sBlock_new2(sBlock* block)
{
    sBlock* self = MALLOC(sizeof(sBlock));

    self->mStatments = sStatments_new2(block->mStatments);
    self->mArgs = VECTOR_NEW(10);
    int i;
    for(i=0; i<vector_size(block->mArgs); i++) {
        vector_add(self->mArgs, STRING_NEW(string_c_str(vector_item(block->mArgs, i))));
    }

    return self;
}

void sBlock_delete(sBlock* self)
{
    sStatments_delete(self->mStatments);
    int i;
    const int max = vector_size(self->mArgs);
    for(i=0; i<max; i++) {
        string_delete(vector_item(self->mArgs, i));
    }
    vector_delete(self->mArgs);

    FREE(self);
}

sBlock* sBlock_load(int fd)
{
    sBlock* self = MALLOC(sizeof(sBlock));
    
    self->mStatments = sStatments_load(fd);

    self->mArgs = VECTOR_NEW(50);
    int n;
    if(read(fd, &n, sizeof(int)) < 0) {
        perror("read");
        exit(1);
    }
    int i;
    for(i=0; i<n; i++) {
        int n;
        if(read(fd, &n, sizeof(int)) < 0) {
            perror("read");
            exit(1);
        }
        
        char* buf = MALLOC(n+1);
        if(read(fd, buf, n) < 0) {
            perror("read");
            exit(1);
        }
        buf[n] = 0;
        string_obj* buf2 = STRING_NEW(buf);
        FREE(buf);
        
        vector_add(self->mArgs, buf2);
    }

    return self;
}

void sBlock_save(sBlock* self, int fd)
{
    sStatments_save(self->mStatments, fd);

    /// mArgs ///
    int n = vector_size(self->mArgs);
    if(write(fd, &n, sizeof(int)) < 0) {
        perror("write");
        exit(1);
    }

    int i;
    for(i=0; i<n; i++) {
        char* str = string_c_str(vector_item(self->mArgs, i));

        int n = strlen(str);
        if(write(fd, &n, sizeof(int)) < 0) {
            perror("write");
            exit(1);
        }
        if(write(fd, str, n) < 0) {
            perror("write");
            exit(1);
        }
    }
}

