#include "common.h"

extern "C"
{
#include <errno.h>
#include <fcntl.h>
#include <grp.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/file.h>
#include <fcntl.h>
#include <unistd.h>
#include <signal.h>
#include <stdio.h>
#include <sys/ioctl.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <termios.h>
#include <dirent.h>
#include <libgen.h>
#include <stdlib.h>

#ifdef __CYGWIN__
# include <ncurses/term.h>
# include <sys/time.h>

typedef void (*SIGHANDLER_T)(int);
#else /* !defined(__CYGWIN) */
# include <term.h>

# ifdef HAVE_SIGHANDLER_T
#  define SIGHANDLER_T sighandler_t
# elif HAVE_SIG_T
#  define SIGHANDLER_T sig_t
# elif HAVE___SIGHANDLER_T
#  define SIGHANDLER_T __sighandler_t
#else
typedef void (*SIGHANDLER_T)(int);
# endif

#endif
}

///////////////////////////////////////////////////////////////////////////////
// 外部にさらす変数
///////////////////////////////////////////////////////////////////////////////
bool gCmdLineActive;        // コマンドラインがアクティブかどうか
vector_obj* gCmdLine;       // コマンドライン文字列の配列
int gCLCursor;              // コマンドラインヒストリのカーソル
int gCursor;                // コマンドラインのカーソル
bool gCmdLineEscapeKey;     // コマンドラインでEscapeキーによるコマンドライン終了がが効くかどうか

VALUE gCmdAfterCmdlineRun = Qnil;
vector_obj* gCCandidate;
vector_obj* gCExplanation;

int gHistorySize = 10000;   // ヒストリの最大数

char gShellName[256];
char gShellPath[256];
char gShellOptEval[256];

bool gXterm = false;
bool gXtermNext = false;
bool gXterm2 = false;

VALUE gXtermPrgName;
VALUE gXtermOptTitle;
VALUE gXtermOptEval;
VALUE gXtermOptExtra;
VALUE gXtermType;

int gCursorBefore;

vector_obj* gHCandidate;

enum eKanjiCode gCmdLineEncode = kUnknown;

///////////////////////////////////////////////////////////////////////////////
// プライベートな関数
///////////////////////////////////////////////////////////////////////////////

/// ヒストリーファイルにコマンドを加える ///
static void add_history(char* str)
{
    char* home = getenv("HOME");
    if(home == NULL) {
        fprintf(stderr, "$HOME is NULL");
        exit(1);
    }

    char path[PATH_MAX];
    sprintf(path, "%s/.mhistory", home);
    if(access(path, W_OK) != 0) {
        creat(path, 0600);
    }

    FILE* f = fopen(path, "a");
    fprintf(f, "%s\n", str);
    fclose(f);
}

/// コマンドラインヒストリ上を移動 ///
static void cmdline_move(int cursor)
{
    gCLCursor += cursor;

    if(gCLCursor < 0) gCLCursor = 0;
    if(gCLCursor >= vector_size(gCmdLine))
        gCLCursor = vector_size(gCmdLine) -1;
}

/// カーソルを移動 ///
static void cursor_move(int v)
{
    if(gKanjiCode == kUtf8) {
        char* str = string_c_str((string_obj*)vector_item(gCmdLine, gCLCursor));
        int utfpos = str_pointer2utfpos(str, str + gCursor);
        utfpos+=v;
        gCursor = str_utfpos2pointer(str, utfpos) - str;
    }
    else {
        gCursor += v;

        if(gCursor < 0) gCursor = 0;
        string_obj* str = (string_obj*)vector_item(gCmdLine, gCLCursor);
        if(gCursor > string_length(str)) gCursor = string_length(str);
    }
}

///////////////////////////////////////////////////////////////////////////////
// 初期化
///////////////////////////////////////////////////////////////////////////////
void cmdline_init()
{
    gCmdLine = vector_new(10);
    gCCandidate = vector_new(10);
    gCExplanation = vector_new(10);

    gHCandidate = vector_new(10);

    gCmdLineActive = false;
    gCLCursor = 0;
    gCursor = 0;

    gAlias = vector_new(10);

    gXtermPrgName = rb_str_new2("xterm");
    gXtermOptTitle = rb_str_new2("-T");    
    gXtermOptEval = rb_str_new2("-e");
    gXtermOptExtra = rb_str_new2("");
    gXtermType = INT2NUM(0);

    rb_global_variable(&gXtermPrgName);
    rb_global_variable(&gXtermOptTitle);
    rb_global_variable(&gXtermOptEval);
    rb_global_variable(&gXtermOptExtra);
    rb_global_variable(&gXtermType);

//    rb_global_variable(&gPrograms);
    rb_global_variable(&gEnvirons);
    gPrograms = rb_ary_new();

    rb_global_variable(&gCmdAfterCmdlineRun);
    strcpy(gShellName, "sh");
    strcpy(gShellPath, "/bin/sh");
    strcpy(gShellOptEval, "-c");

    gCursorBefore = 0;

    cmdline_read_history_file();

    gJobs = vector_new(10);
}

///////////////////////////////////////////////////////////////////////////////
// 終了処理
///////////////////////////////////////////////////////////////////////////////
void cmdline_final()
{
    for(int i=0; i<vector_size(gJobs); i++) {
        delete (sJob*)vector_item(gJobs, i);
    }
    vector_delete(gJobs);

    vector_delete(gHCandidate);
    for(int i=0; i<vector_size(gAlias); i++) {
        FREE(vector_item(gAlias, i));
    }
    vector_delete(gAlias);

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

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

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

///////////////////////////////////////////////////////////////////////////////
// コマンドラインヒストリの最後の行が空なら消す
///////////////////////////////////////////////////////////////////////////////
void cmdline_delete_last_str_if_length_0()
{
    string_obj* last_str = (string_obj*)vector_item(gCmdLine, vector_size(gCmdLine)-1);
    if(string_c_str(last_str)[0] == ' ' || string_length(last_str) == 0) {
        vector_pop_back(gCmdLine);
    }
}

///////////////////////////////////////////////////////////////////////////////
// .mhistoryを読み込む
///////////////////////////////////////////////////////////////////////////////
void cmdline_read_history_file()
{
    /// コマンドラインヒストリを初期化 ///
    for(int i=0; i<vector_size(gCmdLine); i++) {
        string_delete((string_obj*)vector_item(gCmdLine,i));
    }
    vector_clear(gCmdLine);

    /// ヒストリファイルのパスを得てヒストリファイルが無いなら作成 ///
    char* home = getenv("HOME");
    if(home == NULL) {
        return;
    }

    char path[PATH_MAX];
    sprintf(path, "%s/.mhistory", home);

    if(access(path, R_OK) != 0) {
        creat(path, 0600);
    }

    /// ヒストリファイルのサイズを得て、サイズ分メモリを用意する ///
    struct stat hstat;
    if(stat(path, &hstat) < 0) {
        fprintf(stderr, "can't read $HOME/.mhistory\n");
        exit(0);
    }
    char* hfile = (char*)MALLOC(sizeof(char)*(hstat.st_size+1));

    /// ヒストリファイルを読み込む ///
    char buf[BUFSIZ];
    int hfile_size = 0;
    int n2 = 1;
    int f = open(path, O_RDONLY);
    while(n2 > 0) {
        n2 = read(f, buf, sizeof(BUFSIZ));

        memcpy(hfile + hfile_size, buf, sizeof(char)*n2);

        hfile_size += n2;
    }
    close(f);
    hfile[hfile_size] = 0;

    /// ヒストリファイルを一行ごと読み込みgCmdLineに格納する ///
    char cmd[kCmdLineMax];
    int n = 0;

    char* p = hfile;
    int line = 1;
    while(*p) {
        if(*p == '\n') {
            cmd[n] = 0;
            vector_add(gCmdLine, string_new(cmd));
            n = 0;
            line++;
        }
        else {
            cmd[n++] = *p;
        }

        p++;
    }

    /// ヒストリサイズが最大数を超えていた場合半分にする ///
    if(line >= gHistorySize) {
        /// 半分のサイズのヒストリを得る ///
        char* half_hfile = (char*)MALLOC(sizeof(char)*(hstat.st_size+1));

        char* p = hfile;
        int line2 = 0;
        while(*p) {
            if(*p == '\n') {
                line2++;

                if(line2 == line/2) break;
            }

            p++;
        }

        memcpy(half_hfile, p + 1, hfile+hfile_size-p-1);

        half_hfile[hfile+hfile_size-p-1] = 0;

        /// 半分のサイズのヒストリをファイルに書き込む ///
        int f = open(path, O_WRONLY|O_TRUNC);
        write(f, half_hfile, hfile+hfile_size-p-1);
        close(f);

        FREE(half_hfile);
    }

    FREE(hfile);    
}

///////////////////////////////////////////////////////////////////////////////
// コマンドラインに入る
///////////////////////////////////////////////////////////////////////////////
void cmdline_start(char* cmd, int position)
{
    gCmdLineActive = true;

    if(RARRAY(gPrograms)->len == 0) {
        cmdline_rehash();
    }
    else {
        if(gAutoRehash) cmdline_rehash();
    }

    bool ruby_mode;
    bool ruby_mode_nonoutput;
    bool output;
    bool shell_mode;
    bool shell_mode2;
    bool quick;
    bool xterm_mode;
    bool reset_marks;
    string_obj* cmd2 = extend_macro(cmd, &ruby_mode, &ruby_mode_nonoutput, &output, &quick, &shell_mode, &shell_mode2, &xterm_mode, &reset_marks, false, true);
/*
#if defined(HAVE_ICONV_H) || defined(HAVE_BICONV_H)
    if(is_all_ascii(string_c_str(cmd2)) || gKanjiCodeFileName == gKanjiCode) {
        vector_add(gCmdLine, cmd2);
    }
    else {
        string_obj* cmd3 = string_new("");
        if(kanji_convert2(cmd2, cmd3, gKanjiCodeFileName, gKanjiCode) == -1) {
            vector_add(gCmdLine, cmd2);
        }
        else {
            vector_add(gCmdLine, cmd3);
        }
        string_delete(cmd3);
    }
#else
    vector_add(gCmdLine, cmd2);
#endif
*/
    vector_add(gCmdLine, cmd2);

    gCLCursor = vector_size(gCmdLine)-1;
    if(position > 0)
        gCursor = position-1;
    else
        gCursor = string_length(cmd2) + position;
}

///////////////////////////////////////////////////////////////////////////////
// コマンドラインを実行
///////////////////////////////////////////////////////////////////////////////
void cmdline_run(char* str, char* title)
{
    /// extend alias ///
    char cmd[kCmdLineMax];
    if(!extend_alias(str, cmd)) {
        gErrMsgCancel = false; 
        err_msg("can't extend alias");
        return;
    }

    /// extend macro ///
    bool ruby_mode;
    bool ruby_mode_nonoutput;
    bool output;
    bool shell_mode;
    bool shell_mode2;
    bool quick;
    bool xterm_mode;
    bool reset_marks;
    string_obj* cmd2 = extend_macro(cmd, &ruby_mode, &ruby_mode_nonoutput, &output, &quick, &shell_mode, &shell_mode2, &xterm_mode, &reset_marks, true, false);

    bool tmp1;
    bool tmp2;
    bool tmp3;
    bool tmp4;
    bool tmp5;
    bool tmp6;
    bool tmp7;
    bool tmp8;
    string_obj* title2 = extend_macro(title, &tmp1, &tmp2, &tmp3, &tmp4, &tmp5, &tmp6, &tmp7, &tmp8, false, false);

    char* term = getenv("TERM");
    if(term == NULL) {
        fprintf(stderr, "$TERM is null");
        exit(1);
    }

    const int maxy = mgetmaxy();

    mclear_online(maxy-2);
    mclear_online(maxy-1);

    mmove_immediately(maxy-2, 0);
    mrefresh();

    /// check length of cmd2 over screen command line max ///
    bool screen_max = false;
    if(string_length(cmd2) > 4000) {
        screen_max = true;
    }
    
    /// ruby mode ///
    bool flg_screen = (strstr(term, "screen") == term || gGnuScreen) && !screen_max;
    bool flg_xterm = xterm_mode || (gXterm && !(gXtermNext || gXterm2)) 
        || (!gXterm && (gXtermNext || gXterm2));
    if(ruby_mode || ruby_mode_nonoutput) {
        if(!quick) {
            mendwin();

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

        int error;

        VALUE value;
#if defined(HAVE_ICONV_H) || defined(HAVE_BICONV_H)
        int kcode_error;
        rb_eval_string_protect("mfiler2_kcode_tmp = $KCODE", &kcode_error);
        if(gCmdLineEncode == kUtf8 || gCmdLineEncode == kUtf8Mac) {
            rb_eval_string_protect("$KCODE = \"UTF8\"", &kcode_error);
        }
        else if(gCmdLineEncode == kSjis) {
            rb_eval_string_protect("$KCODE = \"SJIS\"", &kcode_error);
        }
        else if(gCmdLineEncode == kEucjp) {
            rb_eval_string_protect("$KCODE = \"EUC\"", &kcode_error);
        }

        string_obj* cmd3 = string_new("");
        if(kanji_convert2(cmd2, cmd3, gKanjiCode, gCmdLineEncode) == -1) {
            value = rb_eval_string_protect(string_c_str(cmd2), &error);
        }
        else {
            value = rb_eval_string_protect(string_c_str(cmd3), &error);
        }
        string_delete(cmd3);

        rb_eval_string_protect("$KCODE = mfiler2_kcode_tmp", &kcode_error);
#else
        value = rb_eval_string_protect(string_c_str(cmd2), &error);
#endif
        
        if(error) {
            if(quick) {
                mendwin();

                mclear_immediately();
                mmove_immediately(0, 0);
                fflush(stdout);
            }
            
            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;
            }
            

            if(quick) {
                printf("\nHIT ANY KEY");
                fflush(stdout);

                minitscr();

                int meta;
                mgetch(&meta);

                mclear();



                mclear_immediately();



            }
        }
        else {
            if(!quick && !ruby_mode_nonoutput) {
                printf("=> ");
                rb_p(value);
            }
        }

        if(!quick) {
            if(output || error) {
                printf("\nHIT ANY KEY");
                fflush(stdout);
            }

            minitscr();

            if(output || error) {
                int meta;
                mgetch(&meta);
            }

            mclear();



            mclear_immediately();


        }
    }

    /// xterm mode ///
    else if(flg_xterm && !shell_mode && !quick) {
        /// normal xterm ///
        if(NUM2INT(gXtermType) == 0) {
            char* argv[256];

            argv[0] = RSTRING(gXtermPrgName)->ptr;
            argv[1] = RSTRING(gXtermOptTitle)->ptr;
            argv[2] = string_c_str(title2);

            int arg_num = 0;
            int c = 0;
            char buf2[128][128];

            char* p = RSTRING(gXtermOptExtra)->ptr;

            while(*p == ' ') p++;

            if(*p != 0) {
                while(1) {
                    if(*p == ' ') {
                        buf2[arg_num][c] = 0;
                        c = 0;
                        arg_num++;

                        while(*p == ' ') p++;
                    }
                    else if(*p == 0) {
                        buf2[arg_num][c] = 0;
                        arg_num++;
                        break;
                    }
                    else {
                        buf2[arg_num][c++] = *p++;
                    }
                }
            }

            for(int i=0; i<arg_num; i++) {
                argv[3+i] = buf2[i];
            }

            argv[3+arg_num] = RSTRING(gXtermOptEval)->ptr;
            argv[4+arg_num] = gShellPath;
            argv[5+arg_num] = gShellOptEval;

            if(output && !quick) {
                string_insert(cmd2, 0, "( ");
                char tmp[256];
                if(gUnixDomainSocket)
                    sprintf(tmp, " ); mfiler2 -r; mhitanykey");
                else 
                    sprintf(tmp, " ); mhitanykey");
                
                string_push_back(cmd2, tmp);
            }
            else {
                string_insert(cmd2, 0, "( ");
                char tmp[256];
                if(gUnixDomainSocket)
                    sprintf(tmp, " ); mfiler2 -r");
                else
                    sprintf(tmp, " )");

                string_push_back(cmd2, tmp);
            }

#if defined(HAVE_ICONV_H) || defined(HAVE_BICONV_H)
            string_obj* cmd3 = string_new("");
            if(kanji_convert2(cmd2, cmd3, gKanjiCode, gCmdLineEncode) == -1) {
                argv[6+arg_num] = string_c_str(cmd2);
            }
            else {
                argv[6+arg_num] = string_c_str(cmd3);
            }
#else
            argv[6+arg_num] = string_c_str(cmd2);
#endif
            argv[7+arg_num] = NULL;
/*
int i = 0;
while(argv[i] != NULL) {
M(("%d (%s)", i, argv[i]));
i++;
}
*/



            pid_t pid = fork();
            if(pid == -1) {
                fprintf(stderr, "fork failed");
                exit(1);
            }


            if(pid == 0) {
                //setsid();
                //close(2);
                if(execvp(RSTRING(gXtermPrgName)->ptr, argv) < 0) {
                    fprintf(stderr, "execvp failed");
                    exit(0);
                }
            }

            if(!cDirWnd::gRemainMarks && reset_marks || cDirWnd::gRemainMarks && !reset_marks) ActiveDir()->ResetMarks();

            if(gSortMarkFileUp) {
                gLDir->Sort();
                gRDir->Sort();
            }

#if defined(HAVE_ICONV_H) || defined(HAVE_BICONV_H)
            string_delete(cmd3);
#endif
        }
        /// GNOME terminal ///
        else {
            char* argv[256];

            argv[0] = RSTRING(gXtermPrgName)->ptr;
            argv[1] = RSTRING(gXtermOptTitle)->ptr;
            argv[2] = string_c_str(title2);

            int arg_num = 0;
            int c = 0;
            char buf2[128][128];

            char* p = RSTRING(gXtermOptExtra)->ptr;

            while(*p == ' ') p++;

            if(*p != 0) {
                while(1) {
                    if(*p == ' ') {
                        buf2[arg_num][c] = 0;
                        c = 0;
                        arg_num++;

                        while(*p == ' ') p++;
                    }
                    else if(*p == 0) {
                        buf2[arg_num][c] = 0;
                        arg_num++;
                        break;
                    }
                    else {
                        buf2[arg_num][c++] = *p++;
                    }
                }
            }

            for(int i=0; i<arg_num; i++) {
                argv[3+i] = buf2[i];
            }

            argv[3+arg_num] = RSTRING(gXtermOptEval)->ptr;


            string_obj* cmd3 = string_new("");
            char* p2 = string_c_str(cmd2);
            while(*p2) {
                if(*p2 == '"') {
                    string_push_back2(cmd3, '\\');
                    string_push_back2(cmd3, '"');
                }
                else {
                    string_push_back2(cmd3, *p2);
                }
                p2++;
            }


            if(output && !quick) {
                char tmp0[1024];
                sprintf(tmp0, "%s %s \"(", gShellPath, gShellOptEval);

                string_insert(cmd3, 0, tmp0);

                char tmp[256];
                if(gUnixDomainSocket)
                    sprintf(tmp, " ); mfiler2 -r; mhitanykey\"");
                else 
                    sprintf(tmp, " ); mhitanykey\"");
                
                string_push_back(cmd3, tmp);
            }
            else {
                char tmp0[1024];
                sprintf(tmp0, "%s %s \"(", gShellPath, gShellOptEval);

                string_insert(cmd3, 0, tmp0);

                char tmp[256];
                if(gUnixDomainSocket)
                    sprintf(tmp, " ); mfiler2 -r\"");
                else
                    sprintf(tmp, " )\"");

                string_push_back(cmd3, tmp);
            }

            argv[4+arg_num] = string_c_str(cmd3);
#if defined(HAVE_ICONV_H) || defined(HAVE_BICONV_H)
            string_obj* cmd4 = string_new("");
            if(kanji_convert2(cmd3, cmd4, gKanjiCode, gCmdLineEncode) == -1) {
                argv[4+arg_num] = string_c_str(cmd3);
            }
            else {
                argv[4+arg_num] = string_c_str(cmd4);
            }
#else
            argv[4+arg_num] = string_c_str(cmd3);
#endif
            argv[5+arg_num] = NULL;

            pid_t pid = fork();
            if(pid == -1) {
                fprintf(stderr, "fork failed");
                exit(1);
            }

            if(pid == 0) {
                //setsid();
                if(execvp(RSTRING(gXtermPrgName)->ptr, argv) < 0) {
                    fprintf(stderr, "execvp failed");
                    exit(0);
                }
            }

            if(!cDirWnd::gRemainMarks && reset_marks || cDirWnd::gRemainMarks && !reset_marks) ActiveDir()->ResetMarks();

            if(gSortMarkFileUp) {
                gLDir->Sort();
                gRDir->Sort();
            }

            string_delete(cmd3);
#if defined(HAVE_ICONV_H) || defined(HAVE_BICONV_H)
            string_delete(cmd4);
#endif
        }
    }

    /// screen terminal ///
    else if(flg_screen && !shell_mode && !quick) 
    {
/*
        if(!quick) {
            mendwin();

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

        bool hitanykey = output && !quick;

#if defined(HAVE_ICONV_H) || defined(HAVE_BICONV_H)
        string_obj* cmd3 = string_new("");
        if(kanji_convert2(cmd2, cmd3, gKanjiCode, gCmdLineEncode) == -1) {
            string_insert(cmd2, 0, "screen -t \"\" bash -c \"(");
            string_insert(cmd2, 11, string_c_str(title2));
            string_push_back(cmd2, "); mhitanykey\"");
            shell(string_c_str(cmd2), false, "");
        }
        else {
            string_insert(cmd3, 0, "screen -t \"\" bash -c \"(");
            string_insert(cmd3, 11, string_c_str(title2));
            string_push_back(cmd3, "); mhitanykey\"");
            shell(string_c_str(cmd3), false, "");
        }
        string_delete(cmd3);
#else
        string_insert(cmd2, 0, "screen -t \"\" bash -c \"(");
        string_insert(cmd2, 11, string_c_str(title2));
        string_push_back(cmd2, "); mhitanykey\"");
        shell(string_c_str(cmd2), false, "");
#endif

        if(!quick) {
            minitscr();
            mclear();

            mclear_immediately();
        }

*/
        char* argv[7];

        if(string_length(title2) > 20) {
            string_trunc(title2, 20);
        }

        argv[0] = "screen";
        argv[1] = "-t";
        argv[2] = string_c_str(title2);
        argv[3] = gShellPath;
        argv[4] = gShellOptEval;
        argv[6] = NULL;

        if(output && !quick) {
            string_insert(cmd2, 0, "( ");
            char tmp[256];
            if(gUnixDomainSocket)
                sprintf(tmp, " ); mfiler2 -r; mhitanykey");
            else
                sprintf(tmp, " ); mhitanykey");

            string_push_back(cmd2, tmp);
        }
        else {
            string_insert(cmd2, 0, "( ");
            char tmp[256];
            if(gUnixDomainSocket)
                sprintf(tmp, " ); mfiler2 -r");
            else
                sprintf(tmp, " )");
            string_push_back(cmd2, tmp);
        }

#if defined(HAVE_ICONV_H) || defined(HAVE_BICONV_H)
        string_obj* cmd3 = string_new("");
        if(kanji_convert2(cmd2, cmd3, gKanjiCode, gCmdLineEncode) == -1) {
            argv[5] = string_c_str(cmd2);
        }
        else {
            argv[5] = string_c_str(cmd3);
        }
#else
        argv[5] = string_c_str(cmd2);
#endif

        pid_t pid = fork();
        if(pid == -1) {
            fprintf(stderr, "fork failed");
            exit(1);
        }

        if(pid == 0) {
            //setsid();
            execvp("screen", argv);
        }

        if(!cDirWnd::gRemainMarks && reset_marks || cDirWnd::gRemainMarks && !reset_marks) ActiveDir()->ResetMarks();

        if(gSortMarkFileUp) {
            gLDir->Sort();
            gRDir->Sort();
        }

        static int pid_before = 0;
        if(pid_before != 0) waitpid(pid_before, NULL, 0);
        pid_before = pid;

#if defined(HAVE_ICONV_H) || defined(HAVE_BICONV_H)
        string_delete(cmd3);
#endif
    }
    
    /// system関数を使う ///
    else if(shell_mode2) {
        SIGHANDLER_T mfiler_int = signal(SIGINT, SIG_DFL);
        SIGHANDLER_T mfiler_quit = signal(SIGQUIT, SIG_DFL);
        SIGHANDLER_T mfiler_abrt = signal(SIGABRT, SIG_DFL);
        SIGHANDLER_T mfiler_kill = signal(SIGKILL, SIG_DFL);
        SIGHANDLER_T mfiler_pipe = signal(SIGPIPE, SIG_DFL);
        SIGHANDLER_T mfiler_alrm = signal(SIGALRM, SIG_DFL);
        SIGHANDLER_T mfiler_term = signal(SIGTERM, SIG_DFL);
        SIGHANDLER_T mfiler_hup = signal(SIGHUP, SIG_DFL);
        SIGHANDLER_T mfiler_stp = signal(SIGTSTP, SIG_DFL);
        SIGHANDLER_T mfiler_ttou = signal(SIGTTOU, SIG_DFL);
        SIGHANDLER_T mfiler_cont = signal(SIGCONT, SIG_DFL);
        SIGHANDLER_T mfiler_winch = signal(SIGWINCH, SIG_DFL);
        SIGHANDLER_T mfiler_child = signal(SIGCHLD, SIG_DFL);

        if(!quick) {
            mendwin();

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

        if(output && !quick) {
            string_insert(cmd2, 0, "( ");
            char tmp[256];
            sprintf(tmp, " ); mhitanykey", cmd2);
            string_push_back(cmd2, tmp);
        }

#if defined(HAVE_ICONV_H) || defined(HAVE_BICONV_H)
        string_obj* cmd3 = string_new("");
        if(kanji_convert2(cmd2, cmd3, gKanjiCode, gCmdLineEncode) == -1) {
            if(msystem(string_c_str(cmd2)) == -1) {
                gErrMsgCancel = false; 
                err_msg("can't evaluate with system()");
            }
        }
        else {
            if(msystem(string_c_str(cmd3)) == -1) {
                gErrMsgCancel = false; 
                err_msg("can't evaluate with system()");
            }
        }
        string_delete(cmd3);
#else
        if(msystem(string_c_str(cmd2)) == -1) {
            gErrMsgCancel = false; 
            err_msg("can't evaluate with system()");
        }
#endif

        if(!quick) {
            minitscr();
            mclear();

            mclear_immediately();

        }

        signal(SIGINT, mfiler_int);
        signal(SIGQUIT, mfiler_quit);
        signal(SIGABRT, mfiler_abrt);
        signal(SIGKILL, mfiler_kill);
        signal(SIGPIPE, mfiler_pipe);
        signal(SIGALRM, mfiler_alrm);
        signal(SIGTERM, mfiler_term);
        signal(SIGHUP, mfiler_hup);
        signal(SIGTSTP, mfiler_stp);
        signal(SIGTTOU, mfiler_ttou);
        signal(SIGCONT, mfiler_cont);
        signal(SIGWINCH, mfiler_winch);
        signal(SIGCHLD, mfiler_child);

        if(!cDirWnd::gRemainMarks && reset_marks || cDirWnd::gRemainMarks && !reset_marks) ActiveDir()->ResetMarks();

        gLDir->Reread();
        gRDir->Reread();
    }

    /// shell mode ///
    else  {
        if(!quick) {
            mendwin();

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

        bool hitanykey = output && !quick;

#if defined(HAVE_ICONV_H) || defined(HAVE_BICONV_H)
        string_obj* cmd3 = string_new("");
        if(kanji_convert2(cmd2, cmd3, gKanjiCode, gCmdLineEncode) == -1) {
            shell(string_c_str(cmd2), hitanykey, string_c_str(title2));
        }
        else {
            shell(string_c_str(cmd3), hitanykey, string_c_str(title2));
        }
        string_delete(cmd3);
#else
        shell(string_c_str(cmd2), hitanykey, string_c_str(title2));
#endif

        if(!quick) {
            minitscr();
            mclear();

            mclear_immediately();
        }

        if(!cDirWnd::gRemainMarks && reset_marks || cDirWnd::gRemainMarks && !reset_marks) ActiveDir()->ResetMarks();

        gLDir->Reread();
        gRDir->Reread();
    }

    string_delete(title2);
    string_delete(cmd2);

/*
    gLDir->Reread();
    gRDir->Reread();
*/

    gCmdLineEncode = kUnknown;

    gXtermNext = false;

    if(gCmdAfterCmdlineRun != Qnil) {
        int error;
        rb_eval_string_protect(RSTRING(gCmdAfterCmdlineRun)->ptr, &error);

        if(error) {
            mendwin();

            mclear_immediately();
            mmove_immediately(0, 0);
            fflush(stdout);
            
            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;
            }
            

            minitscr();

            int meta;
            mgetch(&meta);

            mclear();

            mclear_immediately();

            gCmdAfterCmdlineRun = Qnil;
        }
    }
}

///////////////////////////////////////////////////////////////////////////////
// 入力
///////////////////////////////////////////////////////////////////////////////
void cmdline_input(int meta, int key)
{
    bool kanji[kCmdLineMax];
    char* p = string_c_str((string_obj*)vector_item(gCmdLine, gCLCursor));
    const int len = strlen(p);
    for(int i=0; i<len; i++) {
        if(is_kanji(p[i])) {
            kanji[i] = true;
            kanji[i+1] = false;
            i++;
        }
        else {
            kanji[i] = false;
        }
    }

    /// カーソルを前に移動 ///
    if(key == 6 || key == KEY_RIGHT) {    // CTRL-F
        if(gKanjiCode == kUtf8) {
            cursor_move(1);
        }
        else {
            if(kanji[gCursor])
                cursor_move(2);
            else
                cursor_move(1);
        }

        cmdline_completion_clear();
    }

    /// カーソルを後ろに移動 ///
    else if(key == 2 || key == KEY_LEFT) {    // CTRL-B
        if(gKanjiCode == kUtf8) {
            cursor_move(-1);
        }
        else {
            if(gCursor > 0) {
                if(gCursor == 1) {
                    cursor_move(-1);
                }
                else {
                    if(kanji[gCursor-2])
                        cursor_move(-2);
                    else 
                        cursor_move(-1);
                }
            }
        }

        cmdline_completion_clear();
    }

    /// 後方に文字列分移動 ///
    else if(meta==1 && key == 'b' || meta==1 && key == KEY_LEFT // Meta-b
        || key==KEY_F(13))
    {
        if(gCursor > 0) {
            string_obj* str = (string_obj*) vector_item(gCmdLine, gCLCursor);

            const char* s = string_c_str(str);
            int pos = gCursor;
            pos--;
            while(pos>=0 && (s[pos] == ' ' || s[pos] == '/' || s[pos] == '\'' || s[pos] == '"' || s[pos] == '.')) {
                pos--;
            }
            while(pos>=0 && s[pos] != ' ' && s[pos] != '/' && s[pos] != '\'' && s[pos] != '"' && s[pos] != '.') {
                pos--;
            gCursor = pos+1;
            }

        cmdline_completion_clear();
        }
    }

    /// 前方に文字列分移動 ///
    else if(meta==1 && key == 'f' || meta==1 && key == KEY_RIGHT // Meta-f
        || key==KEY_F(14))
    {
        string_obj* str = (string_obj*) vector_item(gCmdLine, gCLCursor);
        const char* s = string_c_str(str);

        if(s[gCursor] != 0) {
            int pos = gCursor;
            pos++;
            while(s[pos]!=0 && (s[pos] == ' ' || s[pos] == '/' || s[pos] == '\'' || s[pos] == '"' || s[pos] == '.')) {
                pos++;
            }
            while(s[pos]!=0 && s[pos] != ' ' && s[pos] != '/' && s[pos] != '\'' && s[pos] != '"' && s[pos] != '.') {
                pos++;
            }

            gCursor = pos;
        }

        cmdline_completion_clear();
    }

    /// カーソルを一番前に移動 ///
    else if(key == 1 || key == KEY_HOME) {    // CTRL-A
        cursor_move(-999);

        cmdline_completion_clear();
    }

    /// カーソルを末尾に移動 ///
    else if(key == 5 || key == KEY_END) {    // CTRL-E
        cursor_move(999);

        cmdline_completion_clear();
    }

    /// 後方文字を削除 ///
    else if(key == 4 || key == KEY_DC) {    // CTRL-D DELETE
        string_obj* str = (string_obj*)vector_item(gCmdLine, gCLCursor);
        char* cstr = string_c_str(str);

        /// 編集テキストのサイズが0ならコマンドライン終了 ///
        if(string_length(str) == 0) {
            gCmdLineActive = false;
            
            vector_erase(gCmdLine, gCLCursor);
            cmdline_delete_last_str_if_length_0();
        }
        /// 編集テキストのサイズが0以上なら文字を削除 ///
        else {
            if(gCursor < string_length(str)) {
                /// ヒストリーを編集するならコピーを末尾に作成 ///
                if(gCLCursor != vector_size(gCmdLine)-1) {
                    cmdline_delete_last_str_if_length_0();
                    
                    vector_add(gCmdLine, string_new(cstr));
                    gCLCursor = vector_size(gCmdLine) -1;

                    str = (string_obj*)vector_item(gCmdLine, gCLCursor);
                    cstr = string_c_str(str);
                }

                /// 文字を削除 ///
                if(gKanjiCode == kUtf8) {
                    int utfpos = str_pointer2utfpos(cstr, cstr + gCursor);
                    char* next_point = str_utfpos2pointer(cstr, utfpos+1);

                    string_erase(str, gCursor, next_point - (cstr + gCursor));
                }
                else {
                    if(kanji[gCursor]) {
                        string_erase(str, gCursor, 2);
                    }
                    else {
                        string_erase(str, gCursor, 1);
                    }
                }
            }
        }

        cmdline_completion_clear();
    }

    /// 後方文字を削除 ///
    else if(key == 8 || key == KEY_BACKSPACE) {    // CTRL-H
        if(gCursor > 0) {
            string_obj* str = (string_obj*) vector_item(gCmdLine, gCLCursor);
            char* cstr = string_c_str(str);

            /// ヒストリーを編集するならコピーを末尾に作成 ///
            if(gCLCursor != vector_size(gCmdLine)-1) {
                cmdline_delete_last_str_if_length_0();
                
                vector_add(gCmdLine, string_new(cstr));
                gCLCursor = vector_size(gCmdLine) -1;

                str = (string_obj*)vector_item(gCmdLine, gCLCursor);
                cstr = string_c_str(str);
            }

            /// 文字を削除 ///
            if(gKanjiCode == kUtf8) {
                int utfpos = str_pointer2utfpos(cstr, cstr + gCursor);
                char* before_point = str_utfpos2pointer(cstr, utfpos-1);
                int new_cursor = before_point-cstr;

                string_erase(str, before_point - cstr, (cstr + gCursor) - before_point);
                gCursor = new_cursor;
            }
            else {
                if(gCursor == 1) {
                    string_erase(str, gCursor-1, 1);
                    cursor_move(-1);
                }
                else {
                    if(kanji[gCursor-2]) {
                        string_erase(str, gCursor-2, 2);

                        cursor_move(-2);
                    } else {
                        string_erase(str, gCursor-1, 1);

                        cursor_move(-1);
                    }
                }
            }
        }

        cmdline_completion_clear();
    }

    /// 行を削除 ///
    else if(key == 21) {    // CTRL-U
        string_obj* str = (string_obj*)vector_item(gCmdLine, gCLCursor);
        char* cstr = string_c_str(str);

        /// ヒストリーを編集するならコピーを末尾に作成 ///
        if(gCLCursor != vector_size(gCmdLine)-1) {
            cmdline_delete_last_str_if_length_0();
            
            vector_add(gCmdLine, string_new(cstr));
            gCLCursor = vector_size(gCmdLine) -1;

            str = (string_obj*)vector_item(gCmdLine, gCLCursor);
            cstr = string_c_str(str);
        }

        /// 行を削除 ///
        string_put(str, "");

        gCursor = 0;

        cmdline_completion_clear();
    }

    /// 前方文字列を削除 ///
    else if(key == 23) {     // CTRL-W
        string_obj* str = (string_obj*) vector_item(gCmdLine, gCLCursor);
        char* cstr = string_c_str(str);

        /// ヒストリーを編集するならコピーを末尾に作成 ///
        if(gCLCursor != vector_size(gCmdLine)-1) {
            cmdline_delete_last_str_if_length_0();
            
            vector_add(gCmdLine, string_new(cstr));
            gCLCursor = vector_size(gCmdLine) -1;

            str = (string_obj*)vector_item(gCmdLine, gCLCursor);
            cstr = string_c_str(str);
        }

        /// 文字列を削除 ///
        if(gCursor > 0) {
            int pos = gCursor-1;
            if(cstr[pos]==' ' || cstr[pos]=='/' || cstr[pos]=='\'' || cstr[pos]=='"' || cstr[pos]=='.') {
                while(pos>=0 && (cstr[pos]==' ' || cstr[pos]=='/' || cstr[pos]=='\'' || cstr[pos]=='"' || cstr[pos]=='.'))
                {
                    pos--;
                }
            }
            while(pos>=0 && cstr[pos]!=' ' && cstr[pos]!='/' && cstr[pos]!='\'' && cstr[pos]!='"' && cstr[pos]!='.')
            {
                pos--;
            }

            string_erase(str, pos+1, gCursor-pos-1);

            gCursor = pos+1;
        }

        cmdline_completion_clear();
    }

    /// 後方文字列を削除 ///
    else if(meta==1 && key == 'd') {     // Meta-d
        string_obj* str = (string_obj*) vector_item(gCmdLine, gCLCursor);
        char* cstr = string_c_str(str);

        /// ヒストリーを編集するならコピーを末尾に作成 ///
        if(gCLCursor != vector_size(gCmdLine)-1) {
            cmdline_delete_last_str_if_length_0();
            
            vector_add(gCmdLine, string_new(cstr));
            gCLCursor = vector_size(gCmdLine) -1;

            str = (string_obj*)vector_item(gCmdLine, gCLCursor);
            cstr = string_c_str(str);
        }

        /// 後方文字列を削除 ///
        if(cstr[gCursor] != 0) {
            int pos = gCursor;
            pos++;
            while(cstr[pos]!=0 && (cstr[pos] == ' ' || cstr[pos] == '/' || cstr[pos] == '\'' || cstr[pos] == '"' || cstr[pos] == '.')) {
                pos++;
            }
            while(cstr[pos]!=0 && cstr[pos] != ' ' && cstr[pos] != '/' && cstr[pos] != '\'' && cstr[pos] != '"' && cstr[pos] != '.') {
                pos++;
            }

            string_erase(str, gCursor, pos-gCursor);
            //gCursor = pos;
        }

        cmdline_completion_clear();
    }

    /// カーソルから末尾まで削除 ///
    else if(key == 11) {    // CTRL-K
        string_obj* str = (string_obj*)vector_item(gCmdLine, gCLCursor);
        char* cstr = string_c_str(str);

        /// ヒストリーを編集するならコピーを末尾に作成 ///
        if(gCLCursor != vector_size(gCmdLine)-1) {
            cmdline_delete_last_str_if_length_0();
            
            vector_add(gCmdLine, string_new(cstr));
            gCLCursor = vector_size(gCmdLine) -1;

            str = (string_obj*)vector_item(gCmdLine, gCLCursor);
            cstr = string_c_str(str);
        }

        /// 末尾まで削除 ///
        string_erase(str, gCursor, string_length(str)-gCursor);

        cmdline_completion_clear();
    }
    else if(key == 14 || key == KEY_DOWN) {    // CTRL-N
        cmdline_move(1);

        string_obj* str = (string_obj*)vector_item(gCmdLine, gCLCursor);
        gCursor = string_length(str);
        
        cmdline_completion_clear();
    }
    else if(key == 16 || key == KEY_UP) {    //CTRL-P
        cmdline_move(-1);
        string_obj* str = (string_obj*)vector_item(gCmdLine, gCLCursor);
        gCursor = string_length(str);

        cmdline_completion_clear();
    }
    else if(key == 12) {// CTRL-L
        gLDir->Reread();
        gRDir->Reread();

        mclear_immediately();
    }
    // CTRL-C CTRL-G Escape
    else if(!gCmdLineEscapeKey && (key == 3 || key == 7)
            || gCmdLineEscapeKey && (key == 3 || key ==7 || key==27))
    {
        gCmdLineActive = false;

        cmdline_delete_last_str_if_length_0();

        cmdline_completion_clear();
    }
    else if(key == 10 || key == 13) {       // CTRL-J CTRL-M
        string_obj* str = (string_obj*)vector_item(gCmdLine, gCLCursor);
        cmdline_run(string_c_str(str), string_c_str(str));

        /// コマンドラインヒストリのカーソル位置が最後の場合 ///
        if(gCLCursor == vector_size(gCmdLine)-1) {
            if(string_c_str(str)[0] == ' ' || string_length(str) == 0) {
                cmdline_delete_last_str_if_length_0();
            }
            else {
                add_history(string_c_str(str));
            }
        }
        /// コマンドラインヒストリのカーソル位置が最後から１番目の場合 ///
        else if(gCLCursor == vector_size(gCmdLine)-2) {
            cmdline_delete_last_str_if_length_0();
        }
        /// コマンドラインヒストリのカーソル位置がヒストリを指している場合 ///
        else {
            cmdline_delete_last_str_if_length_0();
            
            vector_add(gCmdLine, string_new(string_c_str(str)));
            add_history(string_c_str(str));
        }

        gCmdLineActive = false;

        cmdline_completion_clear();
    }
    else if(meta == 1 && key == 'h') {    // Meta-h
        /// get editing text and position ///
        int editing_position;
        char cmdname[kAliasMax];
        char editing[kAliasMax];
        char editing_before[kAliasMax];
        VALUE earray;
        bool squote;
        bool dquote;
        bool last_squote;
        bool last_dquote;
        char editing_dir[kAliasMax];
        char editing_file[kAliasMax];

        cmdline_parse(cmdname, editing, editing_before, editing_dir, editing_file, editing_position, earray, squote, dquote, last_squote, last_dquote);

        /// manを表示 ///
        char cmdline[kCmdLineMax];

        sprintf(cmdline, "%%q man %s", cmdname);

        gCmdAfterCmdlineRun = Qnil;
        cmdline_run(cmdline, cmdline);
    }
    else if(key == 18) {    // CTRL-R
        string_obj* cmd_now = (string_obj*)vector_item(gCmdLine, gCLCursor);

        const int len = vector_size(gCmdLine);
        for(int i=len-1; i>=0; i--) {
            if(i != gCLCursor) {
                string_obj* cmd = (string_obj*)vector_item(gCmdLine, i);

                if(strstr(string_c_str(cmd), string_c_str(cmd_now))) {
                    vector_add(gHCandidate, cmd);
                }
            }
        }

        gHistoryScrollTop = 0;
        gHistoryCursor = 0;

        cmdline_completion_clear();
    }
    else if(meta == 1 && key == 'z') {
        gXtermNext = !gXtermNext;
    }
    else if(key == 9) {        // CTRL-I
        cmdline_completion_main();
    }
    else {
        string_obj* str = (string_obj*) vector_item(gCmdLine, gCLCursor);
        char* cstr = string_c_str(str);

        /// ヒストリーを編集するならコピーを末尾に作成 ///
        if(gCLCursor != vector_size(gCmdLine)-1) {
            cmdline_delete_last_str_if_length_0();
            
            vector_add(gCmdLine, string_new(cstr));
            gCLCursor = vector_size(gCmdLine) -1;

            str = (string_obj*)vector_item(gCmdLine, gCLCursor);
            cstr = string_c_str(str);
        }

        /// 文字列を追加 ///
        if(gKanjiCode == kUtf8) {
            if(meta == 0 && !(key >= 0 && key <= 27)) {
                char tmp[128];

                sprintf(tmp, "%c", key);
                string_insert(str, gCursor, tmp);
                gCursor++;
            }
        }
        else {
            if(is_kanji(key)) {
                int meta;
                int key2 = mgetch(&meta);

                char tmp[128];
                sprintf(tmp, "%c%c", (char)key, (char)key2);
                string_insert(str, gCursor, tmp);
                gCursor+=2;
            }
            else if(meta == 0 && ' ' <= key && key <= '~') {
                char tmp[128];
                sprintf(tmp, "%c", key);
                string_insert(str, gCursor, tmp);
                gCursor++;
            }
        }
    }
}

