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

const int kCmdLineMax = 4096;

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

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

static VALUE gPrograms;
static VALUE gEnvirons;

vector_obj* gCCandidate;
vector_obj* gCExplanation;

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 char gEditingBefore[kCmdLineMax];
bool gCompletionMenu = false;
static int gCompletionScrollTop = 0;
static int gCompletionCursor = -1;
static char gEditingFile[kCmdLineMax];
static bool gSqort;
static bool gDqort;

void cmdline_init()
{
TBEGIN();
    gCmdLine = vector_new(10);
    gCCandidate = vector_new(10);
    gCExplanation = 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");

    strcpy(gEditingBefore, "");
    
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()
{
    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();

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

// !! out must have enable allocated memory space
static void extend_macro(char* in, RESULT char* out, RESULT bool* ruby_mode, RESULT bool* output, RESULT bool* quick, RESULT bool* shell_mode, RESULT bool* xterm_mode,  bool flg)
{
TBEGIN();

    sFile* cursor = ActiveDir()->CursorFile();
    *ruby_mode = false;
    *output = true;
    *shell_mode = false;
    *quick = false;
    *xterm_mode = false;
    
    char* p = in;
    char* cp = out;
    while(*p) {
        if(*p != '%') {
            *cp = *p;
            cp++;
            p++;
        }
        else {
            p++;

            switch(*p) {
            case '%':
                p++;
                
                if(flg) {
                    *cp++ = '%';
                }
                else {
                    *cp++ = '%';
                    *cp++ = '%';
                }
                break;

            case 'r':
                p++;

                if(flg) {
                    *ruby_mode = true;
                }
                else {
                    *cp++ = '%';
                    *cp++ = 'r';
                }
                break;

            case 'q':
                p++;

                if(flg) {
                    *output = false;
                }
                else {
                    *cp++ = '%';
                    *cp++ = 'q';
                }
                break;

            case 'Q':
                p++;

                if(flg) {
                    *quick = true;
                }
                else {
                    *cp++ = '%';
                    *cp++ = 'Q';
                }
                break;

            case 's':
                p++;

                if(flg) {
                    *shell_mode = true;
                }
                else {
                    *cp++ = '%';
                    *cp++ = 's';
                }
                break;

            case 't':
                p++;

                if(flg) {
                    *xterm_mode = true;
                }
                else {
                    *cp++ = '%';
                    *cp++ = 't';
                }
                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) {
                    *cp++ = '"';
                    memcpy(cp, cursor->mName, len);
                    cp += len;
                    *cp++ = '"';
                }
                else {
                    *cp++ = '\'';
                    memcpy(cp, cursor->mName, len);
                    cp += len;
                    *cp++ = '\'';
                }
                }
                break;

            case 'F': {
                p++;

                const char* name = ActiveDir()->Path();
                const 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) {
                    *cp++ = '"';
                    memcpy(cp, name, len);
                    cp += len;

                    memcpy(cp, name2, len2);
                    cp += len2;
                
                    *cp++ = '"';
                }
                else {
                    *cp++ = '\'';
                    memcpy(cp, name, len);
                    cp += len;

                    memcpy(cp, name2, len2);
                    cp += len2;
                
                    *cp++ = '\'';
                }
                }
                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) {
                    *cp++ = '"';
                    memcpy(cp, extension, len);
                    cp += len;
                    *cp++ = '"';
                }
                else {
                    *cp++ = '\'';
                    memcpy(cp, extension, len);
                    cp += len;
                    *cp++ = '\'';
                }

                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) {
                    *cp++='"';
                    memcpy(cp, nwe, len);
                    cp += len;
                    *cp++='"';
                }
                else {
                    *cp++='\'';
                    memcpy(cp, nwe, len);
                    cp += len;
                    *cp++='\'';
                }
                    
                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) {
                        *cp++='"';
                        memcpy(cp, name, len);
                        cp += len;
                        if(slash) *cp++ = '/';
                        *cp++='"';
                    } else {
                        *cp++='\'';
                        memcpy(cp, name, len);
                        cp += len;
                        if(slash) *cp++ = '/';
                        *cp++='\'';
                    }
                }
                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) {
                        *cp++='"';
                        memcpy(cp, name, len);
                        cp += len;
                        if(slash) *cp++ = '/';
                        *cp++='"';
                    }
                    else {
                        *cp++='\'';
                        memcpy(cp, name, len);
                        cp += len;
                        if(slash) *cp++ = '/';
                        *cp++='\'';
                    }
                }
                else {
                    cp++;
                    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;
                        }
                    }

                    if(squote_name) {
                        *cp++='"';
                        memcpy(cp, name, len-1);
                        cp += len-1;
                        if(slash) *cp++='/';
                        *cp++='"';
                    }
                    else {
                        *cp++='\'';
                        memcpy(cp, name, len-1);
                        cp += len-1;
                        if(slash) *cp++='/';
                        *cp++='\'';
                    }
                }
                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;
                        }
                    }

                    if(squote_name) {
                        *cp++='"';
                        memcpy(cp, name, len-1);
                        cp += len-1;
                        if(slash) *cp++ = '/';
                        *cp++='"';
                    }
                    else {
                        *cp++='\'';
                        memcpy(cp, name, len-1);
                        cp += len-1;
                        if(slash) *cp++ = '/';
                        *cp++='\'';
                    }
                }
                else {
                    cp++;
                    gErrMsgCancel = false;
                    err_msg("illeagal macro char");
                }
                    
                }
                break;

            case 'm':
                p++;

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

                    if(flg) {
                        char buf[4096];
                        ActiveDir()->MarkFilesSQuote(buf);      
                        const int len = strlen(buf);

                        memcpy(cp, buf, len);
                        cp += len;
                    }
                    else {
                        *cp++ = '%';
                        *cp++ = 'm';
                        *cp++ = '1';
                    }
                }
                else if(*p == '2') {
                    p++;

                    if(flg) {
                        char buf[4096];
                        SleepDir()->MarkFilesSQuote(buf);      
                        const int len = strlen(buf);
    
                        memcpy(cp, buf, len);
                        cp += len;
                    }
                    else {
                        *cp++ = '%';
                        *cp++ = 'm';
                        *cp++ = '2';
                    }
                }
                else {
                    cp++;
                    gErrMsgCancel = false; 
                    err_msg("illeagal macro char");
                }

                break;

            default:
                cp++;
                gErrMsgCancel = false; 
                err_msg("illeagal macro char");
                break;
            }
        }
    }
    *cp = 0;
    
TEND();
}

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

    gCmdLineActive = true;

    if(gAutoRehash) cmdline_rehash();
    
    char cmd2[kCmdLineMax];
    bool ruby_mode;
    bool output;
    bool shell_mode;
    bool quick;
    bool xterm_mode;
    extend_macro(cmd, cmd2, &ruby_mode, &output, &quick, &shell_mode, &xterm_mode, false);

    vector_add(gCmdLine, string_new(cmd2));
    gCLCursor = vector_size(gCmdLine)-1;
    if(position > 0)
        gCursor = position-1;
    else
        gCursor = strlen(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, 0, 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, 2+x, "(%d) %-37s", i+1, master->mName);
                mattroff();
            }
            else {
               mmvprintw(i+1+y, 2+x, "(%d) %s", 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

    t.c_iflag |= IGNBRK;
    t.c_iflag &= ~(IXOFF|IXON);
    t.c_cc[VMIN] = 1;
    t.c_cc[VTIME] = 0;
    tcsetattr(STDIN_FILENO, TCSANOW, &t);
    
    sighandler_t old_winch = signal(SIGWINCH, sigwinch_handler);
    sighandler_t old_int = signal(SIGINT, SIG_IGN);
    sighandler_t old_quit = signal(SIGQUIT, SIG_IGN);
    
    raise(SIGWINCH);

    sMasterTty* current = (sMasterTty*)vector_item(gTty, gTCur);
    
    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) {
                FREE(item);
                vector_erase(gTty, i);
                break;
            }
        }
    }

    tcsetattr(STDIN_FILENO, TCSANOW, &ot);

    signal(SIGWINCH, old_winch);
    signal(SIGINT, old_int);
    signal(SIGQUIT, old_quit);

    raise(SIGWINCH);
}

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] = "/bin/sh";
    argv[1] = gShellOptEval;
    argv[2] = buf;
    argv[3] = NULL;

    if(output)
        sprintf(argv[2], "( %s ); mfiler -r %d; echo; echo \"HIT ENTER KEY\"; read TMPTMP", 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(STDIN_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
   
        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("/bin/sh", argv);

        exit(1);
    }
    
    current->mPID = pid;
    
    ptty_loop();

TEND();    
}

void cmdline_restore_pty(int n)
{
    mendwin();

    if(n < 0 || n >= vector_size(gTty)) return;

    gTCur = n;
    
    mclear();
    mrefresh();
    
    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);
    }
    
    ptty_loop();

    minitscr();
    
    mclear();
    view();
    mrefresh();
}

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

    char *p = cmd;
    while(*p) {
        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[kCmdLineMax];
            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[kCmdLineMax];
                            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)
{
TBEGIN();
M(("cmdline_run"));
M(("[%s]", str));
    char cmd[kCmdLineMax];
    if(!extend_alias(str, cmd)) {
        gErrMsgCancel = false; 
        err_msg("can't extend alias");
        return;
    }
M(("extended_alias"));
M(("[%s]", cmd));
    
    char cmd2[kCmdLineMax];
    bool ruby_mode;
    bool output;
    bool shell_mode;
    bool quick;
    bool xterm_mode;
    extend_macro(cmd, cmd2, &ruby_mode, &output, &quick, &shell_mode, &xterm_mode, true);

M(("extended_macro"));
M(("[%s]", cmd2));
    
    char title2[kCmdLineMax];
    bool tmp1;
    bool tmp2;
    bool tmp3;
    bool tmp4;
    bool tmp5;
    extend_macro(title, title2, &tmp1, &tmp2, &tmp3, &tmp4, &tmp5, 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);
    if(ruby_mode) {
M(("ruby_mode"));    
        if(!quick) {
            mendwin();
            
            mclear_immediately();
            mmove_immediately(0, 0);
            fflush(stdout);
        }

M(("eval %s", cmd2));
                
        rb_eval_string(cmd2);
//        int error;
//        rb_eval_string_protect(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];

        char buf[kCmdLineMax];

        if(strlen(title2) > 20) {
            title2[20] = 0;
        }
        
        argv[0] = "screen";
        argv[1] = "-t";
        argv[2] = title2;
        argv[3] = gShellPath;
        argv[4] = gShellOptEval;
        argv[5] = buf;
        argv[6] = NULL;

        if(output && !quick)
            sprintf(argv[5], "( %s ); mfiler -r %d; echo; echo \"HIT ENTER KEY\"; read TMPTMP", cmd2, getpid());
        else 
            sprintf(argv[5], "( %s ); mfiler -r %d", cmd2, getpid());
            
M(("argv[5] %s", argv[5]));

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

        char buf[kCmdLineMax];
        
        argv[0] = RSTRING(gXtermPrgName)->ptr;
        argv[1] = RSTRING(gXtermOptTitle)->ptr;
        argv[2] = 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[6+arg_num] = buf;
        argv[7+arg_num] = NULL;

        if(output && !quick)
            sprintf(argv[6+arg_num], "( %s ); mfiler -r %d; echo; echo \"HIT ENTER KEY\"; read TMPTMP", cmd2, getpid());
        else 
            sprintf(argv[6+arg_num], "( %s ); mfiler -r %d", cmd2, getpid());

for(int i=0; i<arg_num+8; i++) {
M(("argv[%d] = (%s)", i, argv[i]));
}            
            
        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);
            }
        }
    }

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

M(("cmd2 %s", cmd2));
        
        if(output && !quick) {
            char cmd3[kCmdLineMax];
            sprintf(cmd3, "( %s ); echo; echo \"HIT ENTER KEY\"; read TMPTMP", cmd2);
M(("cmd3 %s", cmd3));
            if(msystem(cmd3) == -1) {
                gErrMsgCancel = false; 
                err_msg("can't evaluate with system()");
            }
        }
        else {
            if(msystem(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);
    }
    
    gLDir->Reread();
    gRDir->Reread();
        
    gXtermNext = false;

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)
{
TBEGIN();

    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++) {
            char* item = RSTRING(rb_ary_entry(all_candidate, i))->ptr;
            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[2];
                c = ((char*)vector_item(gCCandidate, 0))[l];
                sprintf(tmp, "%c", c);
                string_insert((string_obj*)vector_item(gCmdLine, gCLCursor)
                                , gCursor, tmp);
                gCursor++;
            }
            else {
                break;
            }
    
            l++;
        }

        if(l == len && vector_size(gCCandidate) == 1) {
            if(!sqort && !dqort && c != '/') {
                char tmp[2];
                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)
{
TBEGIN();

    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) {
                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) {
                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[2];
                c = ((char*)vector_item(gCCandidate, 0))[l];
                sprintf(tmp, "%c", c);
                string_insert((string_obj*)vector_item(gCmdLine, gCLCursor)
                                , gCursor, tmp);
                gCursor++;
            }
            else {
                break;
            }
    
            l++;
        }

        if(l == len && vector_size(gCCandidate) == 1 && c != '/') {
            if(!sqort && !dqort) {
                char tmp[2];
                sprintf(tmp, " ");
                string_insert((string_obj*)vector_item(gCmdLine, gCLCursor)
                                , gCursor, tmp);
                gCursor++;
            }
            
            completion_clear();
        }
    }

TEND();    
}

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

    bool kanji[1024];
    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;
        }
    }

    
    char* cmdline = string_c_str(
                        (string_obj*)vector_item(gCmdLine, gCLCursor));

    /// get editing text and position ///
    int i = 0;
    int editing_position = 0;

    char cmdname[kCmdLineMax];
    char* c = cmdname;
    *c = 0;
        
    char editing[kCmdLineMax];
    char* e = editing;
    *e = 0;

    bool sqort = false;
    bool dqort = false;
    bool qort = false;

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

            while(i < gCursor && cmdline[i] == ' ') i++;
                
            if(editing[0] != '%') editing_position++;
                
            e = editing;
        }
        else if(!sqort && !dqort && cmdline[i]=='\\') {
            i++;

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

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

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

            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 = editing;
                
            editing_position = 0;
            c = cmdname;
                
            while(i < gCursor && cmdline[i] == ' ') i++;
        }
        else {
            *e++ = cmdline[i];
            if(editing_position == 0) *c++ = cmdline[i];
            i++;

            qort = false;
        }
    }
    *e = 0;
    *c = 0;

    /// divide editing into dir part and file part ///
    char editing_dir[kCmdLineMax];
    char editing_file[kCmdLineMax];
    
    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);
    }
        
M(("cmdname editing %s editing_dir %s editing_file %s editing_position %d", cmdname, editing, editing_dir, editing_file, editing_position));

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

        completion_clear();
    }
    else if(key == 2 || key == KEY_LEFT) {    // CTRL-B
        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);
        if(string_length(str) == 0) {
            gCmdLineActive = false;

            
            if(gCLCursor != vector_size(gCmdLine)-1) {
                string_obj* str2 = (string_obj*)vector_item(gCmdLine
                                                     ,vector_size(gCmdLine)-1);
                if(string_length(str2) == 0)
                {
                    string_delete(str2);
                    vector_pop_back(gCmdLine);
                }
            }
            string_delete(str);
            vector_erase(gCmdLine, gCLCursor);
        }
        else {
            if(string_length(str) > gCursor) {
                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);
M(("CTRL-H (%s) %d", string_c_str(str), gCursor));    

            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);
                }
            }
M(("(%s) %d", string_c_str(str), gCursor));    
        }

        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] == '/') {
                while(pos>=0 && (s[pos] == ' ' || s[pos] == '/')) {
                    pos--;
                }
            }
            while(pos>=0 && 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(gCLCursor != vector_size(gCmdLine)-1) {
            string_obj* str2 = (string_obj*)vector_item(gCmdLine
                                                     ,vector_size(gCmdLine)-1);
            if(string_length(str2) == 0)
            {
                string_delete(str2);
                vector_pop_back(gCmdLine);
            }
        }

        if(string_length(str) == 0) {
            string_delete(str);
            vector_erase(gCmdLine, gCLCursor);
        }

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

        /// run history ///
        const int size = vector_size(gCmdLine);
        if(gCLCursor != size-1) {
            string_obj* str = (string_obj*)vector_item(gCmdLine, size-1);
            
            if(string_length(str) == 0) {
                string_delete(str);
                vector_pop_back(gCmdLine);
            }

            if(string_length((string_obj*)vector_item(gCmdLine, gCLCursor))
                    != 0)
                vector_add(gCmdLine, vector_item(gCmdLine, gCLCursor));

            vector_erase(gCmdLine, gCLCursor);
        }

        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);
    }
    else if(key == 9) {        // CTRL-I
        if(vector_size(gCCandidate) > 0
            && strcmp(gEditingBefore, editing) == 0)
        {
            gCompletionMenu = true;
            gCompletionScrollTop = 0;
            gCompletionCursor = 0;
            
            strcpy(gEditingFile, editing_file);
            gSqort = sqort;
            gDqort = dqort;
        }
        else {
    
            /// goto ruby level ///
            VALUE ret = Qnil;
            if(editing_position != 0) {
                ret = rb_funcall(rb_cObject, rb_intern("completion_hook"), 7, rb_str_new2(editing), rb_str_new2(editing_dir), rb_str_new2(editing_file), rb_str_new2(cmdname), INT2NUM(editing_position), sqort ? Qtrue:Qfalse, dqort ? Qtrue:Qfalse) ;
            }

            /// default completion ///
            if(ret == Qnil) {
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);
                    }
                }
                /// not exist editing_dir ///
                else {
M(("not exist editing_dir"));            
                    /// editing enverion ///
                    if(editing[0] == '$') {
                        cmdline_completion(gEnvirons, editing, sqort, dqort);
                    }
                    else if(editing[0] == '~') {
                        VALUE tilda = rb_ary_new();
                        rb_ary_push(tilda, rb_str_new2("~/"));
                        
                        cmdline_completion(tilda, editing, sqort, dqort);
                    }
                    else {
                        /// editing program name ///
                        if(editing_position == 0) {
                            cmdline_completion(gPrograms, editing, sqort, dqort);
                        }
                    
                        /// editing file name ///
                        else {
                            DIR* dir = opendir(".");
                            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, "./");
                                    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);
                            }
                        }
                    }
                }
            }
        }
    }
    else if(is_kanji(key)) {
        int meta;
        int key2 = mgetch(&meta);

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

    strcpy(gEditingBefore, editing);
    
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--;
    }
    if(key == 6 || key == KEY_RIGHT) {    // CTRL-F
        gCompletionCursor++;
    }
    else if(key == 2 || key == KEY_LEFT) {    // CTRL-B
        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=strlen(gEditingFile); i<strlen(candidate); i++) {
            *p++ = candidate[i];
        }
        if(!gSqort && !gDqort && *(p-1)!='/') {
            *p++ = ' ';
        }
        *p = 0;
        
        string_insert((string_obj*)vector_item(gCmdLine, gCLCursor)
                                , gCursor, tmp);
        gCursor+=strlen(tmp);
        
        gCompletionMenu = false;
        completion_clear();
    }
    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) { // CTRL-C CTRL-G Escape
        gCompletionCursor = -1;
        gCompletionScrollTop = 0;
    
        gCompletionMenu = false;
        completion_clear();
    }
    
    if(gCompletionCursor < 0) {
        gCompletionCursor = 0;
    }
    if(gCompletionCursor >= vector_size(gCCandidate)) {
        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();
}
  
void cmdline_view()
{
TBEGIN();

    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();
        }
    }
    
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];
            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 buf[256];
            sprintf(buf, "%s %s", item, explanation);
            
            char tmp[256];
            cut(buf, tmp, maxx-1);

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

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

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