#include "config.h"

#include <errno.h>
#include <stdlib.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <termios.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <signal.h>
#include <dirent.h>

#include <limits.h>

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

// 内部コマンド用のリダイレクト処理
static BOOL statment_tree_redirect(sCommand* command, sRFd ** nextin, sWFd** nextout, int* nexterr, sRFd* pipein, sWFd* pipeout, int pipeerr)
{
    BOOL err_and_output = FALSE;
    int k;
    for(k=0; k<vector_size(command->mRedirects); k++) {
        sRedirect* redirect = (sRedirect*)vector_item(command->mRedirects, k);
        if(redirect->mType == kRedirectErrAndOutput) {
            err_and_output = TRUE;
        }
        else {
            mode_t mode;
            switch(redirect->mType) {
                case kRedirectInput:
                    mode = O_RDONLY;
                    break;
                
                case kRedirectOverwrite:
                    mode = O_WRONLY | O_CREAT | O_TRUNC;
                    break;
                    
                case kRedirectAppend:
                    mode = O_WRONLY | O_CREAT | O_APPEND;
                    break;
            }
            
            int openfd = open(string_c_str(redirect->mFName), mode, 0666);
            if(openfd < 0) {
                fprintf(stderr, "open %s is err1\n"
                            , string_c_str(redirect->mFName));
                exit(1);
            }

            switch(redirect->mType) {
                case kRedirectInput:
                    if(*nextin == pipein) {
                        *nextin = sRFd_new(openfd);
                    }
                    else {
                        if(!sRFd_close(*nextin)) {
                            return FALSE;
                        }
                        sRFd_delete(*nextin);
                        *nextin = sRFd_new(openfd);
                    }
                    break;
                
                case kRedirectOverwrite:
                    if(redirect->mFd == 1) {
                        if(*nextout == pipeout) {
                            *nextout = sWFd_new(openfd);
                        }
                        else {
                            close((*nextout)->mFd);
                            (*nextout)->mFd = openfd;
                        }
                    }
                    else {
                        if(*nexterr != 2 && *nexterr != pipeerr)
                            if(!xclose(*nexterr)) {
                                return FALSE;
                            }
                        
                        *nexterr = openfd;
                    }
                    break;
                    
                case kRedirectAppend:
                    if(redirect->mFd == 1) {
                        if(*nextout == pipeout) {
                            *nextout = sWFd_new(openfd);
                        }
                        else {
                            close((*nextout)->mFd);
                            (*nextout)->mFd = openfd;
                        }
                    }
                    else {
                        if(*nexterr != 2 && *nexterr != pipeerr)
                            if(!xclose(*nexterr)) {
                                return FALSE;
                            }
                        
                        *nexterr = openfd;
                    }
                    break;
            }
        }
    }

    return TRUE;
}

// forkした子供(外部コマンド)をexecさせたい関数
static BOOL statment_tree_exec_cprog(sCommand* command, int nextin, int nextout, int nexterr, vector_obj* argv)
{
//puts("statment_tree_exec_cprog");
    /// リダイレクト ///
    int err_and_output = FALSE;
    int k;
    for(k=0; k<vector_size(command->mRedirects); k++) {
        sRedirect* redirect = (sRedirect*)
                vector_item(command->mRedirects, k);

        if(redirect->mType == kRedirectErrAndOutput) {
            err_and_output = TRUE;
        }
        else {
            mode_t mode;
            switch(redirect->mType) {
                case kRedirectInput:
                    mode = O_RDONLY;
                    break;
                
                case kRedirectOverwrite:
                    mode = O_RDWR | O_CREAT | O_TRUNC;
                    break;
                    
                case kRedirectAppend:
                    mode = O_RDWR | O_CREAT | O_APPEND;
                    break;
            }

            int openfd = open(string_c_str(redirect->mFName), mode, 0666);
            if(openfd < 0) {
                fprintf(stderr, "open %s is err2\n"
                                , string_c_str(redirect->mFName));
                exit(1);
            }
            
            switch(redirect->mFd) {
            case 0:
                if(nextin != 0) if(!xclose(nextin)) return FALSE;
                nextin = openfd;
                break;
                
            case 1:
                if(nextout != 1) if(!xclose(nextout)) return FALSE;
                nextout = openfd;
                break;
                
            case 2:
                if(nexterr != 2) if(!xclose(nexterr)) return FALSE;
                nexterr = openfd;
                break;
            }
        }
    }

    /// パイプ ///
    if(err_and_output) {
        if(dup2(nextout, 2) < 0) {
            perror("dup2 1");
            exit(1);
        }
    }
    
    if(nextin != 0) {
        if(dup2(nextin, 0) < 0) {
            char buf[32];
            snprintf(buf, 32, "dup2 3 nextin %d", nextin);
            perror(buf);
            exit(1);
        }
        if(!xclose(nextin)) { return FALSE; }
    }
    
    if(nextout != 1) {
        if(dup2(nextout, 1) < 0) {
            char buf[128];
            snprintf(buf, 128, "dup2 nextout (%d)", nextout);
            perror(buf);
            exit(1);
        }
        
        if(!xclose(nextout)) { return FALSE; }
    }
    
    if(nexterr != 2) {
        if(dup2(nexterr, 2) < 0) {
            perror("dup2 5");
            exit(1);
        }
        
        if(!xclose(nexterr)) { return FALSE; }
    }

    /// char** に変換 ///
    char** argv2 = (char**)malloc(sizeof(char*)*(vector_size(argv)+1));

    int i;
    for(i=0; i<vector_size(argv); i++) {
        string_obj* item = (string_obj*)vector_item(argv, i);
        argv2[i] = string_c_str(item);
    }
    argv2[i] = NULL;

    /// exec ///
    execvp(argv2[0], argv2);
    fprintf(stderr, "exec('%s') error\n", argv2[0]);
    kill(getppid(), SIGUSR1);
    exit(1);
}

// 外部コマンドの処理
static BOOL statment_tree_external_command(sStatment* statment, vector_obj* argv, sRFd* nextin, sWFd* nextout, int nexterr, int pipefds[2], pid_t* pgroup, sWFd* pipeout, sCommand* command, vector_obj* cprogs, BOOL last_program, int* last_program_pid, char* sname, int line)
{
//printf("nextin %d nextout %d\n", nextin->mFd, nextout->mFd);
    char* argv0 = string_c_str(vector_item(argv, 0));

    /// メモリーパイプをパイプに変換 ///
    int mpipefds[2] = { -1, -1 };
    int mpipefds2[2] = { -1, -1 };
    int nextin2 = nextin->mFd;
    int nextout2 = nextout->mFd;
    if(nextin2 == -1) {
        if(pipe(mpipefds) < 0) {
            perror("pipe");
            exit(1);
        }
        nextin2 = mpipefds[0];
        mpipefds[0] = -1;
    }
    if(nextout2 == -1) {
        if(pipe(mpipefds2) < 0) {
            perror("pipe");
            exit(1);
        }
        
        nextout2 = mpipefds2[1];
        mpipefds2[1] = -1;
    }
 
    /// fork ///
    pid_t pid = fork();
    if(pid < 0) {
        perror("fork");
        exit(1);
    }

    /// 子プロセスの処理 ///
    if(pid == 0) {
        if(mpipefds[0] != -1) close(mpipefds[0]);
        if(mpipefds[1] != -1) close(mpipefds[1]);
        if(mpipefds2[0] != -1) close(mpipefds2[0]);
        if(mpipefds2[1] != -1) close(mpipefds2[1]);

        /// 親が開いた関係のないパイプは閉じておく ///
        if(pipefds[0] != -1) { close(pipefds[0]); }
        if(pipefds[1] != -1) { close(pipefds[1]); }

        pid = getpid();

        if(*pgroup == -1) *pgroup = pid;

        if(gAppType != kATOptC) {
            if(setpgid(pid, *pgroup) < 0) {
                *pgroup = pid;
                (void)setpgid(pid, pid);
            }
        }
        if((gAppType == kATCursesApp 
                || gAppType == kATConsoleApp)
            && !statment->mBackground 
            && pipeout->mFd == STDOUT_FILENO) 
        {
            (void)tcsetpgrp(0, *pgroup);
        }

        saphire_restore_signal_default();
        sigchld_block(0);

        if(!statment_tree_exec_cprog(command, nextin2, nextout2, nexterr , argv))
        {
            return FALSE;
        }

        return TRUE;
    }
    /// 親プロセスの処理 ///
    else {
        if(last_program_pid) *last_program_pid = pid;
        if(*pgroup == -1) *pgroup = pid;

        if(gAppType != kATOptC) {
            if(setpgid(pid, *pgroup) < 0) {
                *pgroup = pid;

                (void)setpgid(pid, *pgroup);
            }
        }
        if((gAppType == kATCursesApp 
                || gAppType == kATConsoleApp)
            && !statment->mBackground 
            && pipeout->mFd == STDOUT_FILENO) 
        {
            (void)tcsetpgrp(0, *pgroup);
        }

        vector_add(cprogs
            , sChildProgram_new(pid
                , string_c_str(statment->mTitle), FALSE));

        /// 子へのメモリパイプのバッファ渡しがあって
        /// 子がTSTP食らうとsaphire本体のパイプの書き込みがブロックし
        /// waitpidもできないためデッドロックする。
        /// そのためバッファを書き込むプロセスを用意してsaphire
        /// 本体はバッファの書き込みをしない
        if(mpipefds[1] != -1) {
            int pid2 = fork();
            if(pid2 < 0) {
                perror("fork2");
                exit(1);
            }
            /// writer process ///
            if(pid2 == 0) {             /// 子プロセス
                if(gAppType != kATOptC) {
                    pid2 = getpid();
                    if(setpgid(pid2, *pgroup) < 0) {
                        (void)setpgid(pid2, pid2);
                    }
                }

                if(mpipefds[1] != -1) {
                    close(nextin2);
                    BOOL epipe = FALSE;
                    if(!gSigUser && write(mpipefds[1], string_c_str(nextin->mBuffer), string_length(nextin->mBuffer)) < 0)
                    {
                        if(errno != EPIPE ) {
                            perror("write memory pipe");
                            exit(1);
                        }
                    }
                    close(mpipefds[1]);
                }
                exit(0);
            }
            /// 親プロセス ///
            else {
                vector_add(cprogs, sChildProgram_new(pid2, "writer process", TRUE));

                if(gAppType != kATOptC) {
                    if(setpgid(pid2, *pgroup) < 0) {
                        *pgroup = pid2;

                        (void)setpgid(pid2, *pgroup);
                    }
                }

                if(mpipefds[1] != -1) {
                    close(nextin2);
                    close(mpipefds[1]);
                }

                if(mpipefds2[0] != -1) {
                    close(nextout2);

                    if(!gSigUser) {
                        char buf[BUFSIZ+1];
                        while(1) {
                            if(gKitutukiSigInt) {
                                err_msg("signal interrupt", sname, line);
                                gKitutukiSigInt = FALSE;
                                return FALSE;
                            }
                            int r = read(mpipefds2[0], buf, BUFSIZ);
                            if(r < 0) {
                                if(errno == EINTR) {
                                    buf[0] = 0;
                                    sWFd_push_back(nextout, buf);
                                    break;
                                }
                                else {
                                    perror("read mpipe");
                                    exit(1);
                                }
                            }
                            buf[r] = 0;

                            sWFd_push_back(nextout, buf);

                            if(r == 0) {
                                break;
                            }
                        }
                    }

                    close(mpipefds2[0]);
                }
            }
        }
        else {
            if(mpipefds2[0] != -1) {
                close(nextout2);

                if(!gSigUser) {
                    char buf[BUFSIZ+1];
                    while(1) {
                        if(gKitutukiSigInt) {
                            err_msg("signal interrupt", sname, line);
                            gKitutukiSigInt = FALSE;
                            return FALSE;
                        }
                        int r = read(mpipefds2[0], buf, BUFSIZ);
                        if(r < 0) {
                            if(errno == EINTR) {
                                buf[0] = 0;
                                sWFd_push_back(nextout, buf);
                                break;
                            }
                            else {
                                perror("read mpipe");
                                exit(1);
                            }
                        }
                        buf[r] = 0;

                        sWFd_push_back(nextout, buf);

                        if(r == 0) {
                            break;
                        }
                    }
                }

                close(mpipefds2[0]);
            }
        }

        return TRUE;
    }
}

// 変数の代入文を処理したい関数
BOOL statment_tree_input_var(char* equalp, char* arg0, vector_obj* argv, int *rcode, char* sname, int line)
{
    char* var_name = MALLOC(equalp - arg0 + 1);

    char* p = arg0;
    char* p2 = var_name;
    while(*p >= 'a' && *p <= 'z' || *p >= 'A' && *p <= 'Z'
            || *p >= '0' && *p <= '9' || *p == '_')
    {
        *p2++ = *p++;
    }
    *p2 = 0;

    hash_obj* hash = hash_item(gHashs, var_name);
    if(hash) {
        if(*p == '[') {
            p++;
        }
        else {
            err_msg("need key for hash", sname, line);
            FREE(var_name);
            return FALSE;
        }

        char* key = MALLOC(equalp - arg0 + 1);

        BOOL squote = FALSE;
        BOOL dquote = FALSE;
        char* p2 = key;
        while(1) {
            /// クォート ///
            if(*p == '\\') {
                p++;
                *p2++ = *p++;
            }
            /// シングルクォート ///
            else if(!dquote && *p == '\'') {
                squote = !squote;
                *p2++ = *p++;
            }
            /// ダブルクォート ///
            else if(!squote && *p == '"') {
                dquote = !dquote;
                *p2++ = *p++;
            }
            /// シングルクォート、ダブルクォート中 ///
            else if(squote || dquote) {
                if(*p == 0) {
                    err_msg("require \" or \'", sname, line);
                    FREE(key);
                    FREE(var_name);
                    return FALSE;
                }
                else {
                    *p2++ = *p++;
                }
            }
            else if(*p == ']') {
                p++;
                break;
            }
            else if(*p == '=') {
                err_msg("need ]", sname, line);

                FREE(key);
                FREE(var_name);
                return FALSE;
            }
            else {
                *p2++ = *p++;
            }
        }
        *p2 = 0;

        if(*p != '=' || strcmp(var_name, "") == 0) 
        {
            err_msg("invalid var name", sname, line);
            FREE(key);
            FREE(var_name);
            return FALSE;
        }

        string_obj* var = hash_item(hash, key);
        if(var) {
            string_put(var, equalp + 1);
        }
        else {
            string_obj* value = STRING_NEW(equalp + 1);
            hash_put(hash, key, value);
        }

        FREE(key);
    }
    else {
        vector_obj* array = hash_item(gArrays, var_name);

        if(array) {
            char* number = MALLOC(equalp - arg0 + 1);
            *number = 0;

            p++;

            BOOL squote = FALSE;
            BOOL dquote = FALSE;
            char* p2 = number;
            while(1) {
                /// クォート ///
                if(*p == '\\') {
                    p++;
                    *p2++ = *p++;
                }
                /// シングルクォート ///
                else if(!dquote && *p == '\'') {
                    squote = !squote;
                    *p2++ = *p++;
                }
                /// ダブルクォート ///
                else if(!squote && *p == '"') {
                    dquote = !dquote;
                    *p2++ = *p++;
                }
                /// シングルクォート、ダブルクォート中 ///
                else if(squote || dquote) {
                    if(*p == 0) {
                        err_msg("require \" or \'", sname, line);
                        FREE(number);
                        FREE(var_name);
                        return FALSE;
                    }
                    else {
                        *p2++ = *p++;
                    }
                }
                else if(*p == ']') {
                    p++;
                    break;
                }
                else if(*p == '=') {
                    err_msg("need ]", sname, line);

                    FREE(number);
                    FREE(var_name);
                    return FALSE;
                }
                else {
                    *p2++ = *p++;
                }
            }
            *p2 = 0;

            if(*p != '=' || strcmp(var_name, "") == 0) 
            {
                err_msg("invalid var name B", sname, line);
                FREE(number);
                FREE(var_name);
                return FALSE;
            }

            int n = atoi(number);
            if(n >= 0 && n < vector_size(array)) {
                string_obj* var = vector_item(array, n);
                string_put(var, equalp + 1);
            }
            else {
                int m = n-vector_size(array);
                int i;
                for(i=0; i<m; i++) {
                    vector_add(array, STRING_NEW(""));
                }
                vector_add(array, STRING_NEW(equalp + 1));
            }

            FREE(number);
        }
        else {
            if(*p != '=' || strcmp(var_name, "") == 0) 
            {
                err_msg("invalid var name D", sname, line);
                FREE(var_name);
                return FALSE;
            }

            string_obj* value = STRING_NEW(equalp + 1);

            saphire_set_local_var(var_name, string_c_str(value));

            string_delete(value);
        }
    }

    *rcode = 0;

    FREE(var_name);

    return TRUE;
}

static int gFilePipeNum = 0;

// 文を処理したい関数。本体。
static BOOL statment_tree_make_argv(sCommand* command, vector_obj* argv, vector_obj* blocks, vector_obj* parent_blocks, BOOL* return_, sRFd* nextin, sRFd* pipein, char* sname, int line)
{
    int ii;
    for(ii=0; ii<vector_size(command->mArgs); ii++) {
        sArg* arg = vector_item(command->mArgs, ii);
        switch(arg->mKind) {
            case 0:
                vector_add(argv, STRING_NEW(string_c_str(arg->mBody)));
                break;

            case 1: {
                sVar* var = arg->mBody;
                
                string_obj* var_name = var->mName;
                string_obj* field = var->mDelimiter;
                string_obj* pipeout2 = STRING_NEW("");

                string_obj* var2;
                if(vector_size(gStackFrame) == 0) {
                    /// グローバル変数を参照 ///
                    var2 = hash_item(gGlobals, string_c_str(var_name));
                }
                else {
                    /// ローカル変数を参照 ///
                    hash_obj* top_stack
                         = vector_item(gStackFrame, vector_size(gStackFrame)-1);
                    var2 = hash_item(top_stack, string_c_str(var_name));

                    /// グローバル変数を参照 ///
                    if(var2 == NULL) {
                        var2 = hash_item(gGlobals, string_c_str(var_name));
                    }
                }

                if(var2) {
                    /// インデックスのパース ///
                    vector_obj* numbers = VECTOR_NEW(10);
                    if(!parse_number(string_c_str(var->mIndex), numbers, sname, line)) {
                        string_delete(pipeout2);
                        int i;
                        for(i=0; i<vector_size(numbers); i++) {
                            sIndex_delete(vector_item(numbers, i));
                        }
                        vector_delete(numbers);
                        return FALSE;
                    }

                    vector_obj* numbers2 = VECTOR_NEW(10);
                    if(!parse_number(string_c_str(var->mIndex2), numbers2, sname, line)) {
                        string_delete(pipeout2);
                        int i;
                        for(i=0; i<vector_size(numbers); i++) {
                            sIndex_delete(vector_item(numbers, i));
                        }
                        vector_delete(numbers);
                        for(i=0; i<vector_size(numbers2); i++) {
                            sIndex_delete(vector_item(numbers2, i));
                        }
                        vector_delete(numbers2);
                        return FALSE;
                    }
                    if(!vcat_expand_env(pipeout2, string_c_str(var2), numbers, sname, line)) {
                        int i;
                        for(i=0; i<vector_size(numbers); i++) {
                            sIndex_delete(vector_item(numbers, i));
                        }
                        vector_delete(numbers);
                        for(i=0; i<vector_size(numbers2); i++) {
                            sIndex_delete(vector_item(numbers2, i));
                        }
                        vector_delete(numbers2);

                        string_delete(pipeout2);
                        return FALSE;
                    }

                    int i;
                    for(i=0; i<vector_size(numbers); i++) {
                        sIndex_delete(vector_item(numbers, i));
                    }
                    vector_delete(numbers);
                    for(i=0; i<vector_size(numbers2); i++) {
                        sIndex_delete(vector_item(numbers2, i));
                    }
                    vector_delete(numbers2);
                }
                else {
                    /// 環境変数を参照 ///
                    char* var3 = getenv(string_c_str(var_name));

                    /// 環境変数があるなら ///
                    if(var3) {
                        string_obj* var4 = STRING_NEW(var3);

                        /// インデックスのパース ///
                        vector_obj* numbers = VECTOR_NEW(10);

                        if(!parse_number(string_c_str(var->mIndex), numbers, sname, line)) {
                            string_delete(var4);
                            int i;
                            for(i=0; i<vector_size(numbers); i++) {
                                sIndex_delete(vector_item(numbers, i));
                            }
                            vector_delete(numbers);
                            return FALSE;
                        }

                        vector_obj* numbers2 = VECTOR_NEW(10);
                        if(!parse_number(string_c_str(var->mIndex2), numbers2, sname, line)) {
                            string_delete(var4);
                            int i;
                            for(i=0; i<vector_size(numbers); i++) {
                                sIndex_delete(vector_item(numbers, i));
                            }
                            vector_delete(numbers);
                            for(i=0; i<vector_size(numbers2); i++) {
                                sIndex_delete(vector_item(numbers2, i));
                            }
                            vector_delete(numbers2);
                            return FALSE;
                        }

                        string_obj* pipeout2 = STRING_NEW("");

                        if(!vcat_expand_env(pipeout2, string_c_str(var4), numbers, sname, line)) {
                            string_delete(var4);
                            int i;
                            for(i=0; i<vector_size(numbers); i++) {
                                sIndex_delete(vector_item(numbers, i));
                            }
                            vector_delete(numbers);
                            for(i=0; i<vector_size(numbers2); i++) {
                                sIndex_delete(vector_item(numbers2, i));
                            }
                            vector_delete(numbers2);

                            string_delete(pipeout2);
                            return FALSE;
                        }

                        vector_add(argv, pipeout2);

                        int i;
                        for(i=0; i<vector_size(numbers); i++) {
                            sIndex_delete(vector_item(numbers, i));
                        }
                        vector_delete(numbers);
                        for(i=0; i<vector_size(numbers2); i++) {
                            sIndex_delete(vector_item(numbers2, i));
                        }
                        vector_delete(numbers2);
                        string_delete(var4);
                    }
                    else {
                        /// 配列を参照 ///
                        vector_obj* array = hash_item(gArrays, string_c_str(var_name));

                        if(array) {
                            /// インデックスのパース ///
                            vector_obj* numbers = VECTOR_NEW(10);
                            if(!parse_number(string_c_str(var->mIndex), numbers, sname, line)) {
                                string_delete(pipeout2);
                                int i;
                                for(i=0; i<vector_size(numbers); i++) {
                                    sIndex_delete(vector_item(numbers, i));
                                }
                                vector_delete(numbers);
                                return FALSE;
                            }

                            vector_obj* numbers2 = VECTOR_NEW(10);
                            if(!parse_number(string_c_str(var->mIndex2), numbers2, sname, line)) {
                                string_delete(pipeout2);
                                int i;
                                for(i=0; i<vector_size(numbers); i++) {
                                    sIndex_delete(vector_item(numbers, i));
                                }
                                vector_delete(numbers);
                                for(i=0; i<vector_size(numbers2); i++) {
                                    sIndex_delete(vector_item(numbers2, i));
                                }
                                vector_delete(numbers2);
                                return FALSE;
                            }

                            /// 添え字なし ///
                            if(vector_size(numbers) == 0) {
                                int k;
                                for(k=0; k<vector_size(array); k++) {
                                    string_obj* str = vector_item(array, k);

                                    string_push_back(pipeout2, string_c_str(str));

                                    if(k<vector_size(array)-1)
                                        string_push_back(pipeout2
                                                , string_c_str(field));
                                }
                            }
                            /// 添え字あり ///
                            else {
                                int i;
                                for(i=0; i<vector_size(numbers); i++) {
                                    sIndex* index = vector_item(numbers, i);

                                    if(index->number_num == 1) { // 添え字が一つ
                                        int n = index->number;

                                        if(n < 0) {
                                            n = vector_size(array) + n;
                                        }

                                        if(n >= 0 && n < vector_size(array)) {
                                            string_obj* str = vector_item(array, n);
                                            if(!vcat_expand_env(pipeout2, 
                                                    string_c_str(str), numbers2, sname, line)) 
                                            {
                                                string_delete(pipeout2);
                                                for(i=0; i<vector_size(numbers); i++) {
                                                    sIndex_delete(vector_item(numbers, i));
                                                }
                                                vector_delete(numbers);
                                                for(i=0; i<vector_size(numbers2); i++) {
                                                    sIndex_delete(vector_item(numbers2, i));
                                                }
                                                vector_delete(numbers2);
                                                return FALSE;
                                            }
                                        }
                                    }
                                    else { // 添え字が範囲
                                        int n = index->number;
                                        int m = index->number2;

                                        if(n < 0) n = vector_size(array) + n;
                                        if(m < 0) m = vector_size(array) + m;

                                        if(n >= vector_size(array)) n = vector_size(array)-1;
                                        if(m >= vector_size(array)) m = vector_size(array)-1;

                                        if(n < 0) n = 0;
                                        if(m < 0) m = 0;

                                        if(n < m) {
                                            int k;
                                            for(k=n; k<=m; k++) {
                                                string_obj* str = vector_item(array, k);

                                                if(!vcat_expand_env(pipeout2
                                                    , string_c_str(str), numbers2, sname, line)) 
                                                {
                                                    string_delete(pipeout2);
                                                    for(i=0; i<vector_size(numbers); i++) {
                                                        sIndex_delete(vector_item(numbers, i));
                                                    }
                                                    vector_delete(numbers);
                                                    for(i=0; i<vector_size(numbers2); i++) {
                                                        sIndex_delete(vector_item(numbers2, i));
                                                    }
                                                    vector_delete(numbers2);
                                                    return FALSE;
                                                }

                                                if(k<=m-1) 
                                                  string_push_back(pipeout2
                                                                , string_c_str(field));
                                            }
                                        }
                                        else {
                                            int k;
                                            for(k=n; k>=m; k--) {
                                                string_obj* str = vector_item(array, k);

                                                if(!vcat_expand_env(pipeout2
                                                    , string_c_str(str), numbers2, sname, line)) 
                                                {
                                                    string_delete(pipeout2);
                                                    for(i=0; i<vector_size(numbers); i++) {
                                                        sIndex_delete(vector_item(numbers, i));
                                                    }
                                                    vector_delete(numbers);
                                                    for(i=0; i<vector_size(numbers2); i++) {
                                                        sIndex_delete(vector_item(numbers2, i));
                                                    }
                                                    vector_delete(numbers2);
                                                    return FALSE;
                                                }

                                                if(k>m)
                                                      string_push_back(
                                                        pipeout2
                                                        , string_c_str(field));
                                            }
                                        }
                                    }

                                    if(i!=vector_size(numbers)-1)
                                        string_push_back(pipeout2, string_c_str(field));
                                }
                            }
                            int i;
                            for(i=0; i<vector_size(numbers); i++) {
                                sIndex_delete(vector_item(numbers, i));
                            }
                            vector_delete(numbers);
                            for(i=0; i<vector_size(numbers2); i++) {
                                sIndex_delete(vector_item(numbers2, i));
                            }
                            vector_delete(numbers2);
                        }
                        else {
                            hash_obj* hash = hash_item(gHashs, string_c_str(var_name));

                            /// ハッシュ ///
                            if(hash) {
                                vector_obj* numbers2 = VECTOR_NEW(10);
                                if(!parse_number(string_c_str(var->mIndex2), numbers2, sname, line)) {
                                    string_delete(pipeout2);
                                    int i;
                                    for(i=0; i<vector_size(numbers2); i++) {
                                        sIndex_delete(vector_item(numbers2, i));
                                    }
                                    vector_delete(numbers2);
                                    return FALSE;
                                }

                                if(strcmp(string_c_str(var->mIndex), "") == 0) {
                                    hash_it* it = hash_loop_begin(hash);
                                    int k = 0;
                                    while(it != NULL) {
                                        string_obj* str = hash_loop_item(it);
                                        char* key2 = hash_loop_key(it);

                                        if(!vcat_expand_env(pipeout2, key2, numbers2, sname, line)) 
                                        {
                                            string_delete(pipeout2);
                                            int i;
                                            for(i=0; i<vector_size(numbers2); i++) {
                                                sIndex_delete(vector_item(numbers2, i));
                                            }
                                            vector_delete(numbers2);
                                            return FALSE;
                                        }

                                        string_push_back(pipeout2, string_c_str(field));

                                        if(!vcat_expand_env(pipeout2
                                            , string_c_str(str), numbers2, sname, line)) 
                                        {
                                            string_delete(pipeout2);
                                            int i;
                                            for(i=0; i<vector_size(numbers2); i++) {
                                                sIndex_delete(vector_item(numbers2, i));
                                            }
                                            vector_delete(numbers2);
                                            return FALSE;
                                        }

                                        if(k < hash_count(hash)-1)
                                            string_push_back(pipeout2, string_c_str(field));

                                        it = hash_loop_next(it);
                                        k++;
                                    }
                                }
                                else {
                                    string_obj* str = hash_item(hash, string_c_str(var->mIndex));

                                    if(str) {
                                        if(!vcat_expand_env(pipeout2
                                            , string_c_str(str), numbers2, sname, line)) 
                                        {
                                            string_delete(pipeout2);
                                            int i;
                                            for(i=0; i<vector_size(numbers2); i++) {
                                                sIndex_delete(vector_item(numbers2, i));
                                            }
                                            vector_delete(numbers2);
                                            return FALSE;
                                        }
                                    }
                                }

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

                vector_add(argv, pipeout2);
                }
                break;

            case 2: {
                sStatments* statments = arg->mBody;
                sWFd* pipeout = sWFd_new(-1);
                int rcode = run(statments, "quick command expansion"
                    , pipeout, pipein, 2, FALSE, FALSE, return_, parent_blocks, FALSE);

                if(rcode == -1) {
                    sWFd_delete(pipeout);

                    return FALSE;
                }
                if(*return_) {
                    sWFd_delete(pipeout);

                    return TRUE;
                }

                vector_add(argv, STRING_NEW(pipeout->mBuffer));

                sWFd_delete(pipeout);
                }
                break;

            case 3: {
                sVar* var = arg->mBody;
                
                string_obj* var_name = var->mName;
                string_obj* field = var->mDelimiter;

                string_obj* var2;
                if(vector_size(gStackFrame) == 0) {
                    /// グローバル変数を参照 ///
                    var2 = hash_item(gGlobals, string_c_str(var_name));
                }
                else {
                    /// ローカル変数を参照 ///
                    hash_obj* top_stack
                         = vector_item(gStackFrame, vector_size(gStackFrame)-1);
                    var2 = hash_item(top_stack, string_c_str(var_name));

                    /// グローバル変数を参照 ///
                    if(var2 == NULL) {
                        var2 = hash_item(gGlobals, string_c_str(var_name));
                    }
                }

                if(var2) {
                    /// インデックスのパース ///
                    vector_obj* numbers = VECTOR_NEW(10);

                    if(!parse_number(string_c_str(var->mIndex), numbers, sname, line)) {
                        int i;
                        for(i=0; i<vector_size(numbers); i++) {
                            sIndex_delete(vector_item(numbers, i));
                        }
                        vector_delete(numbers);
                        return FALSE;
                    }

                    vector_obj* numbers2 = VECTOR_NEW(10);
                    if(!parse_number(string_c_str(var->mIndex2), numbers2, sname, line)) {
                        int i;
                        for(i=0; i<vector_size(numbers); i++) {
                            sIndex_delete(vector_item(numbers, i));
                        }
                        vector_delete(numbers);
                        for(i=0; i<vector_size(numbers2); i++) {
                            sIndex_delete(vector_item(numbers2, i));
                        }
                        vector_delete(numbers2);
                        return FALSE;
                    }

                    string_obj* pipeout2 = STRING_NEW("");

                    if(!vcat_expand_env(pipeout2, string_c_str(var2), numbers, sname, line)) {
                        int i;
                        for(i=0; i<vector_size(numbers); i++) {
                            sIndex_delete(vector_item(numbers, i));
                        }
                        vector_delete(numbers);
                        for(i=0; i<vector_size(numbers2); i++) {
                            sIndex_delete(vector_item(numbers2, i));
                        }
                        vector_delete(numbers2);

                        string_delete(pipeout2);
                        return FALSE;
                    }

                    vector_add(argv, pipeout2);
                    int i;
                    for(i=0; i<vector_size(numbers); i++) {
                        sIndex_delete(vector_item(numbers, i));
                    }
                    vector_delete(numbers);
                    for(i=0; i<vector_size(numbers2); i++) {
                        sIndex_delete(vector_item(numbers2, i));
                    }
                    vector_delete(numbers2);
                }
                else {
                    /// 環境変数を参照 ///
                    char* var3 = getenv(string_c_str(var_name));

                    /// 環境変数があるなら ///
                    if(var3) {
                        string_obj* var4 = STRING_NEW(var3);

                        /// インデックスのパース ///
                        vector_obj* numbers = VECTOR_NEW(10);

                        if(!parse_number(string_c_str(var->mIndex), numbers, sname, line)) {
                            string_delete(var4);
                            int i;
                            for(i=0; i<vector_size(numbers); i++) {
                                sIndex_delete(vector_item(numbers, i));
                            }
                            vector_delete(numbers);
                            return FALSE;
                        }

                        vector_obj* numbers2 = VECTOR_NEW(10);
                        if(!parse_number(string_c_str(var->mIndex2), numbers2, sname, line)) {
                            string_delete(var4);
                            int i;
                            for(i=0; i<vector_size(numbers); i++) {
                                sIndex_delete(vector_item(numbers, i));
                            }
                            vector_delete(numbers);
                            for(i=0; i<vector_size(numbers2); i++) {
                                sIndex_delete(vector_item(numbers2, i));
                            }
                            vector_delete(numbers2);
                            return FALSE;
                        }

                        string_obj* pipeout2 = STRING_NEW("");

                        if(!vcat_expand_env(pipeout2, string_c_str(var4), numbers, sname, line)) {
                            string_delete(var4);
                            int i;
                            for(i=0; i<vector_size(numbers); i++) {
                                sIndex_delete(vector_item(numbers, i));
                            }
                            vector_delete(numbers);
                            for(i=0; i<vector_size(numbers2); i++) {
                                sIndex_delete(vector_item(numbers2, i));
                            }
                            vector_delete(numbers2);

                            string_delete(pipeout2);
                            return FALSE;
                        }

                        vector_add(argv, pipeout2);

                        int i;
                        for(i=0; i<vector_size(numbers); i++) {
                            sIndex_delete(vector_item(numbers, i));
                        }
                        vector_delete(numbers);
                        for(i=0; i<vector_size(numbers2); i++) {
                            sIndex_delete(vector_item(numbers2, i));
                        }
                        vector_delete(numbers2);

                        string_delete(var4);
                    }
                    else {
                        /// 配列を参照 ///
                        vector_obj* array = hash_item(gArrays, string_c_str(var_name));

                        if(array) {
                            /// インデックスのパース ///
                            vector_obj* numbers = VECTOR_NEW(10);

                            if(!parse_number(string_c_str(var->mIndex), numbers, sname, line)) {
                                int i;
                                for(i=0; i<vector_size(numbers); i++) {
                                    sIndex_delete(vector_item(numbers, i));
                                }
                                vector_delete(numbers);
                                return FALSE;
                            }

                            vector_obj* numbers2 = VECTOR_NEW(10);
                            if(!parse_number(string_c_str(var->mIndex2), numbers2, sname, line)) {
                                int i;
                                for(i=0; i<vector_size(numbers); i++) {
                                    sIndex_delete(vector_item(numbers, i));
                                }
                                vector_delete(numbers);
                                for(i=0; i<vector_size(numbers2); i++) {
                                    sIndex_delete(vector_item(numbers2, i));
                                }
                                vector_delete(numbers2);
                                return FALSE;
                            }

                            /// 添え字なし ///
                            if(vector_size(numbers) == 0) {
                                int k;
                                for(k=0; k<vector_size(array); k++) {
                                    string_obj* str = vector_item(array, k);

                                    vector_add(argv
                                        , STRING_NEW(string_c_str(str)));
                                }
                            }
                            /// 添え字あり ///
                            else {
                                int i;
                                for(i=0; i<vector_size(numbers); i++) {
                                    sIndex* index = vector_item(numbers, i);

                                    if(index->number_num == 1) { // 添え字が一つ
                                        int n = index->number;

                                        if(n < 0) {
                                            n = vector_size(array) + n;
                                        }

                                        if(n >= 0 && n < vector_size(array)) {
                                            string_obj* str = vector_item(array, n);
                                            string_obj* pipeout2 = STRING_NEW("");
                                            if(!vcat_expand_env(pipeout2, 
                                                    string_c_str(str), numbers2, sname, line)) 
                                            {
                                                string_delete(pipeout2);
                                                for(i=0; i<vector_size(numbers); i++) {
                                                    sIndex_delete(vector_item(numbers, i));
                                                }
                                                vector_delete(numbers);
                                                for(i=0; i<vector_size(numbers2); i++) {
                                                    sIndex_delete(vector_item(numbers2, i));
                                                }
                                                vector_delete(numbers2);
                                                return FALSE;
                                            }

                                            vector_add(argv, pipeout2);
                                        }
                                    }
                                    else { // 添え字が範囲
                                        int n = index->number;
                                        int m = index->number2;

                                        if(n < 0) n = vector_size(array) + n;
                                        if(m < 0) m = vector_size(array) + m;

                                        if(n >= vector_size(array)) n = vector_size(array)-1;
                                        if(m >= vector_size(array)) m = vector_size(array)-1;

                                        if(n < 0) n = 0;
                                        if(m < 0) m = 0;

                                        if(n < m) {
                                            int k;
                                            for(k=n; k<=m; k++) {
                                                string_obj* str = vector_item(array, k);
                                                string_obj* pipeout2 = STRING_NEW("");

                                                if(!vcat_expand_env(pipeout2
                                                    , string_c_str(str), numbers2, sname, line)) 
                                                {
                                                    string_delete(pipeout2);
                                                    for(i=0; i<vector_size(numbers); i++) {
                                                        sIndex_delete(vector_item(numbers, i));
                                                    }
                                                    vector_delete(numbers);
                                                    for(i=0; i<vector_size(numbers2); i++) {
                                                        sIndex_delete(vector_item(numbers2, i));
                                                    }
                                                    vector_delete(numbers2);
                                                    return FALSE;
                                                }

                                                vector_add(argv, pipeout2);
                                            }
                                        }
                                        else {
                                            int k;
                                            for(k=n; k>=m; k--) {
                                                string_obj* str = vector_item(array, k);
                                                string_obj* pipeout2 = STRING_NEW("");

                                                if(!vcat_expand_env(pipeout2
                                                    , string_c_str(str), numbers2, sname, line)) 
                                                {
                                                    string_delete(pipeout2);
                                                    for(i=0; i<vector_size(numbers); i++) {
                                                        sIndex_delete(vector_item(numbers, i));
                                                    }
                                                    vector_delete(numbers);
                                                    for(i=0; i<vector_size(numbers2); i++) {
                                                        sIndex_delete(vector_item(numbers2, i));
                                                    }
                                                    vector_delete(numbers2);
                                                    return FALSE;
                                                }

                                                vector_add(argv, pipeout2);
                                            }
                                        }
                                    }
                                }
                            }
                            
                            int i;
                            for(i=0; i<vector_size(numbers); i++) {
                                sIndex_delete(vector_item(numbers, i));
                            }
                            vector_delete(numbers);
                            for(i=0; i<vector_size(numbers2); i++) {
                                sIndex_delete(vector_item(numbers2, i));
                            }
                            vector_delete(numbers2);
                        }
                        else {
                            hash_obj* hash = hash_item(gHashs, string_c_str(var_name));

                            /// ハッシュ ///
                            if(hash) {
                                vector_obj* numbers2 = VECTOR_NEW(10);
                                if(!parse_number(string_c_str(var->mIndex2), numbers2, sname, line)) {
                                    int i;
                                    for(i=0; i<vector_size(numbers2); i++) {
                                        sIndex_delete(vector_item(numbers2, i));
                                    }
                                    vector_delete(numbers2);
                                    return FALSE;
                                }

                                if(strcmp(string_c_str(var->mIndex), "") == 0) {
                                    hash_it* it = hash_loop_begin(hash);
                                    int k = 0;
                                    while(it != NULL) {
                                        string_obj* str = hash_loop_item(it);
                                        char* key2 = hash_loop_key(it);

                                        string_obj* pipeout2 = STRING_NEW("");

                                        if(!vcat_expand_env(pipeout2, key2, numbers2, sname, line)) 
                                        {
                                            string_delete(pipeout2);
                                            int i;
                                            for(i=0; i<vector_size(numbers2); i++) {
                                                sIndex_delete(vector_item(numbers2, i));
                                            }
                                            vector_delete(numbers2);
                                            return FALSE;
                                        }

                                        vector_add(argv, pipeout2);

                                        pipeout2 = STRING_NEW("");

                                        if(!vcat_expand_env(pipeout2
                                            , string_c_str(str), numbers2, sname, line)) 
                                        {
                                            string_delete(pipeout2);
                                            int i;
                                            for(i=0; i<vector_size(numbers2); i++) {
                                                sIndex_delete(vector_item(numbers2, i));
                                            }
                                            vector_delete(numbers2);
                                            return FALSE;
                                        }

                                        vector_add(argv, pipeout2);

                                        it = hash_loop_next(it);
                                        k++;
                                    }
                                }
                                else {
                                    string_obj* str = hash_item(hash, string_c_str(var->mIndex));

                                    if(str) {
                                        string_obj* pipeout2 = STRING_NEW("");
                                        if(!vcat_expand_env(pipeout2
                                            , string_c_str(str), numbers2, sname, line)) 
                                        {
                                            string_delete(pipeout2);
                                            int i;
                                            for(i=0; i<vector_size(numbers2); i++) {
                                                sIndex_delete(vector_item(numbers2, i));
                                            }
                                            vector_delete(numbers2);
                                            return FALSE;
                                        }

                                        vector_add(argv, pipeout2);
                                    }
                                }

                                int i;
                                for(i=0; i<vector_size(numbers2); i++) {
                                    sIndex_delete(vector_item(numbers2, i));
                                }
                                vector_delete(numbers2);
                            }
                        }
                    }
                }
                }
                break;

            case 4: {
                sStatments* statments = arg->mBody;
                sWFd* pipeout = sWFd_new(-1);
                int rcode = run(statments, "quick command expansion"
                    , pipeout, pipein, 2, FALSE, FALSE, return_, parent_blocks, FALSE);

                if(rcode == -1) {
                    sWFd_delete(pipeout);

                    return FALSE;
                }
                if(*return_) {
                    sWFd_delete(pipeout);

                    return TRUE;
                }

                sRFd* tmp = sRFd_new2(-1, pipeout->mBuffer);
                while(1) {
                    string_obj* str = STRING_NEW("");
                    int ret = sRFd_read_oneline(tmp, str);

                    if(ret < -1) {
                        string_delete(str);
                        sWFd_delete(pipeout);
                        sRFd_delete(tmp);

                        return FALSE;
                    }
                    else if(ret == 1) {
                        string_delete(str);
                        break;
                    }

                    string_chomp(str);
                    vector_add(argv, str);
                }

                sRFd_delete(tmp);
                sWFd_delete(pipeout);
                }
                break;

            case 5: {
                vector_add(blocks, arg->mBody);
                }
                break;
        }
    }

    return TRUE;
}

// 文を回す
// TRUE 正常終了
// FALSE エラー
static BOOL statment_tree(sStatment* statment, BOOL* last_command_is_inner, int* last_program_pid, sWFd* pipeout, sRFd* pipein, int pipeerr, char* title, int* rcode, pid_t* pgroup, vector_obj* cprogs, BOOL finaly_exec, BOOL in_loop, BOOL* return_, vector_obj* parent_blocks, char* sname, int line) 
{
    sRFd* nextin = pipein;
    int nexterr;

    /// 回す ///
    int j;
    for(j=0; j<vector_size(statment->mCommands); j++) {
        sCommand* command = vector_item(statment->mCommands, j);

        /// パイプ ///
        int pipefds[2] = { -1, -1 };

        const BOOL first_program = j == 0;
        const BOOL last_program = j == (vector_size(statment->mCommands)-1);
        int exist_globalin;
        if(first_program) {
            exist_globalin = statment->mGlobalPipeIn & 0x0F;
        }
        else {
            exist_globalin = 0;
        }

        int exist_globalout;
        int exist_globalout_append;
        if(last_program) {
            exist_globalout = statment->mGlobalPipeOut & 0x0F;
            exist_globalout_append = statment->mGlobalPipeOut & 0xF0;

        }
        else {
            exist_globalout = 0;
            exist_globalout_append = 0;
        }

        /// グローバルパイプ処理 ///
        if(exist_globalin == 1) {
            if(nextin != pipein) {
                sRFd_delete(nextin);
            }

            nextin = sRFd_new(-1);
            string_put(nextin->mBuffer, string_c_str(gGlobalPipe));
        }
        else if(exist_globalin == 2) {
            if(vector_size(gGlobalPipes) == 0) {
                err_msg("stackable global pipe doesn't exist", sname, line);
                if(nextin != pipein) sRFd_delete(nextin);
                return FALSE;
            }

            if(nextin != pipein) {
                sRFd_delete(nextin);
            }

            nextin = sRFd_new(-1);
            string_obj* gpipe = vector_pop_back(gGlobalPipes);
            string_put(nextin->mBuffer, string_c_str(gpipe));
            string_delete(gpipe);
        }
        else if(exist_globalin == 3) {
            if(vector_size(gQGlobalPipes) == 0) {
                err_msg("queue global pipe doesn't exist", sname, line);
                if(nextin != pipein) sRFd_delete(nextin);
                return FALSE;
            }

            if(nextin != pipein) {
                sRFd_delete(nextin);
            }

            nextin = sRFd_new(-1);
            string_obj* gpipe = vector_pop_front(gQGlobalPipes);
            string_put(nextin->mBuffer, string_c_str(gpipe));
            string_delete(gpipe);
        }
        else if(exist_globalin == 4) {
            if(nextin != pipein) {
                sRFd_delete(nextin);
            }

            int num = statment->mGlobalPipeInNum;
            nextin = sRFd_new(-1);
            string_put(nextin->mBuffer, string_c_str(gGlobalPipeNum[num]));
        }

        // argvを引数の削除とかできるようにコピーしておく
        vector_obj* argv = VECTOR_NEW(30);
        vector_obj* blocks = VECTOR_NEW(30);
        if(!statment_tree_make_argv(command, argv, blocks, parent_blocks, return_, nextin, pipein, sname, line)) {
            int ii;
            for(ii=0; ii<vector_size(argv); ii++) {
                string_delete(vector_item(argv, ii));
            }
            vector_delete(argv);
            vector_delete(blocks);
            if(nextin != pipein) sRFd_delete(nextin);
            return FALSE;
        }
        if(*return_) {
            int ii;
            for(ii=0; ii<vector_size(argv); ii++) {
                string_delete(vector_item(argv, ii));
            }
            vector_delete(argv);
            vector_delete(blocks);
            if(nextin != pipein) sRFd_delete(nextin);
            return TRUE;
        }

        int ii;

        char* arg0 = string_c_str(vector_item(argv, 0));
        sFunction* fun = hash_item(gFuncs, arg0);
        sInnerCommand* inner_fun = hash_item(gInnerCommands, arg0);

        if(fun || inner_fun) {
            command->mKind = kInnerCommand;
        }
        /// パイプ処理 ///
        sWFd* nextout;
        if(!exist_globalout && last_program) {  // 最後のパイプ
            nextout = pipeout;
        }
        else { // 最初か途中のパイプ
            if(command->mKind == kCommand) {
                if(pipe(pipefds) < 0) {
                    perror("pipe");
                    exit(1);
                }

                nextout = sWFd_new(pipefds[1]);
                pipefds[1] = -1;
            }
            else {
                nextout = sWFd_new(-1);
            }
        }

        nexterr = pipeerr;

        /// ローカル変数代入 ///
        char* equalp = strstr(arg0, "=");
        if(equalp) {
            if(!statment_tree_input_var(equalp, arg0, argv, rcode, sname, line)) {
                int ii;
                for(ii=0; ii<vector_size(argv); ii++) {
                    string_delete(vector_item(argv, ii));
                }
                vector_delete(argv);
                vector_delete(blocks);

                if(nextout != pipeout) sWFd_delete(nextout);
                if(nextin != pipein) sRFd_delete(nextin);
                return FALSE;
            }
        }
        /// ユーザーコマンド ///
        else if(fun) {
            *last_command_is_inner = TRUE;

            if(!statment_tree_redirect(command, &nextin, &nextout
                            , &nexterr, pipein, pipeout, pipeerr))
            {
                int ii;
                for(ii=0; ii<vector_size(argv); ii++) {
                    string_delete(vector_item(argv, ii));
                }
                vector_delete(argv);
                vector_delete(blocks);

                if(nextout != pipeout) sWFd_delete(nextout);
                if(nextin != pipein) sRFd_delete(nextin);
                return FALSE;
            }


            /// 引数名が指定されていなければスタックフレームを増やす ///
            vector_obj* fun_argv_before;
            vector_obj* fun_argv;
            if(strcmp(string_c_str(fun->arg_name), "") == 0) {
                /// 前の引数を保存 ///
                fun_argv_before = hash_item(gArrays, "ARGV");

                /// 引数を渡す ///
                int k;
                fun_argv = VECTOR_NEW(30);
                for(k=1; k<vector_size(argv); k++) {
                    char* value = string_c_str(vector_item(argv, k));
                    vector_add(fun_argv, STRING_NEW(value));
                }
                
                hash_put(gArrays, "ARGV", fun_argv);
                update_ary_env("ARGV");

                vector_add(gStackFrame, HASH_NEW(30));
            }
            /// 引数名が指定されていなければスタックフレームは増やさない
            else {
                /// 前の引数を保存 ///
                fun_argv_before = hash_item(gArrays
                                        , string_c_str(fun->arg_name));

                /// 引数を渡す ///
                int k;
                fun_argv = VECTOR_NEW(30);
                for(k=1; k<vector_size(argv); k++) {
                    char* value = string_c_str(vector_item(argv, k));
                    vector_add(fun_argv, STRING_NEW(value));
                }
                
                hash_put(gArrays, string_c_str(fun->arg_name), fun_argv);
                update_ary_env(string_c_str(fun->arg_name));
            }

            /// スタックフレームの記録を付ける
            vector_add(gStackTraceFName, STRING_NEW(sname));
            vector_add(gStackTraceLineNum, (void*)line);

            /// 実行 ///
            *rcode = run(fun->statments, title
                , nextout, nextin, nexterr, finaly_exec, in_loop, return_, blocks, FALSE);
            *return_ = FALSE;

            if(strcmp(string_c_str(fun->arg_name), "") == 0) {
                /// スタックを消す ///
                hash_obj* stack = vector_pop_back(gStackFrame);

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

                /// 引数を消す ///
                int k;
                for(k=0; k<vector_size(fun_argv); k++) {
                    string_delete(vector_item(fun_argv, k));
                }
                vector_delete(fun_argv);

                hash_erase(gArrays, "ARGV");

                /// 前の引数を元に戻す ///
                if(fun_argv_before) {
                    hash_put(gArrays, "ARGV", fun_argv_before);
                    update_ary_env("ARGV");
                }
            }
            else {
                /// 引数を消す ///
                int k;
                for(k=0; k<vector_size(fun_argv); k++) {
                    string_delete(vector_item(fun_argv, k));
                }
                vector_delete(fun_argv);

                hash_erase(gArrays, string_c_str(fun->arg_name));

                /// 前の引数を元に戻す ///
                if(fun_argv_before) {
                    hash_put(gArrays, string_c_str(fun->arg_name)
                                , fun_argv_before);
                    update_ary_env(string_c_str(fun->arg_name));
                }
            }

            /// 終了コード ///
            if(statment->mRCodeReverse) {
                if(*rcode == 0) 
                    *rcode = 1;
                else
                    *rcode = 0;
            }

            char buf2[256];
            snprintf(buf2, 256, "%d", *rcode);
            saphire_set_local_var("RCODE", buf2);
/*
            if(*return_) {
                int ii;
                for(ii=0; ii<vector_size(argv); ii++) {
                    string_delete(vector_item(argv, ii));
                }
                vector_delete(argv);
                vector_delete(blocks);

                if(nextout != pipeout) sWFd_delete(nextout);
                if(nextin != pipein) sRFd_delete(nextin);
                return TRUE;
            }
*/

            if(*rcode == -1) {
                string_obj* sname_strace = vector_pop_back(gStackTraceFName);
                int line_strace = (int)vector_pop_back(gStackTraceLineNum);

                string_push_back(gErrMsg, string_c_str(sname_strace));
                char tmp[1024];
                snprintf(tmp, 1024, ":%d\n", line_strace);
                string_push_back(gErrMsg, tmp);
                
                string_delete(sname_strace);

                int ii;
                for(ii=0; ii<vector_size(argv); ii++) {
                    string_delete(vector_item(argv, ii));
                }
                vector_delete(argv);
                vector_delete(blocks);

                if(nextout != pipeout) sWFd_delete(nextout);
                if(nextin != pipein) sRFd_delete(nextin);
                return FALSE;
            }
        }

        /// 外部登録の内部コマンド ///
        else if(inner_fun) {
            *last_command_is_inner = TRUE;
            
            if(!statment_tree_redirect(command, &nextin, &nextout
                                    , &nexterr
                                    , pipein, pipeout, pipeerr))
            {
                int ii;
                for(ii=0; ii<vector_size(argv); ii++) {
                    string_delete(vector_item(argv, ii));
                }
                vector_delete(argv);
                vector_delete(blocks);

                if(nextout != pipeout) sWFd_delete(nextout);
                if(nextin != pipein) sRFd_delete(nextin);
                return FALSE;
            }

            *rcode = 1; // 初期値は1。コマンドが成功したら0を入れる

            if((gAppType == kATCursesApp 
                    || gAppType == kATConsoleApp)
                && !statment->mBackground )
            {
                if(tcsetpgrp(0, getpgid(0)) < 0) {
                    perror("tcsetpgrp(inner command)");
                    exit(1);
                }
            }

            BOOL r = inner_fun->mFun(rcode, argv, blocks, parent_blocks
                        , nextout, nextin, nexterr
                        , title, !first_program || exist_globalin);

            /// 終了コード ///
            if(statment->mRCodeReverse) {
                if(*rcode == 0) 
                    *rcode = 1;
                else
                    *rcode = 0;
            }

            char buf[256];
            snprintf(buf, 256, "%d", *rcode);
            saphire_set_local_var("RCODE", buf);
            if(!r) {
                int ii;
                for(ii=0; ii<vector_size(argv); ii++) {
                    string_delete(vector_item(argv, ii));
                }
                vector_delete(argv);
                vector_delete(blocks);

                if(nextout != pipeout) sWFd_delete(nextout);
                if(nextin != pipein) sRFd_delete(nextin);
                return FALSE;
            }
        }

        /// 内部コマンド ///
        else if(command->mKind != kCommand) {
//printf("command->mKind %d %s\n", command->mKind, gStatmentKindStrs[command->mKind]);
            *last_command_is_inner = TRUE;
            
            if(!statment_tree_redirect(command, &nextin, &nextout
                                    , &nexterr
                                    , pipein, pipeout, pipeerr))
            {
                int ii;
                for(ii=0; ii<vector_size(argv); ii++) {
                    string_delete(vector_item(argv, ii));
                }
                vector_delete(argv);
                vector_delete(blocks);

                if(nextout != pipeout) sWFd_delete(nextout);
                if(nextin != pipein) sRFd_delete(nextin);
                return FALSE;
            }

            *rcode = 1; // 初期値は1。コマンドが成功したら0を入れる

           if((gAppType == kATCursesApp 
                    || gAppType == kATConsoleApp)
                && !statment->mBackground )
            {
                if(tcsetpgrp(0, getpgid(0)) < 0) {
                    perror("tcsetpgrp inner command");
                    exit(1);
                }
            }

            BOOL r = statment_tree_internal_commands(command, rcode
                , argv, blocks, parent_blocks, nextout, nextin, nexterr, j, cprogs
                , title, !first_program || exist_globalin,return_, sname, line);

            /// 終了コード ///
            if(statment->mRCodeReverse) {
                if(*rcode == 0) 
                    *rcode = 1;
                else
                    *rcode = 0;
            }

            char buf[256];
            snprintf(buf, 256, "%d", *rcode);
            saphire_set_local_var("RCODE",  buf);
            if(!r) {
                int ii;
                for(ii=0; ii<vector_size(argv); ii++) {
                    string_delete(vector_item(argv, ii));
                }
                vector_delete(argv);
                vector_delete(blocks);
                if(nextout != pipeout) sWFd_delete(nextout);
                if(nextin != pipein) sRFd_delete(nextin);
                return FALSE;
            }

            /// return
            if(*return_) {
                int ii;
                for(ii=0; ii<vector_size(argv); ii++) {
                    string_delete(vector_item(argv, ii));
                }
                vector_delete(argv);
                vector_delete(blocks);
                if(nextout != pipeout) sWFd_delete(nextout);
                if(nextin != pipein) sRFd_delete(nextin);
                return TRUE;
            }
        }

        /// 外部コマンド ///
        else {
//puts("外部コマンド");

            /// バッファーをフラッシュしておく
            if(nextout == pipeout) {
                if(!sWFd_flash(nextout)) {
                    int ii;
                    for(ii=0; ii<vector_size(argv); ii++) {
                        string_delete(vector_item(argv, ii));
                    }
                    vector_delete(argv);
                    vector_delete(blocks);
                    if(nextout != pipeout) sWFd_delete(nextout);
                    if(nextin != pipein) sRFd_delete(nextin);
                    return FALSE;
                }
            }

            /// リードバッファが空じゃないなら
            if(nextin->mFd != -1 && string_c_str(nextin->mBuffer)[0] != 0)
            {
                int ret = sRFd_read_all_to_buffer(nextin);

                if(ret < 0) {
                    int ii;
                    for(ii=0; ii<vector_size(argv); ii++) {
                        string_delete(vector_item(argv, ii));
                    }
                    vector_delete(argv);
                    vector_delete(blocks);

                    if(nextout != pipeout) sWFd_delete(nextout);
                    if(nextin != pipein) sRFd_delete(nextin);
                    return FALSE;
                }

                nextin->mFd = -1;

                *last_command_is_inner = FALSE;
                if(!statment_tree_external_command(statment, argv, nextin, nextout, nexterr, pipefds, pgroup, pipeout, command, cprogs, last_program, last_program_pid, sname, line))
                {
                    int ii;
                    for(ii=0; ii<vector_size(argv); ii++) {
                        string_delete(vector_item(argv, ii));
                    }
                    vector_delete(argv);
                    vector_delete(blocks);

                    if(nextout != pipeout) sWFd_delete(nextout);
                    if(nextin != pipein) sRFd_delete(nextin);
                    return FALSE;
                }
            }
            else {
                *last_command_is_inner = FALSE;
                if(!statment_tree_external_command(statment, argv, nextin, nextout, nexterr, pipefds, pgroup, pipeout, command, cprogs, last_program, last_program_pid,  sname, line))
                {
                    int ii;
                    for(ii=0; ii<vector_size(argv); ii++) {
                        string_delete(vector_item(argv, ii));
                    }
                    vector_delete(argv);
                    vector_delete(blocks);

                    if(nextout != pipeout) sWFd_delete(nextout);
                    if(nextin != pipein) sRFd_delete(nextin);
                    return FALSE;
                }
            }
        }

        /// パイプを閉める ///
        if(nextin->mFd != -1 && nextin != pipein) {
            if(!sRFd_close(nextin)) {
                int ii;
                for(ii=0; ii<vector_size(argv); ii++) {
                    string_delete(vector_item(argv, ii));
                }
                vector_delete(argv);
                vector_delete(blocks);

                if(nextout != pipeout) sWFd_delete(nextout);
                if(nextin != pipein) sRFd_delete(nextin);
                return FALSE;
            }
        }
        if(nextout->mFd != -1 && nextout != pipeout) {
            if(!sWFd_close(nextout)) {
                int ii;
                for(ii=0; ii<vector_size(argv); ii++) {
                    string_delete(vector_item(argv, ii));
                }
                vector_delete(argv);
                vector_delete(blocks);

                if(nextout != pipeout) sWFd_delete(nextout);
                if(nextin != pipein) sRFd_delete(nextin);
                return FALSE;
            }
        }
        if(nexterr != 2 && nexterr != pipeerr && nexterr != nextout->mFd && nexterr != -1) {
            if(!xclose(nexterr)) {
                int ii;
                for(ii=0; ii<vector_size(argv); ii++) {
                    string_delete(vector_item(argv, ii));
                }
                vector_delete(argv);
                vector_delete(blocks);
                if(nextout != pipeout) sWFd_delete(nextout);
                if(nextin != pipein) sRFd_delete(nextin);
                return FALSE;
            }
        }

        /// グローバルパイプへの出力 ///
        if(exist_globalout == 1) {
            if(command->mKind == kCommand) {
                string_obj* gpipe = STRING_NEW("");

                char buf[BUFSIZ+1];
                while(1) {
                    int r = read(pipefds[0], buf, BUFSIZ);
                    if(r < 0) {
                        perror("read gpipe");
                        exit(1);
                    }
                    buf[r] = 0;

                    string_push_back(gpipe, buf);

                    if(r == 0) {
                        break;
                    }
                }

                close(pipefds[0]);
                pipefds[0] = -1;
                if(exist_globalout_append) {
                    string_push_back(gGlobalPipe, string_c_str(gpipe));
                }
                else {
                    string_put(gGlobalPipe, string_c_str(gpipe));
                }
                string_delete(gpipe);
            }
            else {
                if(exist_globalout_append) {
                    string_push_back(gGlobalPipe, nextout->mBuffer);
                }
                else {
                    string_put(gGlobalPipe, nextout->mBuffer);
                }
                *nextout->mBuffer = 0;
            }
        }
        else if(exist_globalout == 2) {
            if(command->mKind == kCommand) {
                string_obj* gpipe = STRING_NEW("");

                char buf[BUFSIZ+1];
                while(1) {
                    int r = read(pipefds[0], buf, BUFSIZ);
                    if(r < 0) {
                        perror("read gpipe");
                        exit(1);
                    }
                    buf[r] = 0;

                    string_push_back(gpipe, buf);

                    if(r == 0) {
                        break;
                    }
                }

                close(pipefds[0]);
                pipefds[0] = -1;

                if(exist_globalout_append && vector_size(gGlobalPipes) >= 1) {
                    string_obj* last_item = vector_item(gGlobalPipes, vector_size(gGlobalPipes)-1);
                    string_push_back(last_item, string_c_str(gpipe));
                }
                else {
                    vector_add(gGlobalPipes, gpipe);
                }
            }
            else {
                if(exist_globalout_append && vector_size(gGlobalPipes) >= 1) {
                    string_obj* last_item = vector_item(gGlobalPipes, vector_size(gGlobalPipes)-1);
                    string_push_back(last_item, nextout->mBuffer);
                    *nextout->mBuffer = 0;
                }
                else {
                    vector_add(gGlobalPipes, STRING_NEW(nextout->mBuffer));
                    *nextout->mBuffer = 0;
                }
            }
        }
        else if(exist_globalout == 8) {
            if(command->mKind == kCommand) {
                string_obj* gpipe = STRING_NEW("");

                char buf[BUFSIZ+1];
                while(1) {
                    int r = read(pipefds[0], buf, BUFSIZ);
                    if(r < 0) {
                        perror("read gpipe");
                        exit(1);
                    }
                    buf[r] = 0;

                    string_push_back(gpipe, buf);

                    if(r == 0) {
                        break;
                    }
                }

                close(pipefds[0]);
                pipefds[0] = -1;

                if(exist_globalout_append && vector_size(gQGlobalPipes) >= 1) {
                    string_obj* last_item = vector_item(gQGlobalPipes, vector_size(gQGlobalPipes)-1);
                    string_push_back(last_item, string_c_str(gpipe));
                }
                else {
                    vector_add(gQGlobalPipes, gpipe);
                }
            }
            else {
                if(exist_globalout_append && vector_size(gQGlobalPipes) >= 1) {
                    string_obj* last_item = vector_item(gQGlobalPipes, vector_size(gQGlobalPipes)-1);
                    string_push_back(last_item, nextout->mBuffer);
                    *nextout->mBuffer = 0;
                }
                else {
                    vector_add(gQGlobalPipes, STRING_NEW(nextout->mBuffer));
                    *nextout->mBuffer = 0;
                }
            }
        }
        else if(exist_globalout == 4) {
            if(command->mKind == kCommand) {
                string_obj* gpipe = STRING_NEW("");

                char buf[BUFSIZ+1];
                while(1) {
                    int r = read(pipefds[0], buf, BUFSIZ);
                    if(r < 0) {
                        perror("read gpipe");
                        exit(1);
                    }
                    buf[r] = 0;

                    string_push_back(gpipe, buf);

                    if(r == 0) {
                        break;
                    }
                }

                close(pipefds[0]);
                pipefds[0] = -1;
                int num = statment->mGlobalPipeOutNum;
                if(exist_globalout_append) {
                    string_push_back(gGlobalPipeNum[num], string_c_str(gpipe));
                }
                else {
                    string_put(gGlobalPipeNum[num], string_c_str(gpipe));
                }
                string_delete(gpipe);
            }
            else {
                int num = statment->mGlobalPipeOutNum;
                if(exist_globalout_append) {
                    string_push_back(gGlobalPipeNum[num], nextout->mBuffer);
                }
                else {
                    string_put(gGlobalPipeNum[num], nextout->mBuffer);
                }
                *nextout->mBuffer = 0;
            }
        }

        /// 次の入力先を決める
        if(nextout->mFd == -1) {
            if(!last_program) {
                if(nextin == pipein) {
                    nextin = sRFd_new(-1);
                }
                else {
                    sRFd_delete(nextin);
                    nextin = sRFd_new(-1);
                }
                string_put(nextin->mBuffer, nextout->mBuffer);
                *nextout->mBuffer = 0;
            }
        }
        else {
            if(nextin != pipein) {
                sRFd_delete(nextin);
            }
            nextin = sRFd_new(pipefds[0]);
            pipefds[0] = -1;
        }

        for(ii=0; ii<vector_size(argv); ii++) {
            string_delete(vector_item(argv, ii));
        }
        vector_delete(argv);
        vector_delete(blocks);
        if(nextout != pipeout) {
            sWFd_delete(nextout);
        }
    }

    if(nextin != pipein) {
        sRFd_delete(nextin);
    }

    return TRUE;
}

// 消えた子供を回収したい関数
void run_wait_cprogs(vector_obj* cprogs, sStatment* statment, BOOL last_command_is_inner, int* rcode, int pgroup, BOOL err_exist, sWFd* pipeout, int last_program_pid)
{
    if(vector_size(cprogs) > 0) {   // プロセスがあった
        /// - バックグラウンド - ///
        if((gAppType == kATCursesApp || gAppType == kATConsoleApp) 
            &&  statment->mBackground) 
        {
            sJob* job = sJob_new();

            job->mPGroup = pgroup;
            sChildProgram* cprog = vector_item(cprogs, 0);
            string_put(job->mName, string_c_str(cprog->mTitle));

            int k;
            for(k=0; k<vector_size(cprogs); k++) {
                sChildProgram* cprog = vector_item(cprogs, k);
                vector_add(job->mPIDs, (void*)cprog->mPid);
                sChildProgram_delete(cprog);
            }

            vector_add(gJobs, job);
        }

        /// - フォアグラウンド - ///
        else {
            /// saphireがSIGINTを食らっていたら
            /// 子プロセス全部にSIGINTを送っておく
            /// (前面にsaphireがあったら子プロセスにはSIGINT
            //  が送られないため)
            if(err_exist) {
                int k;
                for(k=0; k<vector_size(cprogs); k++) {
                    sChildProgram* cprog = vector_item(cprogs, k);

                    kill(cprog->mPid, SIGTERM);
                    usleep(10000);
                    if(kill(cprog->mPid, 0) != ESRCH) {
                        kill(cprog->mPid, SIGKILL);
                    }
                }
            }

            sigchld_block(0);       // ブロック解除
WAIT_START:
            while(vector_size(cprogs) > 0) {
                pid_t pid = -1;
                int status = 0;

                /// 迷子がcprogsの中に見つかった？
                int l;
                for(l=0; l<vector_size(gStrayCProgs); l++) {
                    sStrayCProg* other_cprog = vector_item(gStrayCProgs, l);
                    int k;
                    for(k=0; k<vector_size(cprogs); k++) {
                        sChildProgram* cprog = vector_item(cprogs, k);
                        
                        if(cprog->mPid == other_cprog->mPid) {
                            pid = cprog->mPid;
                            status = other_cprog->mStatus;

                            sChildProgram_delete(cprog);
                            sStrayCProg_delete(other_cprog);
                            
                            vector_erase(gStrayCProgs, l);
                            vector_erase(cprogs, k);
                            break;
                        }
                    }
                }

                /// 見当たらないならwait
                /// 見当たったら、迷子の後処理をする
                if(pid == -1) {
                    pid = waitpid(-1, &status, WUNTRACED);
                    /*
                    int k;
                    for(k=0; k<vector_size(cprogs); k++) {
                        sChildProgram* cprog = vector_item(cprogs, k);

                        pid_t ret = waitpid(cprog->mPid, &status, WUNTRACED);
                        if(ret >= 0) {
                            pid = ret;
                        }
                    }
                    */
                }

                /// saphireがSIGINTを食らっていたら
                /// 子プロセス全部にSIGINTを送っておく
                /// (前面にsaphireがあったら子プロセスにはSIGINT
                //  が送られないため)
                if(gKitutukiSigInt) {
                    gKitutukiSigInt = FALSE;
                    int k;
                    for(k=0; k<vector_size(cprogs); k++) {
                        sChildProgram* cprog = vector_item(cprogs, k);

                        kill(cprog->mPid, SIGTERM);
                        usleep(10000);
                        if(kill(cprog->mPid, 0) != ESRCH) {
                            kill(cprog->mPid, SIGKILL);
                        }
                    }
                }

                if(pid < 0) {
                    switch(errno) {
                        case EINTR:
                            goto WAIT_START;

                        case ECHILD:
                            goto WAIT_END;

                        case EINVAL: 
                            goto WAIT_END;

                        default:
                            goto WAIT_END;
                    }
                }
                else if(pid == 0) {
                    goto WAIT_START;
                }

                /// -c オプションで起動したときにCTRL-Z食らって
                // fgされたときのための処理
                // CTRL-Zされたときに寝てしまったときの状態変化
                // を無視したい
                static vector_obj* ignore_tstp = NULL;
                if(gKitutukiSigCont) {
                    gKitutukiSigCont = FALSE;
                    if(ignore_tstp == NULL) {
                        ignore_tstp = VECTOR_NEW(10);
                    }

                    int k;
                    for(k=0; k<vector_size(cprogs); k++) {
                        sChildProgram* cprog = vector_item(cprogs, k);

                        vector_add(ignore_tstp, (void*)cprog->mPid);
                    }
                }

                if(ignore_tstp && vector_size(ignore_tstp) > 0) {
                    int k;
                    for(k=0; k<vector_size(ignore_tstp); k++) {
                        int ipid = (int)vector_item(ignore_tstp, k);

                        if(pid == ipid && WIFSTOPPED(status)) {
                            vector_erase(ignore_tstp, k);
                            if(vector_size(ignore_tstp) == 0) {
                                vector_delete(ignore_tstp);
                                ignore_tstp = NULL;
                            }
                            goto WAIT_START;
                        }
                    }
                }

                /// cprogsの中にwaitされたプログラムはあるか？
                int k;
                sChildProgram* prog = NULL;
                for(k=0; k<vector_size(cprogs); k++) {
                    sChildProgram* p = (sChildProgram*) vector_item(cprogs, k);
                    
                    if(p->mPid == pid) {
                        prog = p;
                        break;
                    }
                }
                
                /// 無いなら迷子なので格納しておく ///
                if(prog == NULL) {
                    vector_add(gStrayCProgs
                        , sStrayCprog_new(pid, status, pgroup));
                }
                /// シグナルの配送により終了した場合 ///
                else if(WIFSIGNALED(status)) {
                    if(!prog->mWriterProcess && last_command_is_inner == FALSE) {
                        if(last_program_pid == pid) {
                            *rcode = WTERMSIG(status) + 128;

                            /// 終了コードの反転 ///
                            if(statment->mRCodeReverse) {
                                if(*rcode == 0) {
                                    *rcode = 1;
                                }
                                else {
                                    *rcode = 0;
                                }
                            }

                            char buf[256];
                            snprintf(buf, 256, "%d", *rcode);
                            saphire_set_local_var("RCODE", buf);
                        }
                    }

                    sChildProgram_delete(prog);
                    vector_erase(cprogs, k);
                }
                /// 終了した場合 ///
                else if(WIFEXITED(status)) {
                    if(!prog->mWriterProcess 
                        && last_command_is_inner == FALSE
                        && *rcode != SIGINT + 128) 
                    {
                        if(last_program_pid == pid) {
                            *rcode = WEXITSTATUS(status);

                            /// 終了コードの反転 ///
                            if(statment->mRCodeReverse) {
                                if(*rcode == 0) {
                                    *rcode = 1;
                                }
                                else {
                                    *rcode = 0;
                                }
                            }

                            char buf[256];
                            snprintf(buf, 256, "%d", *rcode);
                            saphire_set_local_var("RCODE", buf);
                        }
                    }
                    sChildProgram_delete(prog);
                    vector_erase(cprogs, k);
                }
                /// シグナルの配送により停止した場合 ///
                else if(WIFSTOPPED(status)) {
                    int sig = WSTOPSIG(status) + 128;

                    if(!prog->mWriterProcess) {
                        if(last_program_pid == pid) {
                            *rcode = sig;

                            /// 終了コードの反転 ///
                            if(statment->mRCodeReverse) {
                                if(*rcode == 0) {
                                    *rcode = 1;
                                }
                                else {
                                    *rcode = 0;
                                }
                            }

                            char buf[256];
                            snprintf(buf, 256, "%d", *rcode);
                            saphire_set_local_var("RCODE", buf);
                        }
                    }

                    pgroup = getpgid(pid);

                    BOOL same_pgroup_exist = FALSE;
                    int l;
                    const int max = vector_size(gJobs);
                    for(l=0; l<max; l++) {
                        sJob* job = vector_item(gJobs, l);
                        if(job->mPGroup == pgroup) {
                            same_pgroup_exist = TRUE;
                        }
                    }

                    if(!same_pgroup_exist) {
                        sJob* job = sJob_new();

                        job->mPGroup = pgroup; 
                        string_put(job->mName, string_c_str(prog->mTitle));
                        vector_add(job->mPIDs, (void*)prog->mPid);
                        vector_add(gJobs, job);

                        if(gAppType == kATCursesApp 
                            || gAppType == kATConsoleApp)
                        {
                            job->mTty = MALLOC(sizeof(struct termios));
                            tcgetattr(STDIN_FILENO, job->mTty);
                        }
                    }
                
                    sChildProgram_delete(prog);
                    vector_erase(cprogs, k);
                }
            }

WAIT_END:
            if((gAppType == kATCursesApp || gAppType == kATConsoleApp)
                && pipeout->mFd == STDOUT_FILENO) 
            {
                //mreset_tty();
                /// saphireを端末の前面に出す ///
                if(tcsetpgrp(0, getpid()) < 0) {
                    perror("tcsetpgrp(saphire)");
                    exit(1);
                }
            }
        }
    }
}

// 複文を実行したい関数。本体。
/// pipeout: プログラムの出力先
/// pipein: プログラムの入力先
/// finaly_exec: パイプの最後でプログラムを実行したとき
/// forkせずにexecするかどうか
// -1が返ったらエラー処理する必要がある
int run(sStatments* statments, char* title, sWFd* pipeout, sRFd* pipein, int pipeerr, BOOL finaly_exec, BOOL in_loop, BOOL* return_, vector_obj* parent_blocks, BOOL try_)
{
    int rcode = 0;

    int i;
    for(i=0; i<vector_size(statments->mStatments); i++) {
        sStatment* statment = vector_item(statments->mStatments, i);

        int line = statment->mLine;
        char* sname = string_c_str(statment->mFName);

        if(statment->mNotEvaled) {
            string_obj* cmdline = STRING_NEW("");
            string_obj* cmdline2 = STRING_NEW("");

            int r = 1;
            string_put(cmdline, string_c_str(statment->mNotEvaled));

            if(r) {
                if(expand_env(string_c_str(cmdline), cmdline2, pipein, sname, &line)) {
                    sStatments* estatments = STATMENTS_NEW();

                    if(parse(string_c_str(cmdline2), sname, &line, estatments)) 
                    {
                        rcode = run(estatments, title, pipeout, pipein
                                , pipeerr, FALSE, in_loop, return_, parent_blocks, FALSE);
                        if(*return_) {
                            sStatments_delete(estatments);

                            string_delete(cmdline);
                            string_delete(cmdline2);
                            return rcode;
                        }

                        if(rcode < 0) {
                            sStatments_delete(estatments);

                            string_delete(cmdline);
                            string_delete(cmdline2);
                            return rcode;
                        }
                    }
                    else {
                        sStatments_delete(estatments);

                        string_delete(cmdline);
                        string_delete(cmdline2);
                        return -1;
                    }

                    sStatments_delete(estatments);
                }
                else {
                    string_delete(cmdline);
                    string_delete(cmdline2);
                    return -1;
                }
            }
            else {
                string_delete(cmdline);
                string_delete(cmdline2);
                return -1;
            }

            string_delete(cmdline);
            string_delete(cmdline2);
        }
        else {
            sigchld_block(1);

            vector_obj* cprogs = VECTOR_NEW(30);

            pid_t pgroup = -1;
            BOOL last_command_is_inner;
            int last_program_pid = -1;

            int r = statment_tree(statment, &last_command_is_inner
                          , &last_program_pid
                          , pipeout, pipein, pipeerr
                          , title, &rcode, &pgroup, cprogs
                          , finaly_exec 
                                && i==vector_size(statments->mStatments)-1
                          , in_loop, return_, parent_blocks , sname, line);


            /// プロセス回収 ///
            run_wait_cprogs(cprogs, statment, last_command_is_inner
                , &rcode, pgroup, r == FALSE, pipeout, last_program_pid);

            vector_delete(cprogs);

            /// ランタイムエラー ///
            if(r == FALSE) {
                return -1;
            }
            // reutrn
            if(*return_) {
                return rcode;
            }
        }

        /// 行末の処理 ///
        if(gSigUser) {
            gSigUser = FALSE;
            err_msg("command not found", sname, line);
            return -1;
        }
        else if(rcode == 128+SIGINT || gKitutukiSigInt) {
            err_msg("signal interrupt!", sname, line);
            gKitutukiSigInt = FALSE;
            return -1;
        }
        else if(rcode == SIGUSR2) {
            return rcode;
        }
        else if(statment->mTerminated == kTOrOr && rcode == 0) {
            while(i<vector_size(statments->mStatments) 
                    && statment->mTerminated != kTNormal) 
            {
                i++;
                statment = vector_item(statments->mStatments, i);
            }
        }
        else if(statment->mTerminated == kTAndAnd && rcode != 0) {
            while(i<vector_size(statments->mStatments) 
                    && statment->mTerminated != kTNormal) 
            {
                i++;
                statment = vector_item(statments->mStatments, i);
            }
        }
        else if(try_ && rcode != 0) {
            return -1;
        }
    }

    /// 空文の場合 ///
    if(vector_size(statments->mStatments) == 0) {
        /// CTRL-Cが押された ///
        if(gKitutukiSigInt) {
            gKitutukiSigInt = FALSE;
            return SIGINT + 128;
        }
        /*
        else {
            err_msg("command is empty", sname, line);
            return -1;           // 空文は偽
        }
        */
    }

    return rcode;
}

///////////////////////////////////////////////////
// ファイル読み込み
///////////////////////////////////////////////////
// -1ならエラーメッセージがgErrMsgに入っているので出力してください

int saphire_load(char* fname, sWFd* pipeout, sRFd* pipein, int pipeerr)
{
    string_obj* str = STRING_NEW("");

    int f = open(fname, O_RDONLY);
    if(f < 0) {
        string_delete(str);
        return -1;
    }

    char buf[BUFSIZ];
    while(1) {
        int size = read(f, buf, BUFSIZ-1);
        if(size == 0) {
            break;
        }
        if(size < 0) {
            string_delete(str);
            close(f);
            return -1;
        }

        buf[size] = 0;

        string_push_back(str, buf);
    }
    close(f);

    int rcode = saphire_shell(string_c_str(str), fname, pipeout, pipein, pipeerr);

    string_delete(str);

    return rcode;
}


///////////////////////////////////////////////////
// シェル実行
///////////////////////////////////////////////////

// -1ならエラーメッセージがgErrMsgに入っているので出力してください
int saphire_shell(char* command, char* fname, sWFd* pipeout, sRFd* pipein, int pipeerr)
{
    gKitutukiSigInt = FALSE;

    string_obj* fname2;
    if(fname == NULL) {
        fname2 = STRING_NEW(command);
    }
    else {
        fname2 = STRING_NEW(fname);
    }

    int line = 1;
    string_obj* str = STRING_NEW("");
    if(!read_expand_brace_expansion(command, str, " ", string_c_str(fname2), &line)) {
        string_delete(str);
        string_delete(fname2);
        return -1;
    }

    line = 1;

    sStatments* statments = STATMENTS_NEW();
    if(!parse(string_c_str(str), string_c_str(fname2), &line, statments)) {
        sStatments_delete(statments);
        string_delete(str);
        string_delete(fname2);
        return -1;
    }
    else {
        /// 実行 ///
        int return_ = FALSE;
        int rcode = run(statments, string_c_str(fname2)
                    , pipeout, pipein, pipeerr, gAppType == kATOptC
                    , FALSE, &return_, NULL, FALSE);

        /// 解放 ///
        string_delete(str);
        string_delete(fname2);
        sStatments_delete(statments);

        return rcode;
    }
}

// コマンド（文字列）を実行して結果をresultに入れたい関数
// -1ならエラーメッセージがgErrMsgに入っているので出力してください
int saphire_shell3(string_obj* result, char* command, char* title, sRFd* pipein)
{
    int pipefds[2];

    if(pipe(pipefds) < 0) {
        perror("pipe");
        exit(1);
    }

    sWFd* pipeout = sWFd_new(pipefds[1]);
    int rcode = saphire_shell(command, title, pipeout, pipein, STDERR_FILENO);
    if(!sWFd_close(pipeout)) {
        return -1;
    }
    sWFd_delete(pipeout);

    if(rcode < 0) {
        if(!xclose(pipefds[0])) { return -1; }
        return -1;
    }

    char buf[BUFSIZ];
    while(2) {
        int size = read(pipefds[0], buf, BUFSIZ-1);
        if(size == 0) {
            break;
        }
        if(size < 0) {
            fprintf(stderr, "expand command err(read)");
            close(pipefds[0]);
            return -1;
        }

        buf[size] = 0;

        string_push_back(result, buf);
    }
    if(!xclose(pipefds[0])) {
        return -1;
    }

    return rcode;
}

///////////////////////////////////////////////////
// ジョブの数
///////////////////////////////////////////////////
int saphire_job_num()
{
    return vector_size(gJobs);
}

///////////////////////////////////////////////////
// ジョブのタイトルを返す
///////////////////////////////////////////////////
char* saphire_job_title(int num)
{
    if(num>=0 && num < vector_size(gJobs)) {
        sJob* job = (sJob*)vector_item(gJobs, num);
        return string_c_str(job->mName);
    }
    else {
        return NULL;
    }
}

///////////////////////////////////////////////////
// 全てのジョブをkillする
///////////////////////////////////////////////////
void saphire_kill_all_jobs()
{
    int i;
    for(i=0; i<vector_size(gJobs); i++) {
        sJob* job = (sJob*)vector_item(gJobs, i);

        int j;
        for(j=0; j<vector_size(job->mPIDs); j++) {
            int pid = (int)vector_item(job->mPIDs, j);
            kill(pid, SIGKILL);
        }

        sJob_delete(job);
    }

    vector_clear(gJobs);
}

///////////////////////////////////////////////////
// ジョブを消す
///////////////////////////////////////////////////
void saphire_kill_job(int num)
{
    if(num>=0 && num < vector_size(gJobs)) {
        sJob* job = (sJob*)vector_item(gJobs, num);

        int i;
        for(i=0; i<vector_size(job->mPIDs); i++) {
            int pid = (int)vector_item(job->mPIDs, i);
            kill(pid, SIGKILL);
        }

        sJob_delete(job);

        vector_erase(gJobs, num);
    }
}

///////////////////////////////////////////////////
// ジョブを前面に出す
///////////////////////////////////////////////////
void (*saphire_job_done)(int job_num, char* job_title) = NULL;

BOOL forground_job(int num)
{
    if(num>=0 && num < vector_size(gJobs)) {
        sJob* job = (sJob*)vector_item(gJobs, num);

        if(job->mTty) tcsetattr(STDIN_FILENO, TCSANOW, job->mTty);
        if(tcsetpgrp(0, job->mPGroup) < 0) {
            err_msg("tcsetpgrp failed. use ps fj to find out job process group and use cpg [job id] [pgroup] to change the job process group which saphire has.", "fg", 1);
            return FALSE;
        }
        
        if(killpg(job->mPGroup, SIGCONT) < 0) {
            int i;
            for(i=0; i<vector_size(job->mPIDs); i++) {
                int pid = (int)vector_item(job->mPIDs, i);
                kill(pid, SIGCONT);
            }
        }

        /// wait ///
        int return_code = 0;

        while(vector_size(job->mPIDs) > 0) {
            pid_t pid = (pid_t)vector_item(job->mPIDs, 0);

            int status = 0;
            pid_t return_pid = waitpid(pid, &status, WUNTRACED);

            /// 終了した場合 ///
            if(WIFEXITED(status)) {
                vector_erase(job->mPIDs, 0);

                if(vector_size(job->mPIDs) == 0) {
                    if(saphire_job_done) 
                        saphire_job_done(num, string_c_str(job->mName));

                    sJob_delete(job);
                    vector_erase(gJobs, num);

                    /// saphireを前面に出す ///
                    //mreset_tty();
                    if(tcsetpgrp(0, getpid()) < 0) {
                        perror("tcsetpgrp");
                        exit(1);
                    }

                    return_code = WEXITSTATUS(status);
                    char buf[256];
                    snprintf(buf, 256, "%d", return_code);
                    saphire_set_local_var("RCODE", buf);
                    break;
                }
            }
            /// シグナルの配送により終了した場合 ///
            else if(WIFSIGNALED(status)) {
                vector_erase(job->mPIDs, 0);

                if(vector_size(job->mPIDs) == 0) {
                    if(saphire_job_done) 
                        saphire_job_done(num, string_c_str(job->mName));

                    sJob_delete(job);
                    vector_erase(gJobs, num);

                    /// saphireを前面に出す ///
                    //mreset_tty();
                    if(tcsetpgrp(0, getpid()) < 0) {
                        perror("tcsetpgrp");
                        exit(1);
                    }

                    return_code = WTERMSIG(status) + 128;
                    char buf[256];
                    snprintf(buf, 256, "%d", return_code);
                    saphire_set_local_var("RCODE", buf);
                    //gKitutukiSigInt = TRUE; // どのシグナルでも止めたい
                    break;
                }
            }
            /// シグナルの配送によりフォワグランドジョブが停止した場合 ///
            else if(WIFSTOPPED(status)) {
                if(!job->mTty) {
                    job->mTty = MALLOC(sizeof(struct termios));
                    tcgetattr(STDIN_FILENO, job->mTty);
                }

                /// saphire を前面に出す ///
                //mreset_tty();
                if(tcsetpgrp(0, getpid()) < 0) {
                    perror("tcsetpgrp");
                    exit(1);
                }
                break;
            }
        }

        return TRUE;
    }

    err_msg("invalid job number", "fg", 1);
    return FALSE;
}

///////////////////////////////////////////////////
// ジョブにSIGCONTする
///////////////////////////////////////////////////
void background_job(int num)
{
    if(num>=0 && num < vector_size(gJobs)) {
        sJob* job = (sJob*)vector_item(gJobs, num);
        
        if(killpg(job->mPGroup, SIGCONT) < 0) {
            int pid = (int)vector_item(job->mPIDs, 0);
            kill(pid, SIGCONT);
        }
    }
}

///////////////////////////////////////////////////
// バックグラウンドジョブが終了していれば後処理をする
///////////////////////////////////////////////////
void saphire_wait_background_job()
{
    int i;
while_start:
    for(i=0; i<vector_size(gJobs); i++) {
        sJob* job = (sJob*)vector_item(gJobs, i);
        int j;
        for(j=0; j<vector_size(job->mPIDs); j++) {
            pid_t pid = (pid_t)vector_item(job->mPIDs, j);

            int status;
            pid_t return_pid = waitpid(pid, &status, WNOHANG);

            if(return_pid == pid || return_pid < 0 && errno == ECHILD) {
                vector_erase(job->mPIDs, j);

                if(vector_size(job->mPIDs) == 0) {
                    if(saphire_job_done) 
                        saphire_job_done(i, string_c_str(job->mName));

                    sJob_delete(job);
                    vector_erase(gJobs, i);

                    goto while_start;
                }
            }
        }
    }
}

///////////////////////////////////////////////////////
// 内部コマンドの登録
///////////////////////////////////////////////////////
void saphire_add_inner_command(char* name, fInnerCommand fun)
{
    hash_put(gInnerCommands, name, sInnerCommand_new(name, fun));
}

// コマンド（文字列）をコンパイルしてstatmentsに格納したい関数
// エラー時はgErrMsgを出力する
BOOL saphire_compile2(char* cmdline, char* fname, sStatments* statments)
{
    int line = 1;
    if(!parse(cmdline, fname, &line, statments)) {
        return FALSE;
    }
    else {
        return TRUE;
    }
}

// compile2で格納されたstatmentsを実行したい関数
int saphire_run(sStatments* statments, char* title, sWFd* pipeout, sRFd* pipein, int pipeerr)
{
    int return_ = FALSE;
    int r = run(statments, title, pipeout, pipein, pipeerr
            , gAppType == kATOptC, FALSE, &return_, NULL, FALSE);

    return r;
}

void saphire_sweep()
{
    tmpfile_sweep();
}

/// ごみ掃除(GCはないけど)したい関数
void tmpfile_sweep()
{
    /// プロセス置換で使った一時ファイルを掃除 ///
    int k;
    for(k=0; k<vector_size(gPSubTmpFiles); k++) {
        string_obj* fname = vector_item(gPSubTmpFiles, k);

        (void)unlink(string_c_str(fname));

        string_delete(fname);
    }
    vector_clear(gPSubTmpFiles);
}

/// ごみ掃除(GCはないけど)したい関数
void tmpfile_all_sweep()
{
    DIR* dir = opendir(gTmpDir);
    if(dir) {
        struct dirent* entry;
        while(entry = readdir(dir)) {
            char fname[PATH_MAX];
            snprintf(fname, PATH_MAX, "%s/%s", gTmpDir, entry->d_name);

            (void)unlink(fname);
        }
    }
    closedir(dir);
}
