#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>
#else
#include <term.h>
#endif

#if defined(__SOLARIS__)
#include <sys/stropts.h>
#endif
}

const int kCmdLineMax = 8192;
//const int kCmdLineMax = 1000000; //8192;
const int kAliasMax = 1024;

struct sAlias
{
    char mAlias[kAliasMax];
    int mArgNum;
    char mCommand[kAliasMax];
};

bool gCmdLineActive;
static vector_obj* gCmdLine;
static int gCLCursor;
static int gCursor;

static VALUE gPrograms;
static VALUE gEnvirons;

vector_obj* gCCandidate;
vector_obj* gCExplanation;

int gHistorySize = 10000;

static int gTCur;
vector_obj* gTty;

vector_obj* gAlias;

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

static BOOL sort_name(void* left, void* right)
{
    return strcmp((char*)left, (char*)right) < 0;
}

bool gXterm = false;
bool gXtermNext = false;

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

static int gCursorBefore;
bool gCompletionMenu = false;
static int gCompletionScrollTop = 0;
static int gCompletionCursor = -1;
static bool gCompletionAddSpace;
static bool gCompletionSqort;
static bool gCompletionDqort;
static bool gCompletionLastSqort;
static bool gCompletionLastDqort;
static int gCompletionCandidateLen;
static int gEditingLen;

vector_obj* gHCandidate;
static int gHistoryScrollTop = 0;
static int gHistoryCursor = -1;

void cmdline_read_history_file()
{
    /// clear cmdline ///
    for(int i=0; i<vector_size(gCmdLine); i++) {
        string_delete((string_obj*)vector_item(gCmdLine,i));
    }
    vector_clear(gCmdLine);

    /// read from history file ///
    char* home = getenv("HOME");
    if(home == NULL) {
        return;
    }

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

    if(access(path, F_OK) != 0) {
        creat(path, 0600);
        /*
           char cmd[kCmdLineMax];
           sprintf(cmd, "touch %s/.mhistory", home);
           system(cmd);
         */
    }

    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;

    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) {
        /// get half history ///
        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;
        M(("half_hfile %s", half_hfile));

        /// write half  ///
        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 add_history(char* str)
{
    char* home = getenv("HOME");
    if(home == NULL) {
        gErrMsgCancel = false;
        err_msg("cmdline_run: $HOME is NULL. I can't make history");
        return;
    }

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

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

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

void cmdline_init()
{
    TBEGIN();
    gCmdLine = vector_new(10);
    gCCandidate = vector_new(10);
    gCExplanation = vector_new(10);

    gHCandidate = vector_new(10);

    gTty = 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("");

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

    rb_global_variable(&gPrograms);
    rb_global_variable(&gEnvirons);

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

    gCursorBefore = 0;

    TEND();    
}

void cmdline_rehash()
{
    gPrograms = rb_ary_new();

    /// add program in PATH to gPrograms ///
    char* path = getenv("PATH");
    if(path == NULL) {
        fprintf(stderr, "$PATH is NULL\n");
        exit(1);
    }

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

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

            DIR* dir = opendir(buf);
            if(dir) {
                struct dirent* entry;
                while(entry = readdir(dir)) {
                    char path2[512];
                    sprintf(path2, "%s/%s", buf, entry->d_name);

                    struct stat stat_;
                    memset(&stat_, 0, sizeof(struct stat));
                    stat(path2, &stat_);

                    if(strcmp(entry->d_name, ".") != 0
                            && strcmp(entry->d_name, "..") != 0
                            &&
                            (stat_.st_mode & S_IXUSR
                             ||stat_.st_mode & S_IXGRP
                             ||stat_.st_mode & S_IXOTH))
                    {
                        rb_ary_push(gPrograms, rb_str_new2(entry->d_name));
                    }
                }

                closedir(dir);
            }

            p2 = buf;
        }

        p++;
    }

    *p2 = 0;

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

        closedir(dir);
    }

    /// add alias to gPrograms ///    
    const int len = vector_size(gAlias);
    for(int i=0; i<len; i++) {
        sAlias* alias = (sAlias*)vector_item(gAlias, i);

        rb_ary_push(gPrograms, rb_str_new2(alias->mAlias));
    }

    gPrograms = rb_ary_sort(gPrograms);
}

void cmdline_init_after_rc_file()
{
    cmdline_rehash();        

    gEnvirons = rb_ary_new();
    for(char** p = environ; *p; p++) {
        char env_name[256];

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

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

        char* env = getenv(*p);
        struct stat estat;
        if(stat(env, &estat) >= 0) {
            if(S_ISDIR(estat.st_mode)) {
                *p2++ = '/';
            }
        }
        *p2 = 0;

        rb_ary_push(gEnvirons, rb_str_new2(env_name));
    }

    gEnvirons = rb_ary_sort(gEnvirons);
}

void cmdline_final()
{
    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(gTty); i++) {
        sMasterTty* item = (sMasterTty*)vector_item(gTty, i);

        kill(item->mPID, SIGKILL);
        fprintf(stdout, "PID %d: killed\n", item->mPID);

        FREE(item);
    }

    vector_delete(gTty);

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

#if defined(UNIX98_PTY) || defined(__CYGWIN__)

extern "C"
{
    extern int grantpt (int __fd);
    extern int unlockpt (int __fd);
    extern char *ptsname (int __fd);
}

static int get_slave_pty(int fd, char* name)
{
    strcpy(name, "/dev/pty");

    int slave = -1;

    /* get slave name */
    name = ptsname(fd);
    if(name == NULL) {
        perror("pty:@ptsname()");
        return slave;
    }

    /* open slave pty */
    if((slave = open(name, O_RDWR)) < 0) {
        perror("pty:@open()");
        close(slave);
        return slave;
    }

    return slave;
}
#else
static int get_slave_pty(char* name)
{
    struct group* gptr;
    gid_t gid;
    int slave = -1;

    if((gptr = getgrnam("tty")) != 0) {
        gid = gptr->gr_gid;
    }
    else {
        gid = -1;
    }

    chown(name, getuid(), gid);

    chmod(name, S_IRUSR|S_IWUSR);

    slave = open(name, O_RDWR);

    return slave;
}
#endif

static int get_master_pty(RESULT char name[])
{
    int master = -1;
    int slave_tmp = -1;

#if defined(UNIX98_PTY) || defined(__CYGWIN__)

    strcpy(name, "/dev/ptmx");

    if((master = open(name, O_RDWR)) < 0) {
        perror("ptmx:open()");
        return master;
    }

    /* chown the slave to the user */
    if(grantpt(master) < 0) {
        perror("ptmx:grantpt()");
        close(master);
        return master;
    }

    /* release an internal lock */
    if(unlockpt(master) < 0) {
        perror("ptmx:unlockpt()");
        close(master);
        return master;
    }
#else
    int i, j;

    strcpy(name, "/dev/ptyXX");

    for(i=0; i<16; i++) {
        for(j=0; j<16; j++) {
            name[8] = "pqrstuvwxyzPQRST"[i];
            name[9] = "0123456789abcdef"[j];

            master = open(name, O_RDWR);
            if(master < 0) {
                if(errno == ENOENT) {
                    return (master);
                }
            }
            //            else if(master == 0) {
            //            }
            else {
                name[5] = 't';

                struct stat statbuf;
                stat(name, &statbuf);

                name[5] = 'p';

                if(statbuf.st_mode&S_IROTH && statbuf.st_mode&S_IWOTH) {
                    goto forend;
                }

                close(master);
            }
        }
    }
forend:

    if((master<0) && (i==16) && (j==16)){
        return master;
    }

    name[5] = 't';
#endif
    return master;
}

void sigwinch_handler(int signal)
{
    TBEGIN();

    sMasterTty* current = (sMasterTty*)vector_item(gTty, gTCur);

    struct winsize ws;
    if(ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) < 0) {
        perror("ptypair: could not get window size");
    }
    if(ioctl(current->mFD, TIOCSWINSZ, &ws) < 0) {
        perror("could not restore window size");
    }

    TEND();
}

static void cmdline_move(int cursor)
{
    TBEGIN();

    gCLCursor += cursor;

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

    TEND();    
}

static void cursor_move(int v)
{
    TBEGIN();


    if(gKanjiCode == kUtf) {
        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);
    }

    TEND();    
}

static ALLOCATED string_obj* extend_macro(char* in, RESULT bool* ruby_mode, RESULT bool* output, RESULT bool* quick, RESULT bool* shell_mode, RESULT bool* xterm_mode, RESULT bool* reset_marks,  bool flg)
{
    TBEGIN();

    sFile* cursor = ActiveDir()->CursorFile();
    *ruby_mode = false;
    *output = true;
    *shell_mode = false;
    *quick = false;
    *xterm_mode = false;
    *reset_marks = true;

    string_obj* out = string_new("");

    char* p = in;
    bool sqort = false;
    bool dqort = false;
    while(*p) {
        if(*p == '\'') {
            p++;

            sqort = !sqort;

            string_push_back(out, "\'");
        }
        else if(*p == '"') {
            dqort = !dqort;
            p++;

            string_push_back(out, "\"");
        }
        else if(!sqort && !dqort && *p == '%') {
            p++;

            switch(*p) {
                case '%':
                    p++;

                    if(flg) {
                        string_push_back(out, "%");
                    }
                    else {
                        string_push_back(out, "%%");
                    }
                    break;

                case 'r':
                    p++;

                    if(flg) {
                        *ruby_mode = true;
                    }
                    else {
                        string_push_back(out, "%r");
                    }
                    break;

                case 'q':
                    p++;

                    if(flg) {
                        *output = false;
                    }
                    else {
                        string_push_back(out, "%q");
                    }
                    break;

                case 'Q':
                    p++;

                    if(flg) {
                        *quick = true;
                    }
                    else {
                        string_push_back(out, "%Q");
                    }
                    break;

                case 's':
                    p++;

                    if(flg) {
                        *shell_mode = true;
                    }
                    else {
                        string_push_back(out, "%s");
                    }
                    break;

                case 't':
                    p++;

                    if(flg) {
                        *xterm_mode = true;
                    }
                    else {
                        string_push_back(out, "%t");
                    }
                    break;

                case 'M':
                    p++;

                    if(flg) {
                        *reset_marks = false;
                    }
                    else {
                        string_push_back(out, "%M");
                    }
                    break;

                case 'f': {
                              p++;

                              const int len = strlen(cursor->mName);

                              bool squote_name = false;
                              for(int i=0; i<len; i++) {
                                  if(cursor->mName[i] == '\'') {
                                      squote_name = true;
                                      break;
                                  }
                              }

                              if(squote_name) {
                                  string_push_back(out, "\"");
                                  string_push_back(out, cursor->mName);
                                  string_push_back(out, "\"");
                              }
                              else {
                                  string_push_back(out, "'");
                                  string_push_back(out, cursor->mName);
                                  string_push_back(out, "'");
                              }
                          }
                          break;

                case 'F': {
                              p++;

                              char* name = ActiveDir()->Path();
                              char* name2 = cursor->mName;

                              const int len = strlen(ActiveDir()->Path());
                              const int len2 = strlen(cursor->mName);

                              bool squote_name = false;
                              for(int i=0; i<len; i++) {
                                  if(name[i] == '\'') {
                                      squote_name = true;
                                      break;
                                  }
                              }
                              for(int i=0; i<len2; i++) {
                                  if(name2[i] == '\'') {
                                      squote_name = true;
                                      break;
                                  }
                              }

                              if(squote_name) {
                                  string_push_back(out, "\"");
                                  string_push_back(out, name);
                                  string_push_back(out, name2);
                                  string_push_back(out, "\"");
                              }
                              else {
                                  string_push_back(out, "'");
                                  string_push_back(out, name);
                                  string_push_back(out, name2);
                                  string_push_back(out, "'");
                              }
                          }
                          break;

                case 'x': {
                              p++;

                              char* extension = extname(cursor->mName);
                              const int len = strlen(extension);

                              bool squote_name = false;
                              for(int i=0; i<len; i++) {
                                  if(extension[i] == '\'') {
                                      squote_name = true;
                                      break;
                                  }
                              }

                              if(squote_name) {
                                  string_push_back(out, "\"");
                                  string_push_back(out, extension);
                                  string_push_back(out, "\"");
                              }
                              else {
                                  string_push_back(out, "'");
                                  string_push_back(out, extension);
                                  string_push_back(out, "'");
                              }

                              FREE(extension);
                          }
                          break;

                case 'X': {
                              p++;

                              char* nwe = noextname(cursor->mName);
                              const int len = strlen(nwe);

                              bool squote_name = false;
                              for(int i=0; i<len; i++) {
                                  if(nwe[i] == '\'') {
                                      squote_name = true;
                                      break;
                                  }
                              }

                              if(squote_name) {
                                  string_push_back(out, "\"");
                                  string_push_back(out, nwe);
                                  string_push_back(out, "\"");
                              }
                              else {
                                  string_push_back(out, "'");
                                  string_push_back(out, nwe);
                                  string_push_back(out, "'");
                              }

                              FREE(nwe);
                          }
                          break;

                case 'd': {
                              p++;

                              if(*p == '1') {
                                  p++;

                                  bool slash = false;
                                  if(*p == 's') {
                                      p++;
                                      slash = true;
                                  }

                                  char* tmp = STRDUP(ActiveDir()->Path());
                                  tmp[strlen(tmp)-1] = 0;
                                  char* name = basename(tmp);
                                  FREE(tmp);
                                  const int len = strlen(name);

                                  bool squote_name = false;
                                  for(int i=0; i<len; i++) {
                                      if(name[i] == '\'') {
                                          squote_name = true;
                                          break;
                                      }
                                  }

                                  if(squote_name) {
                                      string_push_back(out, "\"");
                                      string_push_back(out, name);
                                      if(slash) string_push_back(out, "/");
                                      string_push_back(out, "\"");
                                  } else {
                                      string_push_back(out, "'");
                                      string_push_back(out, name);
                                      if(slash) string_push_back(out, "/");
                                      string_push_back(out, "'");
                                  }
                              }
                              else if(*p == '2') {
                                  p++;

                                  bool slash = false;
                                  if(*p == 's') {
                                      p++;
                                      slash = true;
                                  }

                                  char* tmp = STRDUP(SleepDir()->Path());
                                  tmp[strlen(tmp)-1] = 0;
                                  char* name = basename(tmp);
                                  FREE(tmp);
                                  const int len = strlen(name);

                                  bool squote_name = false;
                                  for(int i=0; i<len; i++) {
                                      if(name[i] == '\'') {
                                          squote_name = true;
                                          break;
                                      }
                                  }

                                  if(squote_name) {
                                      string_push_back(out, "\"");
                                      string_push_back(out, name);
                                      if(slash) string_push_back(out, "/");
                                      string_push_back(out, "\"");
                                  }
                                  else {
                                      string_push_back(out, "'");
                                      string_push_back(out, name);
                                      if(slash) string_push_back(out, "/");
                                      string_push_back(out, "'");
                                  }
                              }
                              else {
                                  gErrMsgCancel = false;
                                  err_msg("illeagal macro char");
                              }
                          }
                          break;

                case 'D': {
                              p++;

                              if(*p == '1') {
                                  p++;

                                  bool slash = false;
                                  if(*p == 's') {
                                      p++;
                                      slash = true;
                                  }

                                  const char* name = ActiveDir()->Path();
                                  const int len = strlen(ActiveDir()->Path());

                                  bool squote_name = false;
                                  for(int i=0; i<len; i++) {
                                      if(name[i] == '\'') {
                                          squote_name = true;
                                          break;
                                      }
                                  }

                                  char tmp[PATH_MAX];
                                  strcpy(tmp, name);
                                  tmp[strlen(tmp)-1] = 0;

                                  if(squote_name) {
                                      string_push_back(out, "\"");
                                      string_push_back(out, tmp);
                                      if(slash) string_push_back(out, "/");
                                      string_push_back(out, "\"");
                                  }
                                  else {
                                      string_push_back(out, "'");
                                      string_push_back(out, tmp);
                                      if(slash) string_push_back(out, "/");
                                      string_push_back(out, "'");
                                  }
                              }
                              else if(*p == '2') {
                                  p++;

                                  bool slash = false;
                                  if(*p == 's') {
                                      p++;
                                      slash = true;
                                  }

                                  const char* name = SleepDir()->Path();
                                  const int len = strlen(name);

                                  bool squote_name = false;
                                  for(int i=0; i<len; i++) {
                                      if(name[i] == '\'') {
                                          squote_name = true;
                                          break;
                                      }
                                  }

                                  char tmp[PATH_MAX];
                                  strcpy(tmp, name);
                                  tmp[strlen(tmp)-1] = 0;

                                  if(squote_name) {
                                      string_push_back(out, "\"");
                                      string_push_back(out, tmp);
                                      if(slash) string_push_back(out, "/");
                                      string_push_back(out, "\"");
                                  }
                                  else {
                                      string_push_back(out, "'");
                                      string_push_back(out, tmp);
                                      if(slash) string_push_back(out, "/");
                                      string_push_back(out, "'");
                                  }
                              }
                              else {
                                  gErrMsgCancel = false;
                                  err_msg("illeagal macro char");
                              }

                          }
                          break;

                case 'm':
                          p++;

                          if(*p == '1') {
                              p++;

                              if(flg) {
                                  string_obj* str = ActiveDir()->MarkFilesSQuote();
                                  string_push_back(out, string_c_str(str));
                                  string_delete(str);
                              }
                              else {
                                  string_push_back(out, "%m1");
                              }
                          }
                          else if(*p == '2') {
                              p++;

                              if(flg) {
                                  string_obj* str = SleepDir()->MarkFilesSQuote();
                                  string_push_back(out, string_c_str(str));
                                  string_delete(str);
                              }
                              else {
                                  string_push_back(out, "%m2");
                              }
                          }
                          else {
                              gErrMsgCancel = false; 
                              err_msg("illeagal macro char");
                          }

                          break;

                default:
                          gErrMsgCancel = false; 
                          err_msg("illeagal macro char");
                          break;
            }
        }
        else {
            char tmp[2];
            tmp[0] = *p++;
            tmp[1] = 0;
            string_push_back(out, tmp);
        }
    }

    return out;

    TEND();
}

void cmdline_start(char* cmd, int position)
{
    TBEGIN();

    cmdline_read_history_file();

    gCmdLineActive = true;

    if(gAutoRehash) cmdline_rehash();

    bool ruby_mode;
    bool output;
    bool shell_mode;
    bool quick;
    bool xterm_mode;
    bool reset_marks;
M(("cmd %s", cmd));
    string_obj* cmd2 = extend_macro(cmd, &ruby_mode, &output, &quick, &shell_mode, &xterm_mode, &reset_marks, false);
M(("cmd2 %s", string_c_str(cmd2)));

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

    TEND();
}

int cmdline_select_pty(char* title)
{
    int result = 0;

    int cursor = 0;

    while(1) {
        const int x = 2;
        const int y = 0;

        mbox(y, x, 47, vector_size(gTty) + 2);

        mattron(kCABold);
        mmvprintw(0 + y, 2 + x, title);
        mattroff();

        for(int i=0; i<vector_size(gTty); i++) {
            sMasterTty* master = (sMasterTty*)vector_item(gTty, i);

            if(i == cursor) {
                mattron(kCAReverse);
                mmvprintw(i+1+y, 1+x, "(%02d) %-40s", i+1, master->mName);
                mattroff();
            }
            else {
                mmvprintw(i+1+y, 1+x, "(%02d) %-40s", i+1, master->mName);
            }
        }

        mrefresh();

        int meta;
        int key = mgetch(&meta);
        if(key == 14 || key == KEY_DOWN) {    // CTRL-N
            cursor++;

            if(cursor >= vector_size(gTty)) cursor = 0;
        }
        else if(key == 16 || key == KEY_UP) {    //CTRL-P
            cursor--;

            if(cursor < 0) cursor = vector_size(gTty)-1;
        }
        else if(key == 10 || key == 13) {		// CTRL-J CTRL-M
            result = cursor;
            break;
        }
        else if(key >= '1' && key <= '0'+ vector_size(gTty)) {
            result = key - '1';
            break;
        }
        else {
            result = -1;
            break;
        }
    }

    return result;
}

typedef void (*sighandler_t)(int);

const int kPttyBuf = 8192;

inline void ptty_loop()
{
    struct termios ot, t;

    tcgetattr(STDIN_FILENO, &ot);

    t = ot;

#ifdef ECHOPRT
    t.c_lflag &= ~(ICANON | ISIG | ECHO | ECHOCTL | ECHOE| ECHOK | ECHOKE
            | ECHONL | ECHOPRT);
#else
    t.c_lflag &= ~(ICANON | ISIG | ECHO | ECHOCTL | ECHOE| ECHOK | ECHOKE
            | ECHONL);
#endif

# if defined(IEXTEN) && !defined(__MINT__)
   t.c_lflag &= ~IEXTEN; 
# endif

    t.c_iflag |= IGNBRK;
    t.c_iflag &= ~(IXOFF|IXON);
    t.c_oflag &= ~ONLCR;        // for emacs -nw
    t.c_iflag &= ~ICRNL;    // for lv

//t.c_iflag &= ~IXANY;
//t.c_iflag &= ~IMAXBEL;
//t.c_iflag &= ~IGNBRK;
//t.c_oflag &= ONLCR;
//t.c_cflag &= ~HUPCL;
//t.c_lflag &= ECHOE|ECHOK|ECHOKE|ECHOCTL;
//t.c_iflag &= IXON|IGNPAR;
    

    t.c_cc[VMIN] = 1;
    t.c_cc[VTIME] = 0;
    t.c_cc[VLNEXT] = 0;
    t.c_cc[VDISCARD] = 0;
    tcsetattr(STDIN_FILENO, TCSANOW, &t);

    putp(tigetstr("smkx"));     // for vim

    sMasterTty* current = (sMasterTty*)vector_item(gTty, gTCur);

    sighandler_t old_winch = signal(SIGWINCH, sigwinch_handler);
    signal(SIGINT, SIG_IGN);
    signal(SIGQUIT, SIG_IGN);
    signal(SIGTSTP, SIG_IGN);

    int done = 0;
    do {
        fd_set ready;

        FD_ZERO(&ready);
        FD_SET(STDIN_FILENO, &ready);
        FD_SET(current->mFD, &ready);

        select(current->mFD+1, &ready, NULL, NULL, NULL);

        if(FD_ISSET(current->mFD, &ready)) {
            char buf[kPttyBuf];

            int i = read(current->mFD, buf, kPttyBuf-1);
            if(i > 0) {
                if((current->mOutput + current->mOutputTail + i)
                        >=
                        (current->mOutput + kOutputSize))
                {
                    memmove(current->mOutput
                            , current->mOutput + kOutputSize / 2
                            , kOutputSize / 2);
                    current->mOutputTail -= kOutputSize / 2;
                }

                memcpy(current->mOutput+current->mOutputTail, buf, i);
                current->mOutputTail += i;

                write(STDOUT_FILENO, buf, i);
            } else {
                done = 2;
            }
        }
        else if(FD_ISSET(STDIN_FILENO, &ready)) {
            char buf[kPttyBuf];

            int i = read(STDIN_FILENO, buf, kPttyBuf-1);

            if(i > 0) {
                int ctrl_z = 0;

                for(int j=0; j<i; j++) {
                    if(buf[j] == 26) {            // CTRL-Z
                        done = 1;

                        char buf2[kPttyBuf];
                        int c = 0;

                        for(int k=0; k<i; k++) {
                            if(buf[k] != 26) {
                                buf2[c++] = buf[k];
                            }
                            else {
                                ctrl_z++;
                            }
                        }

                        memcpy(buf, buf2, i);
                        break;
                    }
                }

                write(current->mFD, buf, i-ctrl_z);
            }
            else {
                done = 2;
            }
        }

    } while(!done);

    if(done == 2) {
        for(int i=0; i<vector_size(gTty); i++) {
            sMasterTty* item = (sMasterTty*)vector_item(gTty, i);
            if(item->mFD == current->mFD) {
                close(item->mFD);
                FREE(item);

                vector_erase(gTty, i);
                break;
            }
        }
    }

    tcsetattr(STDIN_FILENO, TCSANOW, &ot);
    //putp(tigetstr("rmkx"));
    putp(tigetstr("sgr0"));
}


static void new_ptty(char* title, char* cmd, bool output)
{
    TBEGIN();

    /// new tty ///
    sMasterTty* master = (sMasterTty*)MALLOC(sizeof(sMasterTty));
    strcpy(master->mName, title);
    master->mOutputTail = 0;

    vector_add(gTty, master);
    gTCur = vector_size(gTty)-1;

    /// go ///
    char* argv[4];
    char buf[kCmdLineMax];

    argv[0] = gShellPath;
    argv[1] = gShellOptEval;
    argv[2] = buf;
    argv[3] = NULL;

    if(output)
        sprintf(argv[2], "( %s ); mfiler -r %d; mhitanykey", cmd, getpid());
    else 
        sprintf(argv[2], "( %s ); mfiler -r %d", cmd, getpid());

    char name[256];

    sMasterTty* current = (sMasterTty*)vector_item(gTty, gTCur);
    current->mFD = get_master_pty(name);

    if(current->mFD < 0) {
        gErrMsgCancel = false; 
        err_msg("new_ptty: could not open master pty");;
        return;
    }

    struct winsize ws;
    if(ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) < 0) {
        gErrMsgCancel = false; 
        err_msg("new_ptty: could not get window size");
        return;
    }

    int pid = fork();
    if(pid < 0) {
        gErrMsgCancel = false; 
        err_msg("new_ptty: fork false");
        return;
    }

    /// child process ///    
    if(pid == 0) {
        int slave;

#if defined(UNIX98_PTY) || defined(__CYGWIN__)
        if((slave = get_slave_pty(current->mFD, name)) < 0) {
#else
        if((slave = get_slave_pty(name)) < 0) {
#endif
            fprintf(stderr, "new_ptty: could not open slave pty. pleas check permission of %s", name);
            exit(1);
        }

        close(current->mFD);

        if(setsid() < 0) {
            perror("could not set new controlling tty");
        }

#if defined(TIOCSCTTY)
        if(ioctl(slave, TIOCSCTTY, NULL)) {
            perror("could not set new controlling tty");
        }
#endif

#if defined(__SOLARIS__)
        ioctl(slave, I_PUSH, "ptem");
        ioctl(slave, I_PUSH, "ldterm");
        ioctl(slave, I_PUSH, "ttcompat");
#endif

        dup2(slave, STDIN_FILENO);
        dup2(slave, STDOUT_FILENO);
        dup2(slave, STDERR_FILENO);

        if(slave > 2) {
            close(slave);
        }

        if(ioctl(STDOUT_FILENO, TIOCSWINSZ, &ws) < 0) {
            perror("could not restore window size");
        }

        execvp(gShellPath, argv);

        exit(1);
    }

    current->mPID = pid;

    ptty_loop();

    TEND();    
}


void cmdline_restore_pty(int n)
{
    mendwin();

//    struct termios t;
//    tcgetattr(STDIN_FILENO, &t);


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

    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(n < 0 || n >= vector_size(gTty)) return;

    gTCur = n;

    sMasterTty* current = (sMasterTty*)vector_item(gTty, gTCur);

    const int m = mgetmaxx() * mgetmaxy() * 3;
    if(current->mOutputTail < m) {
        write(STDOUT_FILENO, current->mOutput, current->mOutputTail);
    }
    else {
        write(STDOUT_FILENO, current->mOutput + current->mOutputTail -m, m);
    }


    /// send SIGWINCH to refresh screen ///    
    struct winsize ws;
    if(ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) < 0) {
        gErrMsgCancel = false; 
        err_msg("new_ptty: could not get window size");
        return;
    }
    ws.ws_col--;
    if(ioctl(current->mFD, TIOCSWINSZ, &ws) < 0) {
        perror("could not restore window size");
    }
    ws.ws_col++;
    if(ioctl(current->mFD, TIOCSWINSZ, &ws) < 0) {
        perror("could not restore window size");
    }



    ptty_loop();

    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);


//    tcsetattr(STDIN_FILENO, TCSANOW, &t);
    minitscr();
}

bool extend_alias(char* cmd, char* aliased_cmd)
{
    /// divide into words ///
    vector_obj* words = vector_new(30);
    int words_max = 0;

    bool sqort = false;
    bool dqort = false;
    char *p = cmd;
    while(*p) {
        if(*p=='\'') {
            p++;

            char tmp[512];
            char* p2 = tmp;
            *p2++ = '\'';

            while(*p) {
                if(*p == '\'') {
                    *p2++ = *p++;
                    break;
                }
                else 
                    *p2++ = *p++;
            }
            *p2 = 0;

            vector_add(words, STRDUP(tmp));
        }
        else if(*p == '"') {
            p++;

            char tmp[512];
            char* p2 = tmp;
            *p2++ = '"';

            while(*p) {
                if(*p == '"') {
                    *p2++ = *p++;
                    break;
                }
                else 
                    *p2++ = *p++;
            }
            *p2 = 0;

            vector_add(words, STRDUP(tmp));
        }
        else if(*p=='"') {
            dqort = !dqort;
        }
        else if(*p=='&') {
            p++;

            if(*p=='&') {
                p++;

                vector_add(words, STRDUP("&&"));
            }
            else if(*p=='>') {
                p++;

                vector_add(words, STRDUP("&>"));
            }
            else {
                vector_add(words, STRDUP("&"));
            }
        }
        else if(*p=='|') {
            p++;

            if(*p=='|') {
                p++;

                vector_add(words, STRDUP("||"));
            }
            else {
                vector_add(words, STRDUP("|"));
            }
        }
        else if(*p=='>') {
            p++;

            if(*p=='>') {
                p++;

                vector_add(words, STRDUP(">>"));
            }
            else if(*p=='|') {
                p++;

                vector_add(words, STRDUP(">|"));
            }
            else if(*p=='&') {
                p++;

                vector_add(words, STRDUP(">&"));
            }
            else {
                vector_add(words, STRDUP(">"));
            }
        }
        else if(*p=='<') {
            p++;

            if(*p=='<') {
                p++;

                vector_add(words, STRDUP("<<"));
            }
            else if(*p=='>') {
                p++;

                vector_add(words, STRDUP("<>"));
            }
            else if(*p=='&') {
                p++;

                vector_add(words, STRDUP("<&"));
            }
            else {
                vector_add(words, STRDUP("<"));
            }
        }
        else if(*p == ';') {
            p++;

            vector_add(words, STRDUP(";"));
        }
        else if(*p == ' ') {
            p++;

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

            vector_add(words, STRDUP(" "));
        }
        else {
            char word[kAliasMax];
            char* p2 = word;
            while(*p && *p!='&' && *p!='|' && *p!='>' && *p!='<'
                    && *p!=';' && *p!=' ' && *p!='\t')
            {
                *p2++ = *p++;
            }
            *p2 = 0;

            vector_add(words, STRDUP(word));
        }
    }

    for(int i=0; i<vector_size(words); i++) {
        M(("words %d [%s]", i, vector_item(words, i)));
    }

    /// extend alias ///
    char* p2 = aliased_cmd;
    for(int i=0; i<vector_size(words); i++) {
        char* word = (char*)vector_item(words, i);

        /// expand alias ///
        bool found = false;

        const int len = vector_size(gAlias);
        for(int j=0; j<len; j++) {
            sAlias* alias = (sAlias*)vector_item(gAlias, j);

            if(strcmp(word, alias->mAlias) == 0) {
                found = true;

                if(alias->mArgNum == 0) {
                    char* p3 = alias->mCommand;
                    while(*p3) {
                        *p2++ = *p3++;
                    }
                }
                else {    
                    char* p3 = alias->mCommand;
                    while(*p3) {
                        if(*p3 == '#') {
                            p3++;

                            const int arg_num = *p3 - '0';
                            p3++;

                            int c = i + 1;
                            int count = 0;
                            char* word2;
                            while(c < vector_size(words)) {
                                word2 = (char*)vector_item(words, c);
                                if(strcmp(word2, " ") == 0) {
                                    c++;
                                }
                                else {
                                    if(count == arg_num) {
                                        break;
                                    }

                                    count++;
                                    c++;
                                }
                            }

                            if(c == vector_size(words)) {
                                for(int k=0; k<vector_size(words); k++) {
                                    FREE(vector_item(words, k));
                                }
                                vector_delete(words);
                                return false;
                            }

                            char aliased_command[kAliasMax];
                            if(!extend_alias(word2, aliased_command)) {
                                for(int k=0; k<vector_size(words); k++) {
                                    FREE(vector_item(words, k));
                                }
                                vector_delete(words);
                                return false;
                            }

                            char* p5 = aliased_command;

                            while(*p5) {
                                *p2++ = *p5++;
                            }
                        }
                        else {
                            *p2++ = *p3++;
                        }
                    }

                    int count = 0;
                    while(i < vector_size(words)) {
                        char* word3 = (char*)vector_item(words, i);
                        if(strcmp(word3, " ") == 0) {
                            i++;
                        }
                        else {
                            if(count == alias->mArgNum) {
                                break;
                            }
                            count++;
                            i++;
                        }
                    }
                }

                break;
            }
        }

        /// no alias ///
        if(!found) {
            char* p3 = word;
            while(*p3) {
                *p2++ = *p3++;
            }
        }
    }

    *p2 = 0;

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

    return true;
}

int msystem(char* line)
{
    int status, save;
    pid_t pid;
    struct sigaction sa, intr, quit;
#ifndef WAITPID_CANNOT_BLOCK_SIGCHLD
    sigset_t block, omask;
#endif

    if (line == NULL) return 0;

    sa.sa_handler = SIG_IGN;
    sa.sa_flags = 0;
    sigemptyset (&sa.sa_mask);

    if (sigaction (SIGINT, &sa, &intr) < 0)
        return -1;
    if (sigaction (SIGQUIT, &sa, &quit) < 0)
    {
        save = errno;
        (void) sigaction (SIGINT, &intr, (struct sigaction *) NULL);
        errno = save;
        return -1;
    }

#ifndef WAITPID_CANNOT_BLOCK_SIGCHLD

    /* SCO 3.2v4 has a bug where `waitpid' will never return if SIGCHLD is
       blocked.  This makes it impossible for `system' to be implemented in
       compliance with POSIX.2-1992.  They have acknowledged that this is a bug
       but I have not seen nor heard of any forthcoming fix.  */

    sigemptyset (&block);
    sigaddset (&block, SIGCHLD);
    save = errno;
    if (sigprocmask (SIG_BLOCK, &block, &omask) < 0)
    {
        if (errno == ENOSYS)
            errno = save;
        else
        {
            save = errno;
            (void) sigaction (SIGINT, &intr, (struct sigaction *) NULL);
            (void) sigaction (SIGQUIT, &quit, (struct sigaction *) NULL);
            errno = save;
            return -1;
        }
    }
# define UNBLOCK sigprocmask (SIG_SETMASK, &omask, (sigset_t *) NULL)
#else
# define UNBLOCK 0
#endif

    pid = fork ();
    if (pid == (pid_t) 0)
    {
        /* Child side.  */
        const char *new_argv[4];
        new_argv[0] = gShellName;
        new_argv[1] = gShellOptEval;
        new_argv[2] = line;
        new_argv[3] = NULL;

        /* Restore the signals.  */
        (void) sigaction (SIGINT, &intr, (struct sigaction *) NULL);
        (void) sigaction (SIGQUIT, &quit, (struct sigaction *) NULL);
        (void) UNBLOCK;

        /* Exec the shell.  */
        (void) execve (gShellPath, (char *const *) new_argv, environ);
        _exit (127);
    }
    else if (pid < (pid_t) 0)
        /* The fork failed.  */
        status = -1;
    else
        /* Parent side.  */
    {
#ifdef    NO_WAITPID
        pid_t child;
        do
        {
            child = wait (&status);
            if (child <= -1 && errno != EINTR)
            {
                status = -1;
                break;
            }
            /* Note that pid cannot be <= -1 and therefore the loop continues
               when wait returned with EINTR.  */
        }
        while (child != pid);
#else
        int n;

        do
            n = waitpid (pid, &status, 0);
        while (n == -1 && errno == EINTR);

        if (n != pid)
            status = -1;
#endif
    }

    save = errno;
    if ((sigaction (SIGINT, &intr, (struct sigaction *) NULL) |
                sigaction (SIGQUIT, &quit, (struct sigaction *) NULL) |
                UNBLOCK) != 0)
    {
        if (errno == ENOSYS)
            errno = save;
        else
            return -1;
    }

    return status;
}

void cmdline_run(char* str, char* title, bool add_history_file)
{
    TBEGIN();
    M(("cmdline_run"));
    M(("[%s]", str));

    /// add history file ///
    if(add_history_file) {
        if(str[0] != ' ') add_history(str);
    }

    /// extend alias ///
    char cmd[kCmdLineMax];
    if(!extend_alias(str, cmd)) {
        gErrMsgCancel = false; 
        err_msg("can't extend alias");
        return;
    }
    M(("extended_alias"));
    M(("[%s]", cmd));

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

    M(("extended_macro"));
    M(("[%s]", cmd2));

    bool tmp1;
    bool tmp2;
    bool tmp3;
    bool tmp4;
    bool tmp5;
    bool tmp6;
    string_obj* title2 = extend_macro(title, &tmp1, &tmp2, &tmp3, &tmp4, &tmp5, &tmp6, 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();

    /// ruby mode ///
    bool flg_screen = strstr(term, "screen") == term || gGnuScreen;
    bool flg_xterm = xterm_mode || (gXterm && !gXtermNext) 
        || (!gXterm && gXtermNext);
    bool flg_pty = gPty;
    if(ruby_mode) {
        M(("ruby_mode"));    
        if(!quick) {
            mendwin();

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

        rb_eval_string(string_c_str(cmd2));
        //        int error;
        //        rb_eval_string_protect(string_c_str(cmd2), &error);

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

            minitscr();

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

            mclear();
        }
    }

    /// screen terminal ///
    else if(flg_screen && !shell_mode && !quick) 
    {
        M(("screen_terminal"));
        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];
            sprintf(tmp, " ); mfiler -r %d; mhitanykey", getpid());
            string_push_back(cmd2, tmp);
        }
        else {
            string_insert(cmd2, 0, "( ");
            char tmp[256];
            sprintf(tmp, " ); mfiler -r %d", getpid());
            string_push_back(cmd2, tmp);
        }

        argv[5] = string_c_str(cmd2);
        pid_t pid = fork();
        if(pid == -1) {
            fprintf(stderr, "fork failed");
            exit(1);
        }

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

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

    /// xterm mode ///
    else if(flg_xterm && !shell_mode && !quick) {
        M(("xterminal"));

        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++;

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

                while(*p == ' ') p++;
            }
            else if(*p == 0) {
                if(p == RSTRING(gXtermOptExtra)->ptr) {
                    break;
                }

                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;
        argv[7+arg_num] = NULL;

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

        argv[6+arg_num] = string_c_str(cmd2);

        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);
            }
        }
    }
    else if(flg_pty && !shell_mode && !quick) {
        M(("ptty terminal"));
        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);

//        struct termios t;

        if(!quick) {
            mendwin();

//            tcgetattr(STDIN_FILENO, &t);

            mclear_immediately();
            mmove_immediately(0, 0);
            fflush(stdout);
        }
        else {
            mclear();
            mrefresh();
        }

        /*
        // I don't know why. but emacs -nw needs this
        initscr();
        endwin();
        clear();
        refresh();
         */


        new_ptty(string_c_str(title2), string_c_str(cmd2), output);

        if(!quick) {
//            tcsetattr(STDIN_FILENO, TCSANOW, &t);

            minitscr();
            mclear();
        }

        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);
    }

    /// shell mode ///
    else  {
        M(("shell_mode"));  
        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);

        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(msystem(string_c_str(cmd2)) == -1) {
                gErrMsgCancel = false; 
                err_msg("can't evaluate with system()");
            }
        }
        else {
            if(msystem(string_c_str(cmd2)) == -1) {
                gErrMsgCancel = false; 
                err_msg("can't evaluate with system()");
            }
        }

        if(!quick) {
            minitscr();
            mclear();
        }
        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);
    }

    string_delete(title2);
    string_delete(cmd2);

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

    gXtermNext = false;

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

    TEND();
}

static void completion_clear()
{
    for(int i=0; i<vector_size(gCCandidate); i++) {
        FREE(vector_item(gCCandidate, i));
    }
    vector_clear(gCCandidate);
    for(int i=0; i<vector_size(gCExplanation); i++) {
        FREE(vector_item(gCExplanation, i));
    }
    vector_clear(gCExplanation);
}

void cmdline_completion(VALUE all_candidate, char* editing, bool sqort, bool dqort, bool last_sqort, bool last_dqort, bool add_space)
{
    TBEGIN();

    gCompletionAddSpace = add_space;
    gCompletionSqort = sqort;
    gCompletionDqort = dqort;
    gCompletionLastSqort = last_sqort;
    gCompletionLastDqort = last_dqort;

    gEditingLen = strlen(editing);

    completion_clear();

    if(strcmp(editing, "") == 0) {
        for(int i=0; i<RARRAY(all_candidate)->len; i++) {
            vector_add(gCCandidate
                    , STRDUP(RSTRING(rb_ary_entry(all_candidate, i))->ptr));
        }
    }
    else {
        for(int i=0; i<RARRAY(all_candidate)->len; i++) {
            Check_Type(rb_ary_entry(all_candidate, i), T_STRING);
            char* item = RSTRING(rb_ary_entry(all_candidate, i))->ptr;


            //            err_msg("%s %s", item, editing);


            if(strstr(item, editing) == item) {
                vector_add(gCCandidate, STRDUP(item));
            }
        }
    }

    if(vector_size(gCCandidate) > 0) {
        int l = strlen(editing);

        const int len = strlen((char*)vector_item(gCCandidate, 0));
        char c = 0;
        while(l < len) {
            bool same = true;

            for(int i= 0; i<vector_size(gCCandidate); i++) {
                char* item = (char*)vector_item(gCCandidate, i);

                if(item[l] != ((char*)vector_item(gCCandidate, 0))[l]) {
                    same = false;
                }
            }

            if(same) {
                char tmp[256];
                c = ((char*)vector_item(gCCandidate, 0))[l];
                if(!gCompletionSqort && !gCompletionDqort && (c==' ' || c=='!' || c=='*' || c=='>' || c=='&' || c=='~' || c=='#' || c =='$' || c=='(' || c==')' || c=='\\' || c=='|' || c=='[' || c==']' || c=='{' || c=='}' || c==';' || c=='\'' || c=='"' || c=='<' || c=='>' || c=='?'))
                {
                    sprintf(tmp, "\\%c", c);
                    string_insert((string_obj*)vector_item(gCmdLine, gCLCursor)
                            , gCursor, tmp);
                    gCursor+=2;

                    gEditingLen++;
                }
                else {
                    sprintf(tmp, "%c", c);
                    string_insert((string_obj*)vector_item(gCmdLine, gCLCursor)
                            , gCursor, tmp);
                    gCursor++;

                    gEditingLen++;
                }
            }
            else {
                break;
            }

            l++;
        }

        if(l == len && vector_size(gCCandidate) == 1) {
            if(c != '/') {
                if(gCompletionSqort && !gCompletionLastSqort) {
                    char tmp[128];
                    sprintf(tmp, "' ");
                    string_insert((string_obj*)
                            vector_item(gCmdLine, gCLCursor), gCursor, tmp);
                    gCursor+=2;
                }
                else if(gCompletionDqort && !gCompletionLastDqort) {
                    char tmp[128];
                    sprintf(tmp, "\" ");
                    string_insert((string_obj*)vector_item(gCmdLine, gCLCursor)
                            , gCursor, tmp);
                    gCursor+=2;
                }
                else if(gCompletionAddSpace && !gCompletionLastSqort
                        && !gCompletionLastDqort)
                {
                    char tmp[128];
                    sprintf(tmp, " ");
                    string_insert((string_obj*)vector_item(gCmdLine, gCLCursor)
                            , gCursor, tmp);
                    gCursor++;
                }
            }

            completion_clear();
        }
    }

    TEND();    
}

void cmdline_completion2(VALUE all_candidate, char* editing, bool sqort, bool dqort, bool last_sqort, bool last_dqort, bool add_space, int candidate_len)
{
    TBEGIN();
    gCompletionAddSpace = add_space;
    gCompletionSqort = sqort;
    gCompletionDqort = dqort;
    gCompletionLastSqort = last_sqort;
    gCompletionLastDqort = last_dqort;
    gCompletionCandidateLen = candidate_len;
    gEditingLen = strlen(editing);

    completion_clear();

    if(strcmp(editing, "") == 0) {
        for(int i=0; i<RARRAY(all_candidate)->len; i++) {
            VALUE array = rb_ary_entry(all_candidate, i);

            Check_Type(array, T_ARRAY);

            if(RARRAY(array)->len != 2) {
                gErrMsgCancel = false; 
                err_msg("evalid candidate type in completion2");
                return;
            }

            VALUE candidate = rb_ary_entry(array, 0);
            VALUE explanation = rb_ary_entry(array, 1);

            Check_Type(candidate, T_STRING);
            Check_Type(explanation, T_STRING);

            vector_add(gCCandidate, STRDUP(RSTRING(candidate)->ptr));
            vector_add(gCExplanation, STRDUP(RSTRING(explanation)->ptr));
        }
    }
    else {
        for(int i=0; i<RARRAY(all_candidate)->len; i++) {
            VALUE array = rb_ary_entry(all_candidate, i);

            Check_Type(array, T_ARRAY);

            if(RARRAY(array)->len != 2) {
                gErrMsgCancel = false; 
                err_msg("evalid candidate type in completion2");
                return;
            }

            VALUE candidate = rb_ary_entry(array, 0);
            VALUE explanation = rb_ary_entry(array, 1);

            Check_Type(candidate, T_STRING);
            Check_Type(explanation, T_STRING);

            if(strstr(RSTRING(candidate)->ptr, editing) == RSTRING(candidate)->ptr)
            {
                vector_add(gCCandidate, STRDUP(RSTRING(candidate)->ptr));
                vector_add(gCExplanation, STRDUP(RSTRING(explanation)->ptr));
            }
        }
    }

    if(vector_size(gCCandidate) > 0) {
        int l = strlen(editing);

        const int len = strlen((char*)vector_item(gCCandidate, 0));
        char c = 0;
        while(l < len) {
            bool same = true;

            for(int i= 0; i<vector_size(gCCandidate); i++) {
                char* item = (char*)vector_item(gCCandidate, i);

                if(item[l] != ((char*)vector_item(gCCandidate, 0))[l]) {
                    same = false;
                }
            }

            if(same) {
                char tmp[256];
                c = ((char*)vector_item(gCCandidate, 0))[l];
                if(!gCompletionSqort && !gCompletionDqort && (c==' ' || c=='!' || c=='*' || c=='>' || c=='&' || c=='~' || c=='#' || c =='$' || c=='(' || c==')' || c=='\\' || c=='|' || c=='[' || c==']' || c=='{' || c=='}' || c==';' || c=='\'' || c=='"' || c=='<' || c=='>' || c=='?'))
                {
                    sprintf(tmp, "\\%c", c);
                    string_insert((string_obj*)vector_item(gCmdLine, gCLCursor)
                            , gCursor, tmp);
                    gCursor+=2;

                    gEditingLen++;
                }
                else {
                    sprintf(tmp, "%c", c);
                    string_insert((string_obj*)vector_item(gCmdLine, gCLCursor)
                            , gCursor, tmp);
                    gCursor++;

                    gEditingLen++;
                }
            }
            else {
                break;
            }

            l++;
        }

        if(l == len && vector_size(gCCandidate) == 1 && c != '/') {
            if(c != '/') {
                if(gCompletionSqort && !gCompletionLastSqort) {
                    char tmp[128];
                    sprintf(tmp, "' ");
                    string_insert((string_obj*)vector_item(gCmdLine, gCLCursor)
                            , gCursor, tmp);
                    gCursor+=2;
                }
                else if(gCompletionDqort && !gCompletionLastDqort) {
                    char tmp[128];
                    sprintf(tmp, "\" ");
                    string_insert((string_obj*)vector_item(gCmdLine, gCLCursor)
                            , gCursor, tmp);
                    gCursor+=2;
                }
                else if(gCompletionAddSpace && !gCompletionLastSqort
                        && !gCompletionLastDqort)
                {
                    char tmp[128];
                    sprintf(tmp, " ");
                    string_insert((string_obj*)vector_item(gCmdLine, gCLCursor)
                            , gCursor, tmp);
                    gCursor++;
                }
            }

            completion_clear();
        }
    }

    TEND();    
}

static void default_completion(char* editing_dir, char* editing_file, char* editing, bool sqort, bool dqort, bool last_sqort, bool last_dqort, int editing_position)
{
    M(("default completion"));
    /// exist editing_dir ///
    if(strcmp(editing_dir, "") != 0) {
        /// starting environ variable ///
        if(editing_dir[0] == '$') {
            char new_editing_dir[kCmdLineMax];
            char env_name[256];

            char *p = editing_dir;
            p++;

            char *p2 = env_name;

            while(*p != '/') {
                *p2++ = *p++;                
            }
            *p2 = 0;

            strcpy(new_editing_dir, getenv(env_name));
            strcat(new_editing_dir, p);

            M(("new_editing_dir %s", new_editing_dir));

            strcpy(editing_dir, new_editing_dir);
        }
        else if(editing_dir[0] == '~') {
            char new_editing_dir[kCmdLineMax];

            char *p = editing_dir;
            p++;

            strcpy(new_editing_dir, getenv("HOME"));
            strcat(new_editing_dir, p);

            M(("new_editing_dir %s", new_editing_dir));

            strcpy(editing_dir, new_editing_dir);
        }

        DIR* dir = opendir(editing_dir);
        VALUE files = rb_ary_new();

        if(dir) {
            struct dirent* entry;
            while(entry = readdir(dir)) {
                if(strcmp(entry->d_name, ".") != 0
                        && strcmp(entry->d_name, "..") != 0)
                {
                    char buf[PATH_MAX];

                    struct stat _stat;
                    strcpy(buf, editing_dir);
                    strcat(buf, entry->d_name);
                    stat(buf, &_stat);

                    strcpy(buf, entry->d_name);
                    if(S_ISDIR(_stat.st_mode)) {
                        strcat(buf, "/");
                    }

                    rb_ary_push(files, rb_str_new2(buf));
                }
            }

            closedir(dir);

            files = rb_ary_sort(files);
            cmdline_completion(files, editing_file, sqort, dqort, last_sqort, last_dqort, true);
        }
    }
    /// not exist editing_dir ///
    else {
        M(("not exist editing_dir"));            
        /// editing enverion ///
        if(editing[0] == '$') {
            cmdline_completion(gEnvirons, editing, sqort, dqort, last_sqort, last_dqort, true);
        }
        else if(editing[0] == '~') {
            VALUE tilda = rb_ary_new();
            rb_ary_push(tilda, rb_str_new2("~/"));

            cmdline_completion(tilda, editing, sqort, dqort, last_sqort, last_dqort, true);
        }
        else {
            /// editing program name ///
            if(editing_position == 0) {
                cmdline_completion(gPrograms, editing, sqort, dqort, last_sqort, last_dqort, true);
            }

            /// editing file name ///
            else {
                DIR* dir = opendir(".");
                VALUE files = rb_ary_new();
M(("ZZZ %d", dir));

                if(dir) {
                    struct dirent* entry;
                    while(entry = readdir(dir)) {
                        if(strcmp(entry->d_name, ".") != 0
                                && strcmp(entry->d_name, "..") != 0)
                        {
                            char buf[PATH_MAX];

                            struct stat _stat;
                            strcpy(buf, "./");
                            strcat(buf, entry->d_name);
                            stat(buf, &_stat);

                            strcpy(buf, entry->d_name);
                            if(S_ISDIR(_stat.st_mode)) {
                                strcat(buf, "/");
                            }

                            rb_ary_push(files, rb_str_new2(buf));
                        }
                    }

                    closedir(dir);

        /*                                
                      const int len = vector_size(gAlias);
                      for(int i=0; i<len; i++) {
                      sAlias* alias = (sAlias*)vector_item(gAlias, i);

                      rb_ary_push(files, rb_str_new2(alias->mAlias));
                      }
         */        
        files = rb_ary_sort(files);
                    cmdline_completion(files, editing_file, sqort, dqort, last_sqort, last_dqort, true);
                }
            }
        }
    }
}

void parse(char* cmdname, char* editing, char* editing_before, char* editing_dir, char* editing_file, int& editing_position, VALUE& earray, bool& sqort, bool& dqort, bool& last_sqort, bool& last_dqort)
{
    char* cmdline = string_c_str(
            (string_obj*)vector_item(gCmdLine, gCLCursor));

    bool qort = false;
    int i = 0;
    char* e = editing;

    *e = 0;
    editing_position = 0;
    strcpy(cmdname, "");
    strcpy(editing_before, "");
    sqort = false;
    dqort = false;
    last_sqort = false;
    last_dqort = false;
    earray = rb_ary_new();

    while(i < gCursor && cmdline[i] == ' ') i++;

    while(i < gCursor) {
        if(!sqort && !dqort && cmdline[i]=='\\') {
            i++;

            qort = true;
        }
        else if(!dqort && !qort && cmdline[i]=='\'') {
            i++;

            sqort = !sqort;
        }
        else if(!sqort && !qort && cmdline[i]=='"') {
            i++;

            dqort = !dqort;
        }
        else if(!sqort && !dqort && !qort && cmdline[i] == ' ') {
            i++;

            *e = 0;
            rb_ary_push(earray, rb_str_new2(editing));
            strcpy(editing_before, editing);

            if(strcmp(editing, "do") == 0 || strcmp(editing, "then") == 0
                    || strcmp(editing, "else") == 0)
            {
                editing_position = 0;
            }
            else {
                if(editing[0] != '%' && editing[0] != '-') {
                    if(editing_position == 0) strcpy(cmdname, editing);
                    editing_position++;
                }
            }

            e = editing;

            while(i < gCursor && cmdline[i] == ' ') i++;
        }
        else if(!qort && !sqort && !dqort && (cmdline[i]==',' || cmdline[i]=='>'|| cmdline[i]=='<'))
        {
            i++;

            *e = 0;
            rb_ary_push(earray, rb_str_new2(editing));
            strcpy(editing_before, editing);

            if(editing[0] != '%' && editing[0] != '-') {
                if(editing_position == 0) strcpy(cmdname, editing);
                editing_position++;
            }

            e = editing;

            while(i < gCursor && cmdline[i] == ' ') i++;
        }
        else if(!qort && !sqort && !dqort && (cmdline[i] == '|' || cmdline[i] == ';' || cmdline[i] == '&' || cmdline[i]=='(' || cmdline[i]=='`'))
        {
            i++;

            *e = 0;
            rb_ary_push(earray, rb_str_new2(editing));
            strcpy(editing_before, editing);

            e = editing;

            strcpy(cmdname, "");                
            editing_position = 0;

            while(i < gCursor && cmdline[i] == ' ') i++;
        }
        else {
            *e++ = cmdline[i];
            i++;

            qort = false;
        }
    }
    *e = 0;
    rb_ary_push(earray, rb_str_new2(editing));
    //strcpy(editing_before, editing);
    if(editing[0] != '%' && editing[0] != '-')
    {
        if(editing_position == 0) strcpy(cmdname, editing);
    }

    if(strlen(cmdline) >= gCursor && cmdline[gCursor] == '\'') {
        last_sqort = true;
    }

    if(strlen(cmdline) >= gCursor && cmdline[gCursor] == '"') {
        last_dqort = true;
    }

    /// divide editing into dir part and file part ///
    strcpy(editing_dir, "");
    strcpy(editing_file, "");

    e = editing + strlen(editing);
    while(e >= editing) {
        if(*e == '/') {
            memcpy(editing_dir, editing, e - editing + 1);
            editing_dir[e-editing + 1] = 0;

            memcpy(editing_file, e + 1, editing + strlen(editing) - e);
            editing_file[editing + strlen(editing) -e] = 0;
            break;
        }

        e--;
    }

    if(strcmp(editing_dir, "") == 0) {
        strcpy(editing_file, editing);
    }
}

void cmdline_input(int meta, int key)
{
    TBEGIN();

    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;
        }
    }

    /// get editing text and position ///
    int editing_position;
    char cmdname[kAliasMax];
    char editing[kAliasMax];
    char editing_before[kAliasMax];
    VALUE earray;
    bool sqort;
    bool dqort;
    bool last_sqort;
    bool last_dqort;
    char editing_dir[kAliasMax];
    char editing_file[kAliasMax];

    parse(cmdname, editing, editing_before, editing_dir, editing_file, editing_position, earray, sqort, dqort, last_sqort, last_dqort);

    M(("cmdname editing %s editing_dir %s editing_file %s editing_before %s editing_position %d", cmdname, editing, editing_dir, editing_file, editing_before, editing_position));

    /// go ///
    if(key == 6 || key == KEY_RIGHT) {    // CTRL-F
        if(gKanjiCode == kUtf) {
            cursor_move(1);
        }
        else {
            if(kanji[gCursor])
                cursor_move(2);
            else
                cursor_move(1);
        }

        completion_clear();
    }
    else if(key == 2 || key == KEY_LEFT) {    // CTRL-B
        if(gKanjiCode == kUtf) {
            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);
                }
            }
        }

        completion_clear();
    }
    else if(key == 1 || key == KEY_HOME) {    // CTRL-A
        cursor_move(-999);

        completion_clear();
    }
    else if(key == 5 || key == KEY_END) {    // CTRL-E
        cursor_move(999);

        completion_clear();
    }
    else if(key == 4) {    // CTRL-D
        string_obj* str = (string_obj*)vector_item(gCmdLine, gCLCursor);
        char* str2 = string_c_str(str);
        if(string_length(str) == 0) {
            gCmdLineActive = false;
        }
        else {
            if(gCursor < string_length(str)) {
                if(gKanjiCode == kUtf) {
                    int utfpos = str_pointer2utfpos(str2, str2 + gCursor);
                    char* next_point = str_utfpos2pointer(str2, utfpos+1);

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

        completion_clear();
    }
    // CTRL-H
    else if(key == 8 || key == KEY_BACKSPACE || key == 127 || key == KEY_DC) {
        if(gCursor > 0) {
            string_obj* str = (string_obj*) vector_item(gCmdLine, gCLCursor);
            char* str2 = string_c_str(str);

            if(gKanjiCode == kUtf) {
                int utfpos = str_pointer2utfpos(str2, str2 + gCursor);
                char* before_point = str_utfpos2pointer(str2, utfpos-1);
                int new_cursor = before_point-str2;

                string_erase(str, before_point - str2, (str2 + 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);
                    }
                }
            }
        }

        completion_clear();
    }
    else if(key == 21) {    // CTRL-U
        string_obj* str = (string_obj*)vector_item(gCmdLine, gCLCursor);

        string_put(str, "");

        gCursor = 0;

        completion_clear();
    }
    else if(key == 23) {     // CTRL-W
        if(gCursor > 0) {
            string_obj* str = (string_obj*) vector_item(gCmdLine, gCLCursor);

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

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

            gCursor = pos+1;
        }

        completion_clear();
    }
    else if(meta==1 && key == 'd') {     // Meta-d
        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] == '/')) {
                pos++;
            }
            while(s[pos]!=0 && s[pos] != ' ' && s[pos] != '/') {
                pos++;
            }

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

        completion_clear();
    }
    else if(meta==1 && key == 'b') {     // META-b
        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] == '/')) {
                pos--;
            }
            while(pos>=0 && s[pos] != ' ' && s[pos] != '/') {
                pos--;
            }

            gCursor = pos+1;
        }

        completion_clear();
    }
    else if(meta==1 && key == 'f') {     // META-f
        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] == '/')) {
                pos++;
            }
            while(s[pos]!=0 && s[pos] != ' ' && s[pos] != '/') {
                pos++;
            }

            gCursor = pos;
        }

        completion_clear();
    }
    else if(key == 11) {    // CTRL-K
        string_obj* str = (string_obj*)vector_item(gCmdLine, gCLCursor);
        string_erase(str, gCursor, string_length(str)-gCursor);

        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);

        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);

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

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

        string_obj* str = (string_obj*)vector_item(gCmdLine, gCLCursor);

        if(string_length(str) > 0 && gCLCursor == vector_size(gCmdLine)-1) {
            add_history(string_c_str(str));
        }

        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), true);

        gCmdLineActive = false;

        completion_clear();
    }
    else if(meta == 1 && key == 'h') {    // Meta-h
        char cmdline[kCmdLineMax];

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

        cmdline_run(cmdline, cmdline, false);
    }
    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;

        completion_clear();
    }
    else if(key == 9) {        // CTRL-I
        if(vector_size(gCCandidate) > 0 && gCursorBefore == gCursor)
        {
            gCompletionMenu = true;
            gCompletionScrollTop = 0;
            gCompletionCursor = 0;
        }
        else {
            gCompletionScrollTop = 0;
        
            /// goto ruby level ///
            int ret_num = 1;
            if(editing_position != 0) {

                VALUE ret = rb_funcall(rb_cObject, rb_intern("completion_hook"),11, rb_str_new2(editing), rb_str_new2(editing_dir), rb_str_new2(editing_file), rb_str_new2(editing_before), earray, rb_str_new2(cmdname), INT2NUM(editing_position), sqort ?Qtrue:Qfalse, dqort ?Qtrue:Qfalse, last_sqort?Qtrue:Qfalse, last_dqort?Qtrue:Qfalse);

                Check_Type(ret, T_FIXNUM);

                ret_num = FIX2INT(ret);
            }

            /// default completion ///
            if(editing[0] == '%' && !sqort && !dqort) {
                VALUE array = rb_ary_new();

                VALUE array2 = rb_ary_new();
                rb_ary_push(array2, rb_str_new2("%f"));
                rb_ary_push(array2, rb_str_new2("file name under cursor"));
                rb_ary_push(array, array2);

                array2 = rb_ary_new();
                rb_ary_push(array2, rb_str_new2("%F"));
                rb_ary_push(array2, rb_str_new2("full path name under cursor"));
                rb_ary_push(array, array2);

                array2 = rb_ary_new();
                rb_ary_push(array2, rb_str_new2("%x"));
                rb_ary_push(array2, rb_str_new2("extension of cursor file"));
                rb_ary_push(array, array2);

                array2 = rb_ary_new();
                rb_ary_push(array2, rb_str_new2("%X"));
                rb_ary_push(array2, rb_str_new2("file name under cursor without extension"));
                rb_ary_push(array, array2);

                array2 = rb_ary_new();
                rb_ary_push(array2, rb_str_new2("%d1"));
                rb_ary_push(array2, rb_str_new2("active directory name"));
                rb_ary_push(array, array2);

                array2 = rb_ary_new();
                rb_ary_push(array2, rb_str_new2("%d1s"));
                rb_ary_push(array2, rb_str_new2("active directory name with slash"));
                rb_ary_push(array, array2);

                array2 = rb_ary_new();
                rb_ary_push(array2, rb_str_new2("%d2"));
                rb_ary_push(array2, rb_str_new2("non active directory name"));
                rb_ary_push(array, array2);

                array2 = rb_ary_new();
                rb_ary_push(array2, rb_str_new2("%d2s"));
                rb_ary_push(array2, rb_str_new2("non active directory name with slash"));
                rb_ary_push(array, array2);

                array2 = rb_ary_new();
                rb_ary_push(array2, rb_str_new2("%D1"));
                rb_ary_push(array2, rb_str_new2("active directory full path"));
                rb_ary_push(array, array2);

                array2 = rb_ary_new();
                rb_ary_push(array2, rb_str_new2("%D1s"));
                rb_ary_push(array2, rb_str_new2("active directory full path with slash"));
                rb_ary_push(array, array2);

                array2 = rb_ary_new();
                rb_ary_push(array2, rb_str_new2("%D2"));
                rb_ary_push(array2, rb_str_new2("non active directory full path"));
                rb_ary_push(array, array2);

                array2 = rb_ary_new();
                rb_ary_push(array2, rb_str_new2("%D2s"));
                rb_ary_push(array2, rb_str_new2("non active directory full path with slash"));
                rb_ary_push(array, array2);

                array2 = rb_ary_new();
                rb_ary_push(array2, rb_str_new2("%m1"));
                rb_ary_push(array2, rb_str_new2("marked files in active directory"));
                rb_ary_push(array, array2);

                array2 = rb_ary_new();
                rb_ary_push(array2, rb_str_new2("%m2"));
                rb_ary_push(array2, rb_str_new2("marked files in non active directory"));
                rb_ary_push(array, array2);

                array2 = rb_ary_new();
                rb_ary_push(array2, rb_str_new2("%r"));
                rb_ary_push(array2, rb_str_new2("command line will be sent to ruby interpreter"));
                rb_ary_push(array, array2);

                array2 = rb_ary_new();
                rb_ary_push(array2, rb_str_new2("%s"));
                rb_ary_push(array2, rb_str_new2("command line will be sent to shell interpreter in mfiler's screen"));
                rb_ary_push(array, array2);

                array2 = rb_ary_new();
                rb_ary_push(array2, rb_str_new2("%t"));
                rb_ary_push(array2, rb_str_new2("command line will be sent to X terminal"));
                rb_ary_push(array, array2);

                array2 = rb_ary_new();
                rb_ary_push(array2, rb_str_new2("%q"));
                rb_ary_push(array2, rb_str_new2("don't wait when command line excution finished"));
                rb_ary_push(array, array2);

                array2 = rb_ary_new();
                rb_ary_push(array2, rb_str_new2("%Q"));
                rb_ary_push(array2, rb_str_new2("don't wait and refresh screen when command line excution finished"));
                rb_ary_push(array, array2);

                array2 = rb_ary_new();
                rb_ary_push(array2, rb_str_new2("%%"));
                rb_ary_push(array2, rb_str_new2("extend %"));
                rb_ary_push(array, array2);

                array2 = rb_ary_new();
                rb_ary_push(array2, rb_str_new2("%M"));
                rb_ary_push(array2, rb_str_new2("don't clear marks when command excution finished"));
                rb_ary_push(array, array2);

                cmdline_completion2(array, editing, sqort, dqort, last_sqort, last_dqort, true, 5);
            }
            else if(ret_num == 2) {
                completion_clear();
            }
            else if(ret_num == 1) {
                if(strcmp(editing_dir, "") != 0)
                    default_completion(editing_dir, editing_file, editing, sqort, dqort, last_sqort, last_dqort, editing_position);
                else 
                    cmdline_completion(gPrograms, editing, sqort, dqort, last_sqort, last_dqort, true);
            }
            else if(ret_num == 0) {
                default_completion(editing_dir, editing_file, editing, sqort, dqort, last_sqort, last_dqort, editing_position);
            }

            gCursorBefore = gCursor;
        }
    }
    else {
        if(gKanjiCode == kUtf) {
            char tmp[128];

            sprintf(tmp, "%c", key);
            string_obj* str = (string_obj*)vector_item(gCmdLine, gCLCursor);
            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_obj* str = (string_obj*)vector_item(gCmdLine, gCLCursor);
                string_insert(str, gCursor, tmp);
                gCursor+=2;
            }
            else if(meta == 0 && ' ' <= key && key <= '~') {
                char tmp[128];
                sprintf(tmp, "%c", key);
                string_obj* str = (string_obj*)vector_item(gCmdLine, gCLCursor);
                string_insert(str, gCursor, tmp);
                gCursor++;
            }
        }
    }

    TEND();
}

void cmdline_completion_view()
{
    TBEGIN();

    const int maxx = mgetmaxx();
    const int maxy = mgetmaxy();

    if(vector_size(gCExplanation) == 0) {   
        int row = 0;
        int line = 0;
        for(int i= gCompletionScrollTop; i<vector_size(gCCandidate); i++) {
            char* item = (char*)vector_item(gCCandidate, i);

            if(row == 4) {
                row = 0;
                line++;
            }

            if(line >= maxy-2) break;

            char tmp[256];
            if(gKanjiCode == kUtf) 
                str_cut2(item, maxx/4-1, tmp, 256);
            else
                cut(item, tmp, maxx/4-1);

            if(gCompletionCursor == i) mattron(kCAReverse);
            mmvprintw(line, row * (maxx/4), "%s", tmp);
            if(gCompletionCursor == i) mattroff();
            row++;
        }
    }
    else {
        int line = 0;
        for(int i= gCompletionScrollTop; i<vector_size(gCCandidate); i++) {
            char* item = (char*)vector_item(gCCandidate, i);
            char* explanation = (char*)vector_item(gCExplanation, i);

            char tmp[256];
            sprintf(tmp, "%%-%ds %%s", gCompletionCandidateLen);

            char buf[256];
            sprintf(buf, tmp, item, explanation);

            char tmp2[256];
            if(gKanjiCode == kUtf)
                str_cut2(buf, maxx-1, tmp2, 256);
            else
                cut(buf, tmp2, maxx-1);

            if(gCompletionCursor == i) mattron(kCAReverse);        
            mmvprintw(line, 0, "%s", tmp2);
            if(gCompletionCursor == i) mattroff();
            line++;

            if(line >= maxy-2) break;
        }
    }

    TEND();
}

void cmdline_completion_menu_input(int meta, int key)
{
    TBEGIN();
    if(key == 14 || key == KEY_DOWN) {    // CTRL-N
        if(vector_size(gCExplanation) == 0) 
            gCompletionCursor+=4;
        else
            gCompletionCursor++;
    }
    else if(key == 16 || key == KEY_UP) {    //CTRL-P
        if(vector_size(gCExplanation) == 0)
            gCompletionCursor-=4;
        else
            gCompletionCursor--;
    }
    else if(key == 6 || key == KEY_RIGHT) {    // CTRL-F
        gCompletionCursor++;
    }
    else if(key == 2 || key == KEY_LEFT) {    // CTRL-B
        gCompletionCursor--;
    }
    else if(key == 9) {    // CTRL-I
        gCompletionCursor++;
    }
    else if(key == 10 || key == 13) {		// CTRL-J CTRL-M
        char* candidate = (char*)vector_item(gCCandidate, gCompletionCursor);

        char tmp[256];
        char* p = tmp;
        for(int i=gEditingLen; i<strlen(candidate); i++) {
            char c = candidate[i];

            if(!gCompletionSqort && !gCompletionDqort && (c==' ' || c=='!' || c=='*' || c=='>' || c=='&' || c=='~' || c=='#' || c =='$' || c=='(' || c==')' || c=='\\' || c=='|' || c=='[' || c==']' || c=='{' || c=='}' || c==';' || c=='\'' || c=='"' || c=='<' || c=='>' || c=='?'))
            {
                *p++ = '\\';
                *p++ = c;
            }
            else {
                *p++ = c;
            }
        }
        if(*(p-1) != '/') {
            if(gCompletionSqort && !gCompletionLastSqort) {
                *p++ = '\'';
                *p++ = ' ';
            }
            else if(gCompletionDqort && !gCompletionLastDqort) {
                *p++ = '"';
                *p++ = ' ';
            }
            else if(gCompletionAddSpace && !gCompletionLastSqort
                    && !gCompletionLastDqort)
            {
                *p++ = ' ';
            }
        }
        *p = 0;

        string_insert((string_obj*)vector_item(gCmdLine, gCLCursor)
                , gCursor, tmp);
        gCursor+=strlen(tmp);

        gCompletionMenu = false;
        completion_clear();
        gCompletionCursor = -1;
        return;
    }
    else if(key == 4 || key == KEY_NPAGE) {    // CTRL-D
        if(vector_size(gCExplanation) == 0) 
            gCompletionCursor+=20;
        else
            gCompletionCursor+=5;
    }
    else if(key == 21 || key == KEY_PPAGE) {    // CTRL-U
        if(vector_size(gCExplanation) == 0)
            gCompletionCursor-=20;
        else
            gCompletionCursor-=5;
    }
    else if(key == 3 || key == 7 || key == 27 || key == 8 || key == KEY_BACKSPACE || key == 127 || key == KEY_DC) { // CTRL-C CTRL-G Escape Bs
        gCompletionCursor = -1;
        gCompletionScrollTop = 0;

        gCompletionMenu = false;
        completion_clear();
        return;
    }

    if(gCompletionCursor < 0) {
        gCompletionCursor = vector_size(gCCandidate)-1;
    }
    if(gCompletionCursor >= vector_size(gCCandidate)) {
        gCompletionCursor = 0;
        //        gCompletionCursor = vector_size(gCCandidate)-1;
    }

    const int maxy = mgetmaxy();
    if(vector_size(gCExplanation) == 0) {
        if(gCompletionCursor < gCompletionScrollTop) {
            gCompletionScrollTop = (gCompletionCursor/((maxy-2)*4))*((maxy-2)*4);
        }
        if(gCompletionCursor > gCompletionScrollTop + (maxy-3)*4) {
            gCompletionScrollTop = (gCompletionCursor/((maxy-2)*4))*((maxy-2)*4);
        }
    }
    else {
        if(gCompletionCursor < gCompletionScrollTop) {
            gCompletionScrollTop = (gCompletionCursor/(maxy-2))*(maxy-2);
        }
        if(gCompletionCursor > gCompletionScrollTop + maxy-3) {
            gCompletionScrollTop = (gCompletionCursor/(maxy-2))*(maxy-2);
        }
    }

    TEND();
}

static void cmdline_view_euc_sjis()
{
    const int maxx = mgetmaxx();
    const int maxy = mgetmaxy();

    mmvprintw(maxy - 2, 0, "$ ");
    bool kanji = false;
    bool kanji_zure = false;
    string_obj* str = (string_obj*)vector_item(gCmdLine, gCLCursor);

    for(int i=0; i<string_length(str); i++) {
        char c = string_c_str(str)[i];

        if(kanji) {
            kanji = false;
        }
        else if(is_kanji(c)) {
            kanji = true;
        }

        if(i+2 < maxx-1) {
            mmvprintw(maxy -2, 2 + i, "%c", c);
        }
        else if(i+2 == maxx-1) {
            if(kanji) {
                mmvprintw(maxy -1, 2 + i - maxx+1, "%c", c);
                kanji_zure = true;
            }
            else {
                mmvprintw(maxy -2, 2 + i, "%c", c);
            }
        }
        else {
            if(kanji_zure) {
                if(2 + i -maxx+1 < maxx-1) {
                    mmvprintw(maxy -1, 2 + i - maxx+1, "%c", c);
                }
            }
            else {
                if(2 + i - maxx < maxx-1) {
                    mmvprintw(maxy -1, 2 + i - maxx, "%c", c);
                }
            }
        }
    }


    if(kanji_zure) {    
        if(gCursor+2 < maxx-1) {
            mmove_immediately(maxy -2, 2 + gCursor);
            mrefresh();
        }
        else {
            mmove_immediately(maxy-1, 2+ gCursor -maxx+1);
            mrefresh();
        }
    }
    else {
        if(gCursor+2 < maxx) {
            mmove_immediately(maxy -2, 2 + gCursor);
            mrefresh();
        }
        else {
            mmove_immediately(maxy -1, 2 + gCursor - maxx);
            mrefresh();
        }
    }
}

static void cmdline_view_utf8()
{
    const int maxx = mgetmaxx();
    const int maxy = mgetmaxy();

    mmvprintw(maxy - 2, 0, "$ ");
    string_obj* str = (string_obj*)vector_item(gCmdLine, gCLCursor);
    char* str2 = string_c_str(str);

    bool kanji_zure = false;
    for(int i=0; i<str_utflen(str2); i++) {
        int termpos = str_termpos(str2, i);

        if(termpos+2 == maxx-1) {
            char* pointer = str_utfpos2pointer(str2, i);
            if(is_kanji(*pointer)) {
                kanji_zure = true;
                break;
            }
        }
    }

    if(kanji_zure) {
        for(int i=0; i<str_utflen(str2); i++) {
            int termpos = str_termpos(str2, i);

            if(termpos+2 == maxx-1) {
                char* pointer = str_utfpos2pointer(str2, i);

                if(is_kanji(*pointer)) {
                    char line1[2048];

                    memcpy(line1, str2, pointer-str2);
                    line1[pointer-str2] = 0;

                    mprintw("%s", line1);
                    mmvprintw(maxy -1, 0, "%s", pointer);
                    break;
                }
            }
        }
    }
    else {
        mprintw("%s", str2);
    }

    int utfpos = str_pointer2utfpos(str2, str2 + gCursor);
    int termpos = str_termpos(str2, utfpos);

    if(kanji_zure) {
        if(termpos+2 < maxx-1)
            mmove_immediately(maxy -2, 2 + termpos);
        else
            mmove_immediately(maxy -1, 2 + termpos - maxx+1);
    }
    else {
        if(termpos+2 < maxx) {
            mmove_immediately(maxy -2, 2 + termpos);
        }
        else {
            mmove_immediately(maxy -1, 2 + termpos - maxx);
        }
    }
}

void cmdline_view()
{
    TBEGIN();

    if(gKanjiCode == kUtf) {
        cmdline_view_utf8();
    }
    else {
        cmdline_view_euc_sjis();
    }

    TEND();
}

void cmdline_history_view()
{
    TBEGIN();
    const int maxx = mgetmaxx();
    const int maxy = mgetmaxy();

    int line = 0;
    for(int i= gHistoryScrollTop; i<vector_size(gHCandidate); i++) {
        string_obj* item = (string_obj*)vector_item(gHCandidate, i);

        char buf[256];
        sprintf(buf, "%s", string_c_str(item));

        char tmp[256];
        if(gKanjiCode == kUtf)
            str_cut2(buf, maxx-1, tmp, 256);
        else
            cut(buf, tmp, maxx-1);

        if(gHistoryCursor == i) mattron(kCAReverse);        
        mmvprintw(line, 0, "%s", tmp);
        if(gHistoryCursor == i) mattroff();
        line++;

        if(line >= maxy-2) break;
    }

    TEND();
}

void cmdline_history_input(int meta, int key)
{
    TBEGIN();
    if(key == 14 || key == KEY_DOWN) {    // CTRL-N
        gHistoryCursor++;
    }
    else if(key == 16 || key == KEY_UP) {    //CTRL-P
        gHistoryCursor--;
    }
    if(key == 6 || key == KEY_RIGHT) {    // CTRL-F
        gHistoryCursor++;
    }
    else if(key == 2 || key == KEY_LEFT) {    // CTRL-B
        gHistoryCursor--;
    }
    else if(key == 10 || key == 13) {		// CTRL-J CTRL-M
        string_obj* candidate = (string_obj*)vector_item(gHCandidate, gHistoryCursor);
        string_obj* now = (string_obj*)vector_item(gCmdLine, gCLCursor);
        string_put(now, string_c_str(candidate));

        gCursor = string_length(candidate);

        vector_clear(gHCandidate);
    }
    else if(key == 4 || key == KEY_NPAGE) {    // CTRL-D
        gHistoryCursor+=5;
    }
    else if(key == 21 || key == KEY_PPAGE) {    // CTRL-U
        gHistoryCursor-=5;
    }
    else if(key == 3 || key == 7 || key == 27) { // CTRL-C CTRL-G Escape
        vector_clear(gHCandidate);
    }

    if(gHistoryCursor < 0) {
        gHistoryCursor = 0;
    }
    if(gHistoryCursor >= vector_size(gHCandidate)) {
        gHistoryCursor = vector_size(gHCandidate)-1;
    }

    const int maxy = mgetmaxy();
    if(gHistoryCursor < gHistoryScrollTop) {
        gHistoryScrollTop = (gHistoryCursor/(maxy-2))*(maxy-2);
    }
    if(gHistoryCursor > gHistoryScrollTop + maxy-3) {
        gHistoryScrollTop = (gHistoryCursor/(maxy-2))*(maxy-2);
    }

    TEND();
}

void cmdline_alias_add(char* alias, int arg_num, char* cmd)
{
    TBEGIN();

    M(("ALIAS"));                
    sAlias* new_alias = (sAlias*)MALLOC(sizeof(sAlias));

    strcpy(new_alias->mAlias, alias);
    new_alias->mArgNum = arg_num;
    strcpy(new_alias->mCommand, cmd);

    vector_add(gAlias, new_alias);

    TEND();
}
