#include "common.h"
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <signal.h>
#include <errno.h>
#include <sys/shm.h>

vector_obj* gJobs;      // vector of sJob
sJob* gForgroundJob = NULL;    // フォアグランドジョブ

static int wait_forground_job();       // フォアグランドジョブの終了を待つ

///////////////////////////////////////////////////////////////////////////////
// パーサー
///////////////////////////////////////////////////////////////////////////////
enum eRedirect { kRedirectInput, kRedirectOverwrite, kRedirectAppend };

struct sRedirect {
    enum eRedirect mType;
    int mFd;
    char mFName[1024];
};

struct sStatment {
    vector_obj* argvs;
    vector_obj* redirections_list;
    bool background;
    
    sStatment() {
        argvs = vector_new(10);
        redirections_list = vector_new(10);
        background = false;
    }
    
    ~sStatment() {
        for(int i=0; i<vector_size(redirections_list); i++) {
            vector_obj* redirections = (vector_obj*)vector_item(redirections_list, i);
            
            for(int j=0; j<vector_size(redirections); j++) {
                delete (sRedirect*)vector_item(redirections, j);
            }
            
            vector_delete(redirections);
        }
        vector_delete(redirections_list);
        
        for(int i=0; i<vector_size(argvs); i++) {
            vector_obj* argv = (vector_obj*)vector_item(argvs, i);
            
            for(int j=0; j<vector_size(argv); j++) {
                string_delete((string_obj*)vector_item(argv, j));
            }
            
            vector_delete(argv);
        }
        vector_delete(argvs);
    }
};

static bool expand_env(char* p, char* p2)
{
    bool squote = false;
    bool dquote = false;
    
    while(*p) {
        if(!dquote && *p == '\'') {
            *p2++ = *p++;
            squote = !squote;
        }
        else if(!squote && *p == '"') {
            *p2++ = *p++;
            dquote = !dquote;
        }
        else if(!squote && *p == '$' && *(p+1) != '(') {
            p++;
            
            char name[1024];
            char* p3 = name;
            while(*p>='a' && *p<='z' || *p>='A' && *p<='Z' || *p=='_' || *p>='0' && *p<='9') {
                *p3++ = *p++;
            }
            *p3 = 0;
            
            char* env = getenv(name);
            
            if(env) {
                p3 = env;
                while(*p3) {
                    *p2++ = *p3++;
                }
            }
        }
        else if(squote || dquote) {
            *p2++ = *p++;
        }
        else if(*p == '\\') {
            *p2++ = *p++;

            if(*p) *p2++ = *p++;
        }
        else if(*p == '$' && *(p+1) == '(') {
            p++;
            p++;
            char cmd[4096];

            char* p3 = cmd;
            *p3++ = '`';
            while(*p != ')') {
                if(*p == 0) {
                    gErrMsgCancel = false;
                    err_msg("expected `");
                    return false;
                }
                *p3++ = *p++;
            }
            sprintf(p3, "`.chomp");
            p++;
            int error;
            VALUE value = rb_eval_string_protect(cmd, &error);
            if(error) {
                switch(error) {
                case TAG_RETURN:
                    fprintf(stderr, "unexpected return\n");
                    break;
                    
                case TAG_NEXT:
                    fprintf(stderr, "unexpected next\n");
                    break;
                    
                case TAG_BREAK:
                    fprintf(stderr, "unexpected break\n");
                    break;
                    
                case TAG_REDO:
                    fprintf(stderr, "unexpected redo\n");
                    break;
                    
                case TAG_RETRY:
                    fprintf(stderr, "retry outside of rescue clause\n");
                    break;
                    
                case TAG_RAISE:
                case TAG_FATAL:
                    fprintf(stderr, "%s: %s\n",
                        rb_class2name(CLASS_OF(ruby_errinfo)),
                        RSTRING(rb_obj_as_string(ruby_errinfo))->ptr);
                    break;
                
                default:
                    fprintf(stderr, "unknown longjmp status %d\n", error);
                    break;
                }
            }
            else {
                Check_Type(value, T_STRING);
                char* p4 = RSTRING(value)->ptr;
                if(p4 == NULL) {
                    return false;
                }
                else {
                    while(*p4) {
                        *p2++ = *p4++;
                    }
                }
            }
        }
        else if(*p == '`') {
            p++;
            char cmd[4096];

            char* p3 = cmd;
            *p3++ = '`';
            while(*p != '`') {
                if(*p == 0) {
                    gErrMsgCancel = false;
                    err_msg("expected `");
                    return false;
                }
                *p3++ = *p++;
            }
            sprintf(p3, "`.chomp");
            p++;
            int error;
            VALUE value = rb_eval_string_protect(cmd, &error);
            if(error) {
                switch(error) {
                case TAG_RETURN:
                    fprintf(stderr, "unexpected return\n");
                    break;
                    
                case TAG_NEXT:
                    fprintf(stderr, "unexpected next\n");
                    break;
                    
                case TAG_BREAK:
                    fprintf(stderr, "unexpected break\n");
                    break;
                    
                case TAG_REDO:
                    fprintf(stderr, "unexpected redo\n");
                    break;
                    
                case TAG_RETRY:
                    fprintf(stderr, "retry outside of rescue clause\n");
                    break;
                    
                case TAG_RAISE:
                case TAG_FATAL:
                    fprintf(stderr, "%s: %s\n",
                        rb_class2name(CLASS_OF(ruby_errinfo)),
                        RSTRING(rb_obj_as_string(ruby_errinfo))->ptr);
                    break;
                
                default:
                    fprintf(stderr, "unknown longjmp status %d\n", error);
                    break;
                }
            }
            else {
                Check_Type(value, T_STRING);
                char* p4 = RSTRING(value)->ptr;
                if(p4 == NULL) {
                    return false;
                }
                else {
                    while(*p4) {
                        *p2++ = *p4++;
                    }
                }
            }
        }
        else {
            *p2++ = *p++;
        }
    }
    
    *p2 = 0;
    
    return true;
}

static bool parse(char* p, sStatment* statment)
{
    while(*p == ' ' || *p == '\t') {
        p++;
    }
    
    vector_obj* argv = vector_new(10);
    vector_obj* redirections = vector_new(10);

    char buf[8096*2*2*2*2];
    char* p2 = buf;
    bool squote = false;
    bool dquote = false;
    bool next_is_rfname = false;
    sRedirect* redirection;
    
    while(*p) {
        if(!dquote && *p == '\'') {
            p++;
            squote = !squote;
        }
        else if(!squote && *p == '"') {
            p++;
            dquote = !dquote;
        }
        else if(squote || dquote) {
            *p2++ = *p++;
        }
        else if(*p == '\\') {
            p++;

            if(*p) *p2++ = *p++;
        }
        else if(*p == '~' && (*(p+1) == 0 || *(p+1) == ' ' || *(p+1) == '/')) {
            p++;
            
            char* home = getenv("HOME");
            if(home == NULL) {
                fprintf(stderr, "$HOME is null");
                exit(1);
            }
            
            char* p3 = home;
            while(*p3) {
                *p2++ = *p3++;
            }
        }
        else if(*p == '*' || *p == '?' || *p == '[' || *p == '{') {
            while(*p != ' ' && *p != '\t' && *p) {
                *p2++ = *p++;
            }
            *p2 = 0;

            VALUE result = rb_funcall(rb_cObject, rb_intern("mfiler2_extend_glob"), 2, rb_str_new2(ActiveDir()->Path()), rb_str_new2(buf));
            
            for(int i= 0; i<RARRAY(result)->len; i++) {
                VALUE item = rb_ary_entry(result, i);

                if(strcmp(RSTRING(item)->ptr, "") != 0) {
                    if(next_is_rfname) {
                        next_is_rfname = false;
                        strcpy(redirection->mFName, RSTRING(item)->ptr);
                    }
                    else {
                        vector_add(argv, string_new(RSTRING(item)->ptr));
                    }
                }
            }

            p2 = buf;
        }
        else if(*p == ' ' || *p == '\t') {
            while(*p == ' ' || *p == '\t') {
                p++;
            }

            *p2 = 0;
            if(strcmp(buf, "") != 0) {
                if(next_is_rfname) {
                    next_is_rfname = false;
                    strcpy(redirection->mFName, buf);
                }
                else
                    vector_add(argv, string_new(buf));
            }
            p2 = buf;
        }
        else if(*p == '&') {
            p++;
            statment->background = true;

            *p2 = 0;
            if(strcmp(buf, "") != 0) {
                if(next_is_rfname) {
                    next_is_rfname = false;
                    strcpy(redirection->mFName, buf);
                }
                else
                    vector_add(argv, string_new(buf));
            }
            p2 = buf;
        }
        else if(*p == '|') {
            p++;
            
            while(*p == ' ' || *p == '\t') {
                p++;
            }

            *p2 = 0;
            if(strcmp(buf, "") != 0) {
                if(next_is_rfname) {
                    next_is_rfname = false;
                    strcpy(redirection->mFName, buf);
                }
                else
                    vector_add(argv, string_new(buf));
            }
            p2 = buf;
            
            if(vector_size(argv) == 0) return false;
            vector_add(statment->argvs, argv);
            argv = vector_new(10);
            
            if(next_is_rfname) return false;
            vector_add(statment->redirections_list, redirections);
            redirections = vector_new(10);
        }
        else if( (*p == '1' && *(p+1) == '>') 
                    || (*p == '1' && *(p+1) == '>' && *(p+2) == '>') 
                    || (*p == '2' && *(p+1) == '>') 
                    || (*p == '2' && *(p+1) == '>' && *(p+2) == '>') 
                    || (*p == '>' && *(p+1) == '>')
                    || *p =='>' 
                    || *p =='<'
                    ) 
       {
            *p2 = 0;
            if(strcmp(buf, "") != 0) {
                if(next_is_rfname) {
                    next_is_rfname = false;
                    strcpy(redirection->mFName, buf);
                }
                else
                    vector_add(argv, string_new(buf));
            }
            p2 = buf;
            
            int fd;
            eRedirect type;
            
            if(*p == '1' && *(p+1) == '>') {
                p+=2;
                
                fd = 1;
                type = kRedirectOverwrite;
            }
            else if(*p == '1' && *(p+1) == '>' && *(p+2) == '>') {
                p+=3;
                
                fd = 1;
                type = kRedirectAppend;
            }
            else if(*p == '2' && *(p+1) == '>') {
                p+=2;
                
                fd = 2;
                type = kRedirectOverwrite;
            }
            else if(*p == '2' && *(p+1) == '>' && *(p+2) == '>') {
                p+=3;
                
                fd = 2;
                type = kRedirectAppend;
            }
            else if(*p == '>' && *(p+1) == '>') {
                p+=2;
                
                fd = 1;
                type = kRedirectAppend;
            }
            else if(*p =='>') {
                p++;
                
                fd = 1;
                type = kRedirectOverwrite;
            }
            else {
                p++;
                
                fd = 0;
                type = kRedirectInput;
            }
            
            while(*p == ' ' || *p == '\t') {
                p++;
            }
            
            redirection = new sRedirect;
            
            redirection->mType = type;
            redirection->mFd = fd;
            
            next_is_rfname = true;
            
            vector_add(redirections, redirection);
        }
        else {
            *p2++ = *p++;
        }
    }

    while(*p == ' ' || *p == '\t') {
        p++;
    }
    
    *p2 = 0;
    if(strcmp(buf, "") != 0) {
        if(next_is_rfname) {
            next_is_rfname = false;
            strcpy(redirection->mFName, buf);
        }
        else
            vector_add(argv, string_new(buf));
    }
    
    if(vector_size(argv) == 0) return false;
    vector_add(statment->argvs, argv);
    
    if(next_is_rfname) return false;
    vector_add(statment->redirections_list, redirections);
    
    return true;
}

///////////////////////////////////////////////////////////////////////////////
// シェル
///////////////////////////////////////////////////////////////////////////////
int gSharedMemoryID;

void shell(char* cmdline, bool hitanykey, char* title)
{
    /// 端末のタイトルに名前を付ける ///
    if(gChangeTerminalTitle) {
        printf("%c]2;mfiler2 %s%c", 033, title, 007);
        fflush(stdout);
    }

    /// 実行 ///
    char* pointer = cmdline;

    while(*pointer) {
        sStatment* statment = new sStatment;
        
        /// 環境変数の展開 ///
        char sbuf[8096*2*2*2*2];
        
        char* psbuf = sbuf;
        while(*pointer) {
            if(*pointer == ';') {
                pointer++;
                break;
            }
            else {
                *psbuf++ = *pointer++;
            }
        }
        *psbuf = 0;
        
        char sbuf2[8096*2*2*2*2];
        
        if(!expand_env(sbuf, sbuf2)) {
            gErrMsgCancel = false;
            err_msg("expand env err");
            return;
        }
        
        /// パース ///
        if(!parse(sbuf2, statment)) {
            gErrMsgCancel = false;
            err_msg("parser err");
            return;
        }

#if !defined(__CYGWIN__)
        /// shared memory ///
        gSharedMemoryID = shmget(1234, 1, 0666|IPC_CREAT);
        if(gSharedMemoryID == -1) {
            fprintf(stderr, "shmget failed\n");
            exit(1);
        }
#endif

        /// 実行 ///
        int nextin = 0;
        int nextout = 1;
        
        sJob* job = new sJob(title);
        vector_add(gJobs, job);

        job->mPGroup = -1;
        
        for(int i=0; i<vector_size(statment->argvs); i++) {
            vector_obj* argv = (vector_obj*)vector_item(statment->argvs, i);
            vector_obj* redirections = (vector_obj*)vector_item(statment->redirections_list, i);

            char* cmd = string_c_str((string_obj*)vector_item(argv, 0));

            int pipefds[2];

            if(i == vector_size(statment->argvs)-1) {
                nextout = 1;
            }
            else {
                if(pipe(pipefds) < 0) {
                    perror("pipe");
                }
                nextout = pipefds[1];
            }

            /// 内蔵コマンド ///

            /// read ///
            if(strcmp(cmd, "read") == 0) {
                string_obj* str = string_new("");
                while(1) {
                    char buf[BUFSIZ];
                    int r = read(nextin, buf, BUFSIZ);
                    if(r < 0) {
                        perror("read");
                        goto read_end;
                    }

                    char* p;
                    if(p = strstr(buf, "\n")) {
                        buf[p - buf] = 0;
                        string_push_back(str, buf);
                        break;
                    }
                    else if(r == 0) {
                        string_push_back(str, buf);
                        break;
                    }
                }

                if(vector_size(argv) >= 2) {
                    (void)setenv(string_c_str((string_obj*)vector_item(argv, 1)), string_c_str(str), 1);
                }

read_end:
                if(nextout != 1) close(nextout);

                nextin = pipefds[0];
            }
            /// export ///
            else if(strcmp(cmd, "export") == 0) {
                if(vector_size(argv) != 2) {
                    gErrMsgCancel = false;
                    err_msg("export syntax error");
                }
                else {
                    char* p = string_c_str((string_obj*)vector_item(argv, 1));
                    char* p2 = strstr(p, "=");

                    if(p2) {
                        char var_name[BUFSIZ];
                        char value[BUFSIZ];

                        memcpy(var_name, p, p2 - p);
                        var_name[strstr(p, "=") - p] = 0;
                        strcpy(value, p2 + 1);

                        (void)setenv(var_name, value, 1);
                    }
                }

                if(nextout != 1) close(nextout);

                nextin = pipefds[0];
            }
            else {
#if !defined(__CYGWIN__)
                void* shared_memory = shmat(gSharedMemoryID, (void*)0, 0);
                *(char*)shared_memory = 0;
#endif
            	
                /// fork ///
                pid_t pid = fork();
                if(pid < 0) {
                    perror("fork");
                    exit(1);
                }

                /// 子プロセスの処理 ///
                if(pid == 0) {
                    signal(SIGTTIN, SIG_DFL);
                    signal(SIGTTOU, SIG_DFL);
                    signal(SIGINT, SIG_DFL);
                    signal(SIGQUIT, SIG_DFL);
                    signal(SIGTSTP, SIG_DFL);
                    signal(SIGCHLD, SIG_DFL);
                    signal(SIGWINCH, SIG_DFL);
/*
P((f, "子プロセス pid %d", getpid()));
P((f, "子プロセス ssid %d", tcgetsid(0)));
P((f, "子プロセス pgrp %d\n", tcgetpgrp(0)));
*/
                    if(job->mPGroup == -1) {
                        pid_t pid = getpid();

                        /// 独自のプロセスグループに入るまで待つ ///
                        pid_t pgrp = getpgid(0);
                        while(pgrp != pid) {
                            pgrp = getpgid(0);
                        }

                        // このプロセスグループのプロセスがフォアグランドに出るまで待つ //
                        if(!statment->background) {
                            pid_t pgrp = tcgetpgrp(0);
                            while(pgrp != pid) {
                                pgrp = tcgetpgrp(0);
                            }
                        }
                    }
                    else {
                        /// 独自のプロセスグループに入るまで待つ ///
                        pid_t pgrp = getpgid(0);
                        while(pgrp != job->mPGroup) {
                            pgrp = getpgid(0);
                        }
                    }

/*
P((f, "子プロセス 2 pid %d", getpid()));
P((f, "子プロセス 2 ssid %d", tcgetsid(0)));
P((f, "子プロセス 2 pgrp %d\n", tcgetpgrp(0)));
*/

                    /// パイプ ///
                    if(nextin != 0) {
                        if(dup2(nextin, 0) < 0) {
                            perror("dup2");
                        }
                        close(nextin);
                    }
                    
                    if(nextout != 1) {
                        if(dup2(nextout, 1) < 0) {
                            perror("dup2");
                        }
                        close(nextout);
                    }
                    
                    /// リダイレクト ///
                    for(int i=0; i<vector_size(redirections); i++) {
                        sRedirect* redirect = (sRedirect*)vector_item(redirections, i);
                        
                        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(redirect->mFName, mode, 0666);
                        if(openfd < 0) {
                            fprintf(stderr, "open %s is err", redirect->mFName);
                            exit(1);
                        }
                        
                        if(openfd != redirect->mFd) {
                            if(dup2(openfd, redirect->mFd) < 0) {
                                perror("dup2");
                            }
                            close(openfd);
                        }
                    }

                    /// 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);
//P((f, "(%s)", string_c_str(item)));
                    }
                    argv2[i] = NULL;

/*
P((f, "子プロセス exec前 pid %d", getpid()));
P((f, "子プロセス exec前 ssid %d", tcgetsid(0)));
P((f, "子プロセス exec前 pgrp %d\n", tcgetpgrp(0)));
*/
#if !defined(__CYGWIN__)
                    while(*(char*)shared_memory == 0) {
                    }
#endif

                    /// exec ///
                    execvp(argv2[0], argv2);
                    fprintf(stderr, "exec() error");
                    exit(1);
                }
                /// 親プロセスの処理 ///
                else {
/*
P((f, "親プロセス pid %d", getpid()));
P((f, "親プロセス ssid %d", tcgetsid(0)));
P((f, "親プロセス pgrp %d\n", tcgetpgrp(0)));
*/
                    if(job->mPGroup == -1) {
                        job->mPGroup = pid;

                        // 子を独自プロセスグループに入れる
//P((f, "setpgid(%d, %d)", pid, job->mPGroup));
                        if(setpgid(pid, job->mPGroup) < 0) perror("setpgid1");

#if !defined(__CYGWIN__)
                        *(char*)shared_memory = 1;

                        if(shmdt(shared_memory) == -1) {
                            fprintf(stderr, "shmdt failed\n");
                            exit(1);
                        }
#endif

                        // 子のプロセスグループのプロセスを
                        // フォアグランドに出す
                        if(!statment->background) {
                            if(tcsetpgrp(0, job->mPGroup)) perror("tcsetpgrp");
                        }
                    }
                    else {
                        // 子を独自のプロセスグループに入れる
//P((f, "setpgid(%d, %d)", pid, job->mPGroup));
                        if(setpgid(pid, job->mPGroup) < 0) {
                            perror("setpgid2");
                            exit(1);
                        }

#if !defined(__CYGWIN__)
                        *(char*)shared_memory = 1;

                        if(shmdt(shared_memory) == -1) {
                            fprintf(stderr, "shmdt failed\n");
                            exit(1);
                        }
#endif
                    }

                    job->AddChildProgram(pid, argv, 0);
                    
                    if(nextin != 0) close(nextin);
                    if(nextout != 1) close(nextout);
                    
                    nextin = pipefds[0];
                }
            }
        }

        /// フォアグランドジョブの処理 ///
        if(!statment->background) {
            gForgroundJob = job;

            if(gForgroundJob->mRunningProgs == 0) {
                for(int i = 0; i<vector_size(gJobs); i++) {
                    if(vector_item(gJobs, i) == gForgroundJob) {
                        vector_erase(gJobs, i);
                        break;
                    }
                }
                delete gForgroundJob;
                gForgroundJob = NULL;
            }
            else {
                int rcode = wait_forground_job();
            }
        }
        
        if(*pointer != 0) {
            if(mis_curses() > 0) {
                printf("\n");
            }
            else {
                printf("\n");
            }
        }

        delete statment;
    }

    /// hitanykey ///
    if(hitanykey) {
        if(mis_curses() > 0) {
            printf("\nHIT ANY KEY");
            fflush(stdout);

            int meta;
            mgetch(&meta);
        }
        else {
            printf("\nHIT ANY KEY");
            fflush(stdout);

            minitscr();
            int meta;
            mgetch(&meta);
            mendwin();
        }
    }

#if !defined(__CYGWIN__)
    if(shmctl(gSharedMemoryID, IPC_RMID, 0) == -1) {
        fprintf(stderr, "shmctl(IPC_RMID) failed\n");
    }
#endif
    
    /// 端末のタイトルに名前を付ける ///
    if(gChangeTerminalTitle) {
        char tmp2[PATH_MAX];
        char* home = getenv("HOME");
        if(home == NULL) {
            fprintf(stderr, "HOME is NULL");
            exit(1);
        }
        if(strstr(ActiveDir()->Path(), home) == ActiveDir()->Path()) {
            strcpy(tmp2, "~");
            sprintf(tmp2 + strlen(tmp2), ActiveDir()->Path() + strlen(home));
            printf("%c]2;mfiler2 %s%c", 033, tmp2, 007);
            fflush(stdout);
        }
        else {
            printf("%c]2;mfiler2 %s%c", 033, ActiveDir()->Path(), 007);
            fflush(stdout);
        }
    }
}

///////////////////////////////////////////////////////////////////////////////
// 描写
///////////////////////////////////////////////////////////////////////////////
void shell_view()
{
    const int maxy = mgetmaxy();
    const int maxx = mgetmaxx();

    const int size = vector_size(gJobs);
    for(int i=0; i<size; i++) {
        sJob* job = (sJob*)vector_item(gJobs, i);

        const int x = (maxx/(size<5?5:size))*i;
        char buf[1024];
        sprintf(buf, "[%d]%s", i+1, string_c_str(job->mName));
        if(strlen(buf) > maxx/ (size<5?5:size)) {
            buf[maxx/(size<5?5:size)] = 0;
        }
        mmvprintw(maxy-3-gViewHookLineNum, x, "%s", buf);
    }
}

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

///////////////////////////////////////////////////////////////////////////////
// ジョブのタイトルを返す
///////////////////////////////////////////////////////////////////////////////
char* shell_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;
    }
}

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

        for(int i=0; i<vector_size(job->mChildPrograms); i++) {
            sChildProgram* prog = (sChildProgram*)vector_item(job->mChildPrograms, i);
            kill(prog->mPid, SIGKILL);
        }

        delete job;
        vector_erase(gJobs, num);
    }
}

///////////////////////////////////////////////////////////////////////////////
// ジョブを前面に出す
///////////////////////////////////////////////////////////////////////////////
void shell_forground_job(int num)
{
    if(num>=0 && num < vector_size(gJobs)) {
        mendwin();

        termios tty;
        tcgetattr(STDIN_FILENO, &tty);

        mclear_immediately();
        mmove_immediately(0, 0);
        fflush(stdout);

        sJob* job = (sJob*)vector_item(gJobs, num);

        gForgroundJob = job;
        
        if(tcsetpgrp(0, gForgroundJob->mPGroup)) {
            perror("tcsetpgrp");
        }
        
        if(kill(gForgroundJob->mPGroup, SIGCONT) < 0) {
            sChildProgram* prog = (sChildProgram*)vector_item(job->mChildPrograms, 0);
            kill(prog->mPid, SIGCONT);
        }

        /*
        if(tcsetpgrp(0, job->mPGroup)) {
            perror("tcsetpgrp");
        }
        
        gForgroundJob = job;
        
        sChildProgram* prog = (sChildProgram*)vector_item(job->mChildPrograms, 0);
        
        kill(prog->mPid, SIGCONT);
        */

        /// 端末のタイトルに名前を付ける ///
        if(gChangeTerminalTitle) {
            printf("%c]2;mfiler2 %s%c", 033, string_c_str(job->mName), 007);
            fflush(stdout);
        }
        
        wait_forground_job();

        /// 端末のタイトルに名前を付ける ///
        if(gChangeTerminalTitle) {
            char tmp2[PATH_MAX];
            char* home = getenv("HOME");
            if(home == NULL) {
                fprintf(stderr, "HOME is NULL");
                exit(1);
            }
            if(strstr(ActiveDir()->Path(), home) == ActiveDir()->Path()) {
                strcpy(tmp2, "~");
                sprintf(tmp2 + strlen(tmp2), ActiveDir()->Path() + strlen(home));
                printf("%c]2;mfiler2 %s%c", 033, tmp2, 007);
                fflush(stdout);
            }
            else {
                printf("%c]2;mfiler2 %s%c", 033, ActiveDir()->Path(), 007);
                fflush(stdout);
            }
        }
        
        tcsetattr(STDIN_FILENO, TCSANOW, &tty);

        minitscr();
        mclear();

        mclear_immediately();
    }
}


///////////////////////////////////////////////////////////////////////////////
// 全てのジョブをkillする
///////////////////////////////////////////////////////////////////////////////
void shell_kill_all_jobs()
{
    for(int i=0; i<vector_size(gJobs); i++) {
        sJob* job = (sJob*)vector_item(gJobs, i);
        
        for(int j=0; j<vector_size(job->mChildPrograms); j++) {
            sChildProgram* prog = (sChildProgram*)vector_item(job->mChildPrograms, j);
            kill(prog->mPid, SIGKILL);
        }
    }
}

///////////////////////////////////////////////////////////////////////////////
// フォアグランドジョブの終了を待つ
///////////////////////////////////////////////////////////////////////////////
static int wait_forground_job()
{
//P((f, "wait_forground_job"));

    int return_code = 0;

    if(vector_size(gForgroundJob->mChildPrograms) > 0) {
        while(gForgroundJob && vector_size(gForgroundJob->mChildPrograms) > 0) {
#if defined(__DARWIN__)
            sChildProgram* prog = (sChildProgram*)vector_item(gForgroundJob->mChildPrograms, 0);
            int status;
            pid_t pid = waitpid(prog->mPid, &status, WUNTRACED|WNOHANG);
            usleep(10000);

            if(pid || (kill(prog->mPid, 0) == -1 && errno == ESRCH)) {
                /// 終了した場合 ///
                if(WIFEXITED(status) || WIFSIGNALED(status)) {
                    gForgroundJob->mRunningProgs--;
                    delete prog;
                    vector_erase(gForgroundJob->mChildPrograms, 0);

                    if(gForgroundJob->mRunningProgs == 0) {
                        for(int i=0; i<vector_size(gJobs); i++) {
                            if(vector_item(gJobs, i) == gForgroundJob) {
                                vector_erase(gJobs, i);
                                break;
                            }
                        }
                        delete gForgroundJob;
                        gForgroundJob = NULL;
            
                        gRDir->Reread();
                        gLDir->Reread();

                        if(tcsetpgrp(0, getpid())) perror("tcsetpgrp");
                    }
                }
                /// シグナルの配送によりフォワグランドジョブが停止した場合 ///
                else if(WIFSTOPPED(status)) {
                    gForgroundJob = NULL;

                    if(tcsetpgrp(0, getpid())) perror("tcsetpgrp");
                }
            }
#else
            sChildProgram* prog = (sChildProgram*)vector_item(gForgroundJob->mChildPrograms, 0);
            int status;
            pid_t pid = waitpid(prog->mPid, &status, WUNTRACED);
            //pid_t pid = waitpid(prog->mPid, &status, WUNTRACED|WCONTINUED);
            //waitpid(prog->mPid, &status, WUNTRACED|WCONTINUED);
            //waitpid(prog->mPid, &status, WNOHANG);
            //usleep(100000);
            
            if(pid) {
                /// 終了した場合 ///
                if(WIFEXITED(status) || WIFSIGNALED(status)) {
    //P((f,"wait_forground_job WIFEXITED"));
                    gForgroundJob->mRunningProgs--;
                    delete prog;
                    vector_erase(gForgroundJob->mChildPrograms, 0);

                    if(gForgroundJob->mRunningProgs == 0) {
                        for(int i=0; i<vector_size(gJobs); i++) {
                            if(vector_item(gJobs, i) == gForgroundJob) {
                                vector_erase(gJobs, i);
                                break;
                            }
                        }
                        delete gForgroundJob;
                        gForgroundJob = NULL;
            
                        gRDir->Reread();
                        gLDir->Reread();

                        if(tcsetpgrp(0, getpid())) perror("tcsetpgrp");
                    }

                    return_code = WEXITSTATUS(status);
                    /*
                    char buf[256];
                    sprintf(buf, "%d", return_code);
                    setenv("RCODE", buf, 1);
                    */
                }
                /// シグナルの配送によりフォワグランドジョブが停止した場合 ///
                else if(WIFSTOPPED(status)) {
    //P((f,"wait_forground_job WIFSTOPPED"));
                    gForgroundJob = NULL;

                    if(tcsetpgrp(0, getpid())) perror("tcsetpgrp");
                }
    /*
                /// その他 ///
                else {
                    fprintf(stderr, "unexpected things happened in wait_forground_job\n");
                    exit(1);
                }
    */
            }
#endif
        }
    }

    return return_code;
}

///////////////////////////////////////////////////////////////////////////////
// バックグラウンドジョブが終了していれば後処理をする
///////////////////////////////////////////////////////////////////////////////
void shell_wait_backgroud_job()
{
/*
    /// waitpidして状態が変化していればjobを消す ///
    int status;
    pid_t pid = waitpid(-1, &status, WNOHANG);

    if(pid > 0) {
        for(int i=0; i<vector_size(gJobs); i++) {
            sJob* job = (sJob*)vector_item(gJobs, i);
            for(int j=0; j<vector_size(job->mChildPrograms); j++) {
                sChildProgram* prg = (sChildProgram*)vector_item(job->mChildPrograms, j);
                if(prg->mPid == pid) {
                    delete prg;
                    vector_erase(job->mChildPrograms, j);

                    job->mRunningProgs--;

                    if(job->mRunningProgs <= 0) {
                        vector_erase(gJobs, i);
                        delete job;
        
                        gRDir->Reread();
                        gLDir->Reread();
                    }

                    goto while_end1;
                }
            }
        }
    }

while_end1:
    int n = 0;
*/
while_start:
    for(int i=0; i<vector_size(gJobs); i++) {
        sJob* job = (sJob*)vector_item(gJobs, i);
        for(int j=0; j<vector_size(job->mChildPrograms); j++) {
            sChildProgram* prg = (sChildProgram*)vector_item(job->mChildPrograms, j);

            int status;
            pid_t pid = waitpid(prg->mPid, &status, WNOHANG);

            if(pid == prg->mPid || pid < 0 && errno == ECHILD) {
                delete prg;
                vector_erase(job->mChildPrograms, j);

                job->mRunningProgs--;

                if(job->mRunningProgs == 0) {
                    vector_erase(gJobs, i);
                    delete job;
    
                    gRDir->Reread();
                    gLDir->Reread();
                }

                goto while_start;
            }
        }
    }
}


