extern "C"
{
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <signal.h>
#include <termios.h>
#include <ctype.h>
#include <stdarg.h>
#include <time.h>
#include <ctype.h>
#include <locale.h>
#include <sys/types.h>
#include <pwd.h>
#ifdef __CYGWIN__
#include <ncurses/term.h>
#include <sys/time.h>
#else
#include <term.h>
#endif
}

#include "common.h"

#if defined(HAVE_MIGEMO)
extern "C"
{
#include "oniguruma.h"
#include "migemo.h"
}

migemo* gMigemo;
#endif

const int kKeyMetaFirst = 128;
const int kKeyEsc = 27;

cDirWnd* gLDir;
cDirWnd* gRDir;
bool gMainLoop;
hash_obj* gKeyCommand[2][KEY_MAX];

bool gISearch = false;
bool gColor = false;
bool gIndividualCursor = false;
bool gCheckDeleteFile = true;
bool gCheckCopyFile = false;
bool gCheckExit = true;
bool gShiftISearch = false;
bool gGnuScreen = false;
bool gPty = true;
bool gAutoRehash = false;
bool gSelect = true;

bool gBoldExe = true;
bool gBoldDir = true;
int gColorMark = kCAYellow;
int gColorDir = kCACyan;
int gColorExe = kCARed;
int gColorLink = kCAMagenta;
bool gISearchEnterDecision = true;
bool gISearchMismatchFinish = false;
bool gRemainCursor = true;

bool gReadDirHistory = true;

char gTrashBoxName[PATH_MAX];

static string_obj* gInputFileName;

static void read_rc_file(bool read_etc);
static void input(int meta, int key);

void regex_init()
{
#if defined(HAVE_MIGEMO)
    char buf[PATH_MAX];
    gMigemo = migemo_open(NULL);

    if(gKanjiCode == kUtf) {
        sprintf(buf, "%s/share/migemo-dic/utf-8/migemo-dict", PREFIX_PATH);
        if(migemo_load(gMigemo, MIGEMO_DICTID_MIGEMO, buf) == MIGEMO_DICTID_INVALID) {
            fprintf(stderr, "%s is not found\n", buf);
            exit(1);
        }
        sprintf(buf, "%s/share/migemo-dic/utf-8/roma2hira.dat", PREFIX_PATH);
        if(migemo_load(gMigemo, MIGEMO_DICTID_ROMA2HIRA, buf) == MIGEMO_DICTID_INVALID) {
            fprintf(stderr, "%s is not found\n", buf);
            exit(1);
        }
        sprintf(buf, "%s/share/migemo-dic/utf-8/hira2kata.dat", PREFIX_PATH);
        if(migemo_load(gMigemo, MIGEMO_DICTID_HIRA2KATA, buf) == MIGEMO_DICTID_INVALID) {
            fprintf(stderr, "%s is not found\n", buf);
            exit(1);
        }
        sprintf(buf, "%s/share/migemo-dic/utf-8/han2zen.dat", PREFIX_PATH);
        if(migemo_load(gMigemo, MIGEMO_DICTID_HAN2ZEN, buf) == MIGEMO_DICTID_INVALID) {
            fprintf(stderr, "%s is not found\n", buf);
            exit(1);
        }
    }
    else if(gKanjiCode == kEuc) {
        sprintf(buf, "%s/share/migemo-dic/euc-jp/migemo-dict", PREFIX_PATH);
        if(migemo_load(gMigemo, MIGEMO_DICTID_MIGEMO, buf) == MIGEMO_DICTID_INVALID) {
            fprintf(stderr, "%s is not found\n", buf);
            exit(1);
        }
        sprintf(buf, "%s/share/migemo-dic/euc-jp/roma2hira.dat", PREFIX_PATH);
        if(migemo_load(gMigemo, MIGEMO_DICTID_ROMA2HIRA, buf) == MIGEMO_DICTID_INVALID) {
            fprintf(stderr, "%s is not found\n", buf);
            exit(1);
        }
        sprintf(buf, "%s/share/migemo-dic/euc-jp/hira2kata.dat", PREFIX_PATH);
        if(migemo_load(gMigemo, MIGEMO_DICTID_HIRA2KATA, buf) == MIGEMO_DICTID_INVALID) {
            fprintf(stderr, "%s is not found\n", buf);
            exit(1);
        }
        sprintf(buf, "%s/share/migemo-dic/euc-jp/han2zen.dat", PREFIX_PATH);
        if(migemo_load(gMigemo, MIGEMO_DICTID_HAN2ZEN, buf) == MIGEMO_DICTID_INVALID) {
            fprintf(stderr, "%s is not found\n", buf);
            exit(1);
        }
    }
    else {
        sprintf(buf, "%s/share/migemo-dic/cp932/migemo-dict", PREFIX_PATH);
        if(migemo_load(gMigemo, MIGEMO_DICTID_MIGEMO, buf) == MIGEMO_DICTID_INVALID) {
            fprintf(stderr, "%s is not found\n", buf);
            exit(1);
        }
        sprintf(buf, "%s/share/migemo-dic/cp932/roma2hira.dat", PREFIX_PATH);
        if(migemo_load(gMigemo, MIGEMO_DICTID_ROMA2HIRA, buf) == MIGEMO_DICTID_INVALID) {
            fprintf(stderr, "%s is not found\n", buf);
            exit(1);
        }
        sprintf(buf, "%s/share/migemo-dic/cp932/hira2kata.dat", PREFIX_PATH);
        if(migemo_load(gMigemo, MIGEMO_DICTID_HIRA2KATA, buf) == MIGEMO_DICTID_INVALID) {
            fprintf(stderr, "%s is not found\n", buf);
            exit(1);
        }
        sprintf(buf, "%s/share/migemo-dic/cp932/han2zen.dat", PREFIX_PATH);
        if(migemo_load(gMigemo, MIGEMO_DICTID_HAN2ZEN, buf) == MIGEMO_DICTID_INVALID) {
            fprintf(stderr, "%s is not found\n", buf);
            exit(1);
        }
    }

#endif
}

void regex_final()
{
#if defined(HAVE_MIGEMO)
    onig_end();
    migemo_close(gMigemo);
#endif
}

static inline bool is_isearch_char(int key)
{
    if(gISearchEnterDecision || gISearchMismatchFinish) {
        return (key >= ' ' && key <= '~');
    }
    else {
        return (key >= ' ' && key <= '~')
                && key != '/'
                && key != '\\'
                && key != '~'
                && key != ' '
                && key != '?';
    }
}

bool gExitFlg = false;

void atexit_fun()
{
    if(!gExitFlg) {
        cDirWnd::SaveHistory();
        if(mis_curses()) {
        mendwin();
    }
        putp(tigetstr("rmkx"));
    }
}

void sig_exit(int signal)
{
M(("signal %d", signal));
//    gMainLoop = false;

exit(1);        
}

void sig_child(int siganl)
{
M(("sig_child"));
    int status;
    waitpid(-1, &status, WNOHANG);
}

void sig_winch(int signal)
{
    gLDir->Reread();
    gRDir->Reread(); 
   
//    mclear_immediately();
    mclear_buffer();
    mclear();
    view();
    mrefresh();
}
/*
extern "C"
{
extern void Init_socket();
extern void Init_etc();
}
*/
#if defined(__SOLARIS__)
char** environ;
int main(int argc, char* argv[], char** envp)
{
    environ = envp;
#else
int main(int argc, char* argv[])
{
#endif
CHECKML_BEGIN();
LOG_BEGIN();

    /// is terminal ///
    if(!isatty(0) || !isatty(1)) {
        fprintf(stderr, "standard input is not a tty\n");
        return 1;
    }

    /// option ///
    bool read_etc = false;
    bool screen_mode = false;
    bool xterm_mode = false;
    int pid = 0;
    
    char init_path[512];
    strcpy(init_path, "");
    
    int opt;
    while((opt = getopt(argc, argv, "r:tsxv")) != -1) {
        switch(opt) {
        case 'r':
            pid = atoi(optarg);
            break;

        case 't':
            read_etc = true;
            break;

        case 's':
            screen_mode = true;
            break;

        case 'x':
            xterm_mode = true;
            break;

        case 'v':
            printf("the Minnu's Filer2 version 1.99\n");
            exit(0);
            break;

        case ':':
            printf("option needs a value\n");
            exit(1);
            break;

        case '?':
            printf("usage mfiler [tsx] [-r PID] directory\n\n");
            printf("-t : read %s/etc/mfiler instead of $HOME/.mfiler\n", PREFIX_PATH);
            printf("-s : run with GNU screen mode\n");
            printf("-x : run with X terminal mode\n");
            printf("\n");
            printf("-r PID : send a command to a running mfiler of PID. The command is rereading disk and refreshing screen\n");
            exit(1);
            break;
        }
    }

    for(int i=optind; i<argc; i++) {
        if(argv[i][0] != '-') {
            strcpy(init_path, argv[i]);
        }
    }

    /// send socket message ///
    if(pid != 0) {
        char soc_name[256];
        sprintf(soc_name, "/tmp/mfiler%d", pid);

        if(access(soc_name, F_OK) != 0) {
            fprintf(stderr, "illegal pid.");
            return 1;
        }

        int soc = socket(AF_UNIX, SOCK_DGRAM, 0);
        if(soc < 0) {
            perror("socket");
            return 1;
        }

        struct sockaddr_un addr;
        addr.sun_family = AF_UNIX;
        strcpy(addr.sun_path, soc_name);
        if(connect(soc, (struct sockaddr *)&addr, sizeof(addr)) == -1) {
            perror("bind");
            return 1;
        }

        write(soc, "refresh", 8);
        close(soc);
    
        return 0;
    }

    /// init global var ///
    char* home = getenv("HOME");
    if(home == NULL) {
        fprintf(stderr, "$HOME is NULL\n");
        return 1;
    }

    strcpy(gTrashBoxName, home);
    strcat(gTrashBoxName, "/mtrashbox");
    
    for(int i=0; i<2; i++) {
        for(int j=0; j<KEY_MAX; j++) {
            gKeyCommand[i][j] = hash_new(10);
         }
    }

    /// signal ///
    signal(SIGINT, sig_exit);
    signal(SIGQUIT, sig_exit);
    signal(SIGABRT, sig_exit);
    signal(SIGKILL, sig_exit);
    signal(SIGPIPE, sig_exit);
    signal(SIGALRM, sig_exit);
    signal(SIGTERM, sig_exit);
    signal(SIGHUP, sig_exit);
    signal(SIGTSTP, SIG_IGN);
    signal(SIGTTOU, SIG_DFL);
    signal(SIGCONT, SIG_DFL);
    signal(SIGWINCH, sig_winch);

//    signal(SIGCHLD, SIG_IGN);
//    signal(SIGCHLD, sig_child);
    
    /// init ruby interpreter ///
M(("init ruby itepreter"));
    
    ruby_init();
    //ruby_script("embedded");
    //ruby_init_loadpath();
    //rb_require("broken.rb");

//    Init_socket();
//    Init_etc();
    
    rb_define_global_const("KEY_UP", INT2NUM(KEY_UP));
    rb_define_global_const("KEY_RIGHT", INT2NUM(KEY_RIGHT));
    rb_define_global_const("KEY_DOWN", INT2NUM(KEY_DOWN));
    rb_define_global_const("KEY_LEFT", INT2NUM(KEY_LEFT));
    rb_define_global_const("KEY_INSERT", INT2NUM(KEY_IC));
    rb_define_global_const("KEY_DELETE", INT2NUM(KEY_DC));
    rb_define_global_const("KEY_HOME", INT2NUM(KEY_HOME));
    rb_define_global_const("KEY_END", INT2NUM(KEY_END));
    rb_define_global_const("KEY_PAGEUP", INT2NUM(KEY_PPAGE));
    rb_define_global_const("KEY_PAGEDOWN", INT2NUM(KEY_NPAGE));
    
    rb_define_global_const("KEY_ENTER", INT2NUM(10));
    rb_define_global_const("KEY_BACKSPACE", INT2NUM(KEY_BACKSPACE));
    rb_define_global_const("KEY_SPACE", INT2NUM(32));
    
    rb_define_global_const("KEY_F1", INT2NUM(KEY_F(1)));
    rb_define_global_const("KEY_F2", INT2NUM(KEY_F(2)));
    rb_define_global_const("KEY_F3", INT2NUM(KEY_F(3)));
    rb_define_global_const("KEY_F4", INT2NUM(KEY_F(4)));
    rb_define_global_const("KEY_F5", INT2NUM(KEY_F(5)));
    rb_define_global_const("KEY_F6", INT2NUM(KEY_F(6)));
    rb_define_global_const("KEY_F7", INT2NUM(KEY_F(7)));
    rb_define_global_const("KEY_F8", INT2NUM(KEY_F(8)));
    rb_define_global_const("KEY_F9", INT2NUM(KEY_F(9)));
    rb_define_global_const("KEY_F10", INT2NUM(KEY_F(10)));
    rb_define_global_const("KEY_F11", INT2NUM(KEY_F(11)));
    rb_define_global_const("KEY_F12", INT2NUM(KEY_F(12)));
    
    rb_define_global_const("KEY_a", INT2NUM('a'));
    rb_define_global_const("KEY_b", INT2NUM('b'));
    rb_define_global_const("KEY_c", INT2NUM('c'));
    rb_define_global_const("KEY_d", INT2NUM('d'));
    rb_define_global_const("KEY_e", INT2NUM('e'));
    rb_define_global_const("KEY_f", INT2NUM('f'));
    rb_define_global_const("KEY_g", INT2NUM('g'));
    rb_define_global_const("KEY_h", INT2NUM('h'));
    rb_define_global_const("KEY_i", INT2NUM('i'));
    rb_define_global_const("KEY_j", INT2NUM('j'));
    rb_define_global_const("KEY_k", INT2NUM('k'));
    rb_define_global_const("KEY_l", INT2NUM('l'));
    rb_define_global_const("KEY_m", INT2NUM('m'));
    rb_define_global_const("KEY_n", INT2NUM('n'));
    rb_define_global_const("KEY_o", INT2NUM('o'));
    rb_define_global_const("KEY_p", INT2NUM('p'));
    rb_define_global_const("KEY_q", INT2NUM('q'));
    rb_define_global_const("KEY_r", INT2NUM('r'));
    rb_define_global_const("KEY_s", INT2NUM('s'));
    rb_define_global_const("KEY_t", INT2NUM('t'));
    rb_define_global_const("KEY_u", INT2NUM('u'));
    rb_define_global_const("KEY_v", INT2NUM('v'));
    rb_define_global_const("KEY_w", INT2NUM('w'));
    rb_define_global_const("KEY_x", INT2NUM('x'));
    rb_define_global_const("KEY_y", INT2NUM('y'));
    rb_define_global_const("KEY_z", INT2NUM('z'));
    
    rb_define_global_const("KEY_CTRL_SPACE", INT2NUM(0));
    rb_define_global_const("KEY_CTRL_A", INT2NUM(1));
    rb_define_global_const("KEY_CTRL_B", INT2NUM(2));
    rb_define_global_const("KEY_CTRL_C", INT2NUM(3));
    rb_define_global_const("KEY_CTRL_D", INT2NUM(4));
    rb_define_global_const("KEY_CTRL_E", INT2NUM(5));
    rb_define_global_const("KEY_CTRL_F", INT2NUM(6));
    rb_define_global_const("KEY_CTRL_G", INT2NUM(7));
    rb_define_global_const("KEY_CTRL_H", INT2NUM(8));
    rb_define_global_const("KEY_CTRL_I", INT2NUM(9));
    rb_define_global_const("KEY_CTRL_J", INT2NUM(10));
    rb_define_global_const("KEY_CTRL_K", INT2NUM(11));
    rb_define_global_const("KEY_CTRL_L", INT2NUM(12));
    rb_define_global_const("KEY_CTRL_M", INT2NUM(13));
    rb_define_global_const("KEY_CTRL_N", INT2NUM(14));
    rb_define_global_const("KEY_CTRL_O", INT2NUM(15));
    rb_define_global_const("KEY_CTRL_P", INT2NUM(16));
    rb_define_global_const("KEY_CTRL_Q", INT2NUM(17));
    rb_define_global_const("KEY_CTRL_R", INT2NUM(18));
    rb_define_global_const("KEY_CTRL_S", INT2NUM(19));
    rb_define_global_const("KEY_CTRL_T", INT2NUM(20));
    rb_define_global_const("KEY_CTRL_U", INT2NUM(21));
    rb_define_global_const("KEY_CTRL_V", INT2NUM(22));
    rb_define_global_const("KEY_CTRL_W", INT2NUM(23));
    rb_define_global_const("KEY_CTRL_X", INT2NUM(24));
    rb_define_global_const("KEY_CTRL_Y", INT2NUM(25));
    rb_define_global_const("KEY_CTRL_Z", INT2NUM(26));
    rb_define_global_const("KEY_ESCAPE", INT2NUM(27));
    
    rb_define_global_const("KEY_A", INT2NUM('A'));
    rb_define_global_const("KEY_B", INT2NUM('B'));
    rb_define_global_const("KEY_C", INT2NUM('C'));
    rb_define_global_const("KEY_D", INT2NUM('D'));
    rb_define_global_const("KEY_E", INT2NUM('E'));
    rb_define_global_const("KEY_F", INT2NUM('F'));
    rb_define_global_const("KEY_G", INT2NUM('G'));
    rb_define_global_const("KEY_H", INT2NUM('H'));
    rb_define_global_const("KEY_I", INT2NUM('I'));
    rb_define_global_const("KEY_J", INT2NUM('J'));
    rb_define_global_const("KEY_K", INT2NUM('K'));
    rb_define_global_const("KEY_L", INT2NUM('L'));
    rb_define_global_const("KEY_M", INT2NUM('M'));
    rb_define_global_const("KEY_N", INT2NUM('N'));
    rb_define_global_const("KEY_O", INT2NUM('O'));
    rb_define_global_const("KEY_P", INT2NUM('P'));
    rb_define_global_const("KEY_Q", INT2NUM('Q'));
    rb_define_global_const("KEY_R", INT2NUM('R'));
    rb_define_global_const("KEY_S", INT2NUM('S'));
    rb_define_global_const("KEY_T", INT2NUM('T'));
    rb_define_global_const("KEY_U", INT2NUM('U'));
    rb_define_global_const("KEY_V", INT2NUM('V'));
    rb_define_global_const("KEY_W", INT2NUM('W'));
    rb_define_global_const("KEY_X", INT2NUM('X'));
    rb_define_global_const("KEY_Y", INT2NUM('Y'));
    rb_define_global_const("KEY_Z", INT2NUM('Z'));

    rb_define_global_const("KEY_0", INT2NUM('0'));
    rb_define_global_const("KEY_1", INT2NUM('1'));
    rb_define_global_const("KEY_2", INT2NUM('2'));
    rb_define_global_const("KEY_3", INT2NUM('3'));
    rb_define_global_const("KEY_4", INT2NUM('4'));
    rb_define_global_const("KEY_5", INT2NUM('5'));
    rb_define_global_const("KEY_6", INT2NUM('6'));
    rb_define_global_const("KEY_7", INT2NUM('7'));
    rb_define_global_const("KEY_8", INT2NUM('8'));
    rb_define_global_const("KEY_9", INT2NUM('9'));
        
    rb_define_global_const("KEY_EXCLAM", INT2NUM('!'));
    rb_define_global_const("KEY_DQUOTE", INT2NUM('"'));
    rb_define_global_const("KEY_SHARP", INT2NUM('#'));
    rb_define_global_const("KEY_DOLLAR", INT2NUM('$'));
    rb_define_global_const("KEY_PERCENT", INT2NUM('%'));
    rb_define_global_const("KEY_AND", INT2NUM('&'));
    rb_define_global_const("KEY_SQUOTE", INT2NUM('\''));
    rb_define_global_const("KEY_LPAREN", INT2NUM('('));
    rb_define_global_const("KEY_RPAREN", INT2NUM(')'));
    rb_define_global_const("KEY_TILDA", INT2NUM('~'));
    rb_define_global_const("KEY_EQUAL", INT2NUM('='));
    rb_define_global_const("KEY_MINUS", INT2NUM('-'));
    rb_define_global_const("KEY_CUP", INT2NUM('^'));
    rb_define_global_const("KEY_VBAR", INT2NUM('|'));
    rb_define_global_const("KEY_BACKSLASH", INT2NUM('\\'));
    rb_define_global_const("KEY_ATMARK", INT2NUM('@'));
    rb_define_global_const("KEY_BAPOSTROPHE", INT2NUM('`'));
    rb_define_global_const("KEY_LCURLY", INT2NUM('{'));
    rb_define_global_const("KEY_LBRACK", INT2NUM('['));
    rb_define_global_const("KEY_PLUS", INT2NUM('+'));
    rb_define_global_const("KEY_SEMICOLON", INT2NUM(';'));
    rb_define_global_const("KEY_STAR", INT2NUM('*'));
    rb_define_global_const("KEY_COLON", INT2NUM(':'));
    rb_define_global_const("KEY_RCURLY", INT2NUM('}'));
    rb_define_global_const("KEY_RBRACK", INT2NUM(']'));
    rb_define_global_const("KEY_LSS", INT2NUM('<'));
    rb_define_global_const("KEY_COMMA", INT2NUM(','));
    rb_define_global_const("KEY_GTR", INT2NUM('>'));
    rb_define_global_const("KEY_DOT", INT2NUM('.'));
    rb_define_global_const("KEY_SLASH", INT2NUM('/'));
    rb_define_global_const("KEY_QMARK", INT2NUM('?'));
    rb_define_global_const("KEY_UNDERBAR", INT2NUM('_'));
    
    rb_define_global_const("NOMETA", INT2NUM(0));
    rb_define_global_const("META", INT2NUM(1));

    rb_define_global_const("MA_REVERSE", INT2NUM(kCAReverse));
    rb_define_global_const("MA_BOLD", INT2NUM(kCABold));
    rb_define_global_const("MA_UNDERLINE", INT2NUM(kCAUnderline));
    
    rb_define_global_const("MA_WHITE", INT2NUM(kCAWhite));
    rb_define_global_const("MA_BLUE", INT2NUM(kCABlue));
    rb_define_global_const("MA_CYAN", INT2NUM(kCACyan));
    rb_define_global_const("MA_GREEN", INT2NUM(kCAGreen));
    rb_define_global_const("MA_YELLOW", INT2NUM(kCAYellow));
    rb_define_global_const("MA_MAGENTA", INT2NUM(kCAMagenta));
    rb_define_global_const("MA_RED", INT2NUM(kCARed));

//    rb_eval_string("def prompt() \"the Minnu's Filer2 \"; end");
    rb_eval_string("def cursor_move_hook(i) 0; end");
    rb_eval_string("def completion_hook(editing, editing_dir, editing_file, editing_before, earray, cmd, editing_position, sqort, dqort, last_sqort, last_dqort) return 0; end");
    
    /// init modules ///
M(("init modules"));
    
    command_init();
    cmdline_init();
    cMenu::Init();
    cDirWnd::Init();
    mcurses_init();
        
    /// init global var ///
M(("init global var"));
    if(strcmp(init_path, "") == 0) {  
        gLDir = new cDirWnd(NULL, true);
        gRDir = new cDirWnd(NULL, false);
    }
    else {
        struct stat _stat;
        if(stat(init_path, &_stat) < 0) {
            fprintf(stderr, "%s don't exist\n", init_path);
            exit(1);
        }
        if(!S_ISDIR(_stat.st_mode)) {
            fprintf(stderr, "%s is not directory\n", init_path);
            exit(1);
        }
    
        gLDir = new cDirWnd(init_path, true);
        gRDir = new cDirWnd(init_path, false);
    }

//    hash_put(gKeyCommand[0]['q'], "*", STRDUP("mf_exit();"));
    gInputFileName = string_new("");

    /// init socket ///
M(("init socket"));
    char soc_name[256];
    sprintf(soc_name, "/tmp/mfiler%d", getpid());

    if(access(soc_name, F_OK) == 0) {
        unlink(soc_name);
    }
    
    int soc = socket(AF_UNIX, SOCK_DGRAM, 0);
    if(soc < 0) {
        perror("socket");
        return 1;
    }
    
    struct sockaddr_un addr;
    addr.sun_family = AF_UNIX;
    strcpy(addr.sun_path, soc_name);
    if(bind(soc, (struct sockaddr *)&addr, sizeof(addr)) == -1) {
        perror("bind");
        return 1;
    }
    
    /// read rc file ///
M(("read rc file"));
    
    read_rc_file(read_etc);
    
    cmdline_init_after_rc_file();

    if(screen_mode) {
        gGnuScreen = true;
    }
    if(xterm_mode) {
        gXterm = true;
    }

    /// set locale ///
    if(gKanjiCode == kUtf) setlocale(LC_CTYPE, "ja_JP.UTF-8");

    /// migemo init ///
    regex_init();

    /// sig child ///
    char* term = getenv("TERM");
    if(term == NULL) {
        fprintf(stderr, "$TERM is null");
        exit(1);
    }
    
    if(!(strstr(term, "screen") == term || gGnuScreen)) {
        signal(SIGCHLD, sig_child);
    }

    /// read dir history ///
    if(gReadDirHistory) cDirWnd::ReadHistory();

    if(strcmp(init_path, "") != 0) {  
        gLDir->Move(init_path);
        gLDir->SetScrollTop(0);
        gLDir->MoveCursor(0);

        gRDir->Move(init_path);
        gRDir->SetScrollTop(0);
        gRDir->MoveCursor(0);
    }
        
    /// start curses ///
    minitscr();

    mclear_immediately();
    mclear();
    mrefresh();

    /// atexit ///
    atexit(atexit_fun);
    
    /// main loop ///
M(("main loop start"));

    gMainLoop = true;

    fd_set mask;
    fd_set read_ok;

    FD_ZERO(&mask);
    FD_SET(0, &mask);
    FD_SET(soc, &mask);

    while(gMainLoop) {
M(("Main Loop"));    
        /// view ///
        view();

        /// select ///
        if(gSelect) {
            /// get event ///
M(("select"));

            read_ok = mask;

            if(!mkbuf_exist())
                select(soc + 1, &read_ok, NULL, NULL, NULL);
M(("select end"));
M(("socket %d %d", soc, FD_SETSIZE));        

            /// key event ///
            if(FD_ISSET(0, &read_ok) || mkbuf_exist()) {
M(("key event"));
                int meta;
                int key = mgetch(&meta);
M(("key %d(%d)", key,meta));
           
                /// esc ///
                if(key == -1) {
                }
                else if(meta) {
M(("meta - key %d", key));
                
                    input(1, key);
                }
            
                /// meta key || kanji ///
                else if(key >= kKeyMetaFirst && key <= kKeyMetaFirst+127) {
                    if(gCmdLineActive) {    // for Kanji input
                        input(0, key);
                    }
                    else {
                        input(1, key - kKeyMetaFirst);
                    }
                }
    
                /// normal key ///
                else {
                   input(0, key);
                }
            }

            /// socket event ///
            else if(FD_ISSET(soc, &read_ok)) {
M(("socket event"));

                char buf[256];
            
                struct sockaddr_un caddr;
                socklen_t caddr_len;
                int len = recvfrom(soc, buf, 256, 0, (struct sockaddr *)&caddr, &caddr_len);
M(("buf (%s)", buf));
                rb_eval_string("reread");
                //rb_eval_string(buf);
            
//               sleep(1);
            }
        }
        
        /// non select ///
        else {
            int meta;
            int key = mgetch(&meta);
M(("key %d(%d)", key,meta));
           
            /// esc ///
            if(key == -1) {
            }
            else if(meta) {
M(("meta - key %d", key));
                
                input(1, key);
            }
            
            /// meta key || kanji ///
            else if(key >= kKeyMetaFirst && key <= kKeyMetaFirst+127) {
                if(gCmdLineActive) {    // for Kanji input
                    input(0, key);
                }
                else {
                   input(1, key - kKeyMetaFirst);
                }
            }
    
            /// normal key ///
            else {
                input(0, key);
            }
        }
M(("Main Loop end"));        
    }

    /// finish curses ///
M(("finish ecurses"));
    mmove_immediately(0,0);
    mclear_immediately();
    mclear();
    mrefresh();
    mendwin();

    putp(tigetstr("rmkx"));
    

    /// finalize modules ///
M(("finalize modules"));
    cDirWnd::Final();
    cmdline_final();
    command_final();
    cMenu::Final();
    mcurses_final();
    regex_final();
    
    /// release global var ///    
M(("release global var"));
    string_delete(gInputFileName);
    unlink(soc_name);

    delete gLDir;
    delete gRDir;

    for(int i=0; i<2; i++) {
        for(int j=0; j<KEY_MAX; j++) {
            for(hash_it* it = gKeyCommand[i][j]->mEntryIt;
                it;
                it = it->mNextIt)
            {
                FREE(it->mItem);
            }
            hash_delete(gKeyCommand[i][j]);
         }
    }
    
CHECKML_END();
LOG_END();

    gExitFlg = true;
    return 0;
}

static void read_rc_file(bool read_etc)
{
    /// $PREFIX/etc/mfcompl ///
    char mc_fname[256];
    sprintf(mc_fname, "%s/etc/mfcompl", PREFIX_PATH);

    if(access(mc_fname, F_OK) == 0) {
        /// get file size ///
        struct stat sbuf;
        stat(mc_fname, &sbuf);
        const int size = sbuf.st_size;

        /// read to buffer ///
        char* buf = (char*)MALLOC(size + 1);
        
        FILE* mcfile = fopen(mc_fname, "r");
        fread(buf, sizeof(char), size, mcfile);
        buf[size] = 0;
        fclose(mcfile);

        /// eval ///
        rb_eval_string(buf);
        
        FREE(buf);
    }

    /// $HOME/.mfiler ///
    char* home = getenv("HOME");
    if(home == NULL) {
        fprintf(stderr, "$HOME is NULL");
        exit(1);
    }

    char rc_fname[256];
    sprintf(rc_fname, "%s/.mfiler", home);
    
    /* if not exist ~/.mfiler,read from $PREFIX/etc/mfiler */
    FILE *rctest=NULL;
    if(!(rctest=fopen(rc_fname,"r"))){
        sprintf(rc_fname,"%s/etc/mfiler", PREFIX_PATH);
    }
    else {
        fclose(rctest);
    }

    if(read_etc) {
        sprintf(rc_fname, "%s/etc/mfiler", PREFIX_PATH);
    }
    
    if(access(rc_fname, F_OK) == 0) {
        /// get file size ///
        struct stat sbuf;
        stat(rc_fname, &sbuf);
        const int size = sbuf.st_size;

        /// read to buffer ///
        char* buf = (char*)MALLOC(size + 1);
        
        FILE* rcfile = fopen(rc_fname, "r");
        fread(buf, sizeof(char), size, rcfile);
        buf[size] = 0;
        fclose(rcfile);

        /// eval ///
        rb_eval_string(buf);
        
        FREE(buf);
    }
    else {
        fprintf(stderr, "can't find $HOME/.mfiler and %s/etc/mfiler file. pleas copy it to home directory\n", PREFIX_PATH);
        exit(1);
    }
}

static bool match()
{
    bool same = true;
    if(string_length(gInputFileName) > 1) {
        for(int i=1; i<string_length(gInputFileName); i++)
            if(string_c_str(gInputFileName)[i]
                != string_c_str(gInputFileName)[i-1])
            {
                same = false;
            }
    }
    else {
        same = false;
    }

    if(same) {
        int mfiles[2096];
        int n = 0;
        
        for(int i=0; i<vector_size(ActiveDir()->Files()); i++) {
            sFile* file = (sFile*)vector_item(ActiveDir()->Files(), i);
            if(tolower(file->mName[0])
                == tolower(string_c_str(gInputFileName)[0]))
            {
                mfiles[n++] = i;
            }
        }

        if(n > 0) {
            ActiveDir()->MoveCursor(
                            mfiles[(string_length(gInputFileName)-1) % n]);
            return true;
        }
    }
    else {
#if !defined(HAVE_MIGEMO)
        for(int i=0; i<vector_size(ActiveDir()->Files()); i++) {
            sFile* file = (sFile*)vector_item(ActiveDir()->Files(), i);

            bool equal = true;
            const int len = string_length(gInputFileName);
            for(int j=0; j<len; j++) {
                if(tolower(string_c_str(gInputFileName)[j])
                    != tolower(file->mName[j]))
                {
                    equal = false;
                }
            }

            if(equal) {
                ActiveDir()->MoveCursor(i);
                return true;
            }
        }
    }

    return false;

#else

        /// get reg ///
        regex_t* reg;

        const OnigUChar* p = migemo_query(gMigemo, (const unsigned char*)string_c_str(gInputFileName));
        if(p == NULL) {
            return false;
        }
M(("migemo %s", string_c_str(gInputFileName)));
//M(("migemo %s", p));

        int r;
        OnigErrorInfo err_info;

        if(gKanjiCode == kUtf) {
            r = onig_new(&reg, p, p + strlen((char*)p), ONIG_OPTION_DEFAULT, ONIG_ENCODING_UTF8, ONIG_SYNTAX_DEFAULT,  &err_info);
        }
        else if(gKanjiCode == kEuc) {
            r = onig_new(&reg, p, p + strlen((char*)p), ONIG_OPTION_DEFAULT, ONIG_ENCODING_EUC_JP, ONIG_SYNTAX_DEFAULT, &err_info);
        }
        else {
            r = onig_new(&reg, p, p + strlen((char*)p), ONIG_OPTION_DEFAULT, ONIG_ENCODING_SJIS, ONIG_SYNTAX_DEFAULT, &err_info);
        }

        migemo_release(gMigemo, (unsigned char*) p);

        if(r != ONIG_NORMAL) {
char buf[ONIG_MAX_ERROR_MESSAGE_LEN];
onig_error_code_to_str((OnigUChar*)buf, r, &err_info);
M(("%s", buf));

M(("!onig_normal"));
            return false;
        }

M(("onig_new end"));

        /// start ///
        for(int i=0; i<vector_size(ActiveDir()->Files()); i++) {
            sFile* file = (sFile*)vector_item(ActiveDir()->Files(), i);

            bool all_english = true;
            const int len = strlen(file->mName);
            char* str = file->mName;
            for(int j=0; j<len; j++) {
                if(!(str[j]>=' ' && str[j]<='~')) {
                    all_english = false;
                }
            }

M(("file name %s", file->mName));

            if(all_english) {
M(("all english"));
                bool equal = true;
                const int len = string_length(gInputFileName);
                for(int j=0; j<len; j++) {
                    if(tolower(string_c_str(gInputFileName)[j])
                        != tolower(file->mName[j]))
                    {
                        equal = false;
                    }
                }

                if(equal) {
M(("hit"));
                    ActiveDir()->MoveCursor(i);
                    onig_free(reg);
                    
                    return true;
                }
            }
            else {
M(("nihongo"));
                OnigRegion* region = onig_region_new();
                
                OnigUChar* str2 = (OnigUChar*)file->mName;

                int r2 = onig_search(reg, str2, str2 + strlen((char*)str2), str2, str2 + strlen((char*)str2), region, ONIG_OPTION_NONE);

                onig_region_free(region, 1);

M(("r2 %d", r2));
                if(r2 == 0) {
                    ActiveDir()->MoveCursor(i);
                    onig_free(reg);
                    return true;
                }
            }
        }

        onig_free(reg);
    }

    return false;
#endif
}

static void input(int meta, int key)
{
TBEGIN();

    if(gViewHelp) {
        help_input(meta, key);
    }
    else if(gSelectDir) {
        ActiveDir()->SelectDirInput(meta, key);
    }
    else if(gCmdLineActive) {
        if(vector_size(gHCandidate) > 0)
            cmdline_history_input(meta, key);
        else if(gCompletionMenu)
            cmdline_completion_menu_input(meta, key);
        else
            cmdline_input(meta, key);
    }
    else if(gActiveMenu) {
        gActiveMenu->Input(meta, key);
    }
    else {
M(("main input"));    
        sFile* cursor = ActiveDir()->CursorFile();
        const char* name = cursor->mName;

        char extension[256];
        char* tmp = extname(cursor->mName);
        sprintf(extension, ".%s", tmp);
        FREE(tmp);
    
        char* command;

        if(gShiftISearch && meta == 0 && (key >= 'A' && key <= 'Z'))
        {
            string_push_back2(gInputFileName, key);

            if(!match()) {
                string_put(gInputFileName, "");
                string_push_back2(gInputFileName, (char)key);

                if(!match()) {
                    string_put(gInputFileName, "");
                }
            }
        }
        else if(gISearch && (gISearchEnterDecision || gISearchMismatchFinish)) {
            if(gISearchEnterDecision) {
                if(key == 8 || key == KEY_BACKSPACE || key == 127 || key == KEY_DC)
                {
                    string_erase(gInputFileName
                                        , string_length(gInputFileName)-1, 1);
                }
                else if(!is_isearch_char(key)) {
                    gISearch = false;
                    string_put(gInputFileName, "");
                }
                else {
                    string_push_back2(gInputFileName, (char)key);
        
                    if(!match()) {
                        string_erase(gInputFileName
                            , string_length(gInputFileName)-1, 1);
                    }
                }
            }
            else if(gISearchMismatchFinish) {
                string_push_back2(gInputFileName, (char)key);
    
                if(!match()) {
                    gISearch = false;
                    string_put(gInputFileName, "");
    
                    if(ActiveDir()->Marking()
                        && key >= 0 && key < KEY_MAX 
                        && hash_item(gKeyCommand[meta][key], ".mark"))
                    {
                        rb_eval_string((char*)hash_item(gKeyCommand[meta][key],".mark"));
                    }
                    else if(S_ISDIR(cursor->mStat.st_mode)
                        && key >= 0 && key < KEY_MAX 
                        && hash_item(gKeyCommand[meta][key], ".dir"))
                    {
                        rb_eval_string((char*)hash_item(gKeyCommand[meta][key], ".dir"));
                    }
                    else if(key >= 0 && key < KEY_MAX  
                          && hash_item(gKeyCommand[meta][key], (char*)name))
                    {
                       rb_eval_string(
                       (char*)hash_item(gKeyCommand[meta][key], (char*)name));
                    }
                    else if(key >= 0 && key < KEY_MAX 
                           && hash_item(gKeyCommand[meta][key], extension))
                    {
                        rb_eval_string(
                         (char*)hash_item(gKeyCommand[meta][key], extension));
                    }
                    else if((cursor->mStat.st_mode&S_IXUSR
                         || cursor->mStat.st_mode&S_IXGRP || cursor->mStat.st_mode&S_IXOTH)
                        && key >= 0 && key < KEY_MAX 
                        && hash_item(gKeyCommand[meta][key], ".execute"))
                    {
                        rb_eval_string((char*)hash_item(gKeyCommand[meta][key],".execute"));
                    }
                    else if(key >= 0 && key < KEY_MAX 
                        && hash_item(gKeyCommand[meta][key], "*")) 
                    {
                        rb_eval_string((char*)hash_item(gKeyCommand[meta][key], "*"));
                    }
                }
            }
        }
        else if(gISearch && meta == 0 && is_isearch_char(key)) {
            string_push_back2(gInputFileName, (char)key);
    
            if(!match()) {
                string_put(gInputFileName, "");
                string_push_back2(gInputFileName, (char)key);
    
                if(!match()) {
                    string_put(gInputFileName, "");
                }
            }
        }
        else if(ActiveDir()->Marking()
            && key >= 0 && key < KEY_MAX 
            && hash_item(gKeyCommand[meta][key], ".mark"))
        {
M(("key mark eval"));
            rb_eval_string((char*)hash_item(gKeyCommand[meta][key],".mark"));
            
            string_put(gInputFileName, "");
        }
        else if(S_ISDIR(cursor->mStat.st_mode)
            && key >= 0 && key < KEY_MAX 
            && hash_item(gKeyCommand[meta][key], ".dir"))
        {
M(("key dir eval"));
            rb_eval_string((char*)hash_item(gKeyCommand[meta][key], ".dir"));
            
            string_put(gInputFileName, "");
        }
        else if(key >= 0 && key < KEY_MAX  
              && hash_item(gKeyCommand[meta][key], (char*)name))
        {
M(("key name eval"));
           rb_eval_string(
               (char*)hash_item(gKeyCommand[meta][key], (char*)name));

            string_put(gInputFileName, "");
        }
        else if(key >= 0 && key < KEY_MAX 
               && hash_item(gKeyCommand[meta][key], extension))
        {
M(("key extension eval"));
            rb_eval_string(
                (char*)hash_item(gKeyCommand[meta][key], extension));

            string_put(gInputFileName, "");
        }
        else if((cursor->mStat.st_mode&S_IXUSR
             || cursor->mStat.st_mode&S_IXGRP || cursor->mStat.st_mode&S_IXOTH)
            && key >= 0 && key < KEY_MAX 
            && hash_item(gKeyCommand[meta][key], ".execute"))
        {
M(("key exe eval"));
            rb_eval_string((char*)hash_item(gKeyCommand[meta][key],".execute"));
            
            string_put(gInputFileName, "");
        }
        else if(key >= 0 && key < KEY_MAX 
            && hash_item(gKeyCommand[meta][key], "*")) 
        {
M(("key file * eval"));
            rb_eval_string((char*)hash_item(gKeyCommand[meta][key], "*"));
            
            string_put(gInputFileName, "");
        }
        else {
            string_put(gInputFileName, "");
        }
    }

TEND();    
}

void view()
{
TBEGIN();

    if(gViewHelp) {
        help_view();
    }
    else if(gActiveMenu) {
        gActiveMenu->View();
    }
    else if(gSelectDir) {
        ActiveDir()->SelectDirView();
    }
    else {
        const int maxx = mgetmaxx();
        const int maxy = mgetmaxy();
        
        mclear();
        
        if(vector_size(gHCandidate))
            cmdline_history_view();
        else if(vector_size(gCCandidate))
            cmdline_completion_view();
        else {
            gLDir->View();
            gRDir->View();
        }

        /// draw ptty ///
M(("draw ptty"));        
        for(int i=0;
            i<(vector_size(gTty) > 5 ? 5: vector_size(gTty));
            i++)
        {
            const int max_width = maxx / 5;

            char buf[1024];
            sMasterTty* tty = (sMasterTty*)vector_item(gTty, i);
            sprintf(buf, "<%d>%s", i+1, tty->mName);
            
            bool kanji = false;
            int j;
            for(j=0; j<max_width; j++) {
                if(kanji)
                    kanji = false;
                else if(is_kanji(buf[j]))
                    kanji = true;
            }
            if(kanji)
                buf[j-1] = 0;
            else 
                buf[j] = 0;

            mmvprintw(maxy-3, max_width*i, "%s", buf);
        }
        
        /// draw cursor file stat ///
M(("draw cursor file stat"));        
        if(gCmdLineActive)
            cmdline_view();
        else {
            /// draw User String ///      
            /*
            VALUE prompt = rb_funcall(rb_cObject, rb_intern("prompt"), 0);
            Check_Type(prompt, T_STRING);
            mmvprintw(maxy -2, 0, RSTRING(prompt)->ptr);
            */

            char host_name[256];
            gethostname(host_name, 256);
            struct passwd* uname = getpwuid(getuid());

            mmvprintw(maxy -2, 0, "the Minnu's Filer2 1.99 sort:%s %s@%s ", cDirWnd::kSortName[cDirWnd::gSortKind], uname->pw_name, host_name);

            /// draw x / InputFileName ///            
            if((gXterm && !gXtermNext) || (!gXterm && gXtermNext))mprintw("x");
            if(gISearch) mprintw("/");
            
            char buf[1024];
            char* str = string_c_str(gInputFileName);
            const int len = strlen(str);
            int i;
            for(i=0; i<maxx-35; i++) {
                if(i<len) {
                    buf[i] = str[i];
                }
                else {
                    buf[i] = ' ';
                }
            }
            buf[i] = 0;
            mprintw(buf);

/*            
            char hname[256];
            gethostname(hname, 256);

            struct passwd* uname = getpwuid(getuid());

            char uhname[256];
            sprintf(uhname, " %s@%s", uname->pw_name, hname);
            mmvprintw(maxy-2, maxx-strlen(uhname)-1, "%s", uhname);
*/

            /// draw file stat ///          
            sFile* file = ActiveDir()->CursorFile();
        
            char permission[12];
            strcpy(permission, "----------");
            if(S_ISDIR(file->mLStat.st_mode)) permission[0] = 'd';
            if(S_ISLNK(file->mLStat.st_mode)) permission[0] = 'l';
            
            mode_t smode = file->mLStat.st_mode;
            if(smode & S_IRUSR) permission[1] = 'r';
            if(smode & S_IWUSR) permission[2] = 'w';
            if(smode & S_IXUSR) permission[3] = 'x';
            if(smode & S_IRGRP) permission[4] = 'r';
            if(smode & S_IWGRP) permission[5] = 'w';
            if(smode & S_IXGRP) permission[6] = 'x';
            if(smode & S_IROTH) permission[7] = 'r';
            if(smode & S_IWOTH) permission[8] = 'w';
            if(smode & S_IXOTH) permission[9] = 'x';
            if(smode & S_ISUID) permission[3] = 's';
            if(smode & S_ISGID) permission[6] = 's';
#if defined(S_ISTXT)
            if(smode & S_ISTXT) permission[9] = 't';
#endif
#if defined(S_ISVTX)
            if(smode & S_ISVTX) permission[9] = 't';
#endif
            
            permission[10] = 0;
        
            time_t t = file->mLStat.st_mtime;
            struct tm* tm_ = (struct tm*)localtime(&t);
             
            char buf2[1024];

            char owner[256];
            char* tmp = mygetpwuid(&file->mLStat);
            if(tmp) {
                if(gKanjiCode == kUtf)
                    str_cut2(tmp, 8, owner, 256);
                else 
                    cut(tmp, owner, 8);
            }
            else {
                sprintf(owner, "%d", file->mLStat.st_uid);
            }

            char group[256];
            tmp = mygetgrgid(&file->mLStat);
            if(tmp) {
                if(gKanjiCode == kUtf)
                    str_cut2(tmp, 7, group, 256);
                else
                    cut(tmp, group, 7);
            }
            else {
                sprintf(group, "%d", file->mLStat.st_gid);
            }


            if(!S_ISLNK(file->mLStat.st_mode)) {
                int year = tm_->tm_year-100;
                if(year < 0) year+=100;
                while(year > 100) year-=100;
                
                sprintf(buf2
                    , "%s %3d %-8s %-7s%10lld %02d-%02d-%02d %02d:%02d %s"
                    , permission, file->mLStat.st_nlink
                    , owner, group
                                        
                    , file->mLStat.st_size
                    
                    , year, tm_->tm_mon+1, tm_->tm_mday
                    , tm_->tm_hour, tm_->tm_min
                    , file->mName);
                    
                char buf3[1024];
                if(gKanjiCode == kUtf) 
                    str_cut2(buf2, maxx-1, buf3, 1024);
                else
                    cut(buf2, buf3, maxx-1);
                                    
                mmvprintw(maxy-1, 0, buf3);
            }
            else {
                char link[PATH_MAX + 1];
                char path[PATH_MAX];
                strcpy(path, ActiveDir()->Path());
                strcat(path, file->mName);                 
                 
                int bytes = readlink(path, link, PATH_MAX);
                link[bytes] = 0;
                 
                sprintf(buf
                   , "%s %3d %s%s%10lld %02d-%02d-%02d %02d:%02d %s -> %s"
                   , permission, file->mLStat.st_nlink
                   , owner, group
                                        
                   , file->mLStat.st_size

                   , tm_->tm_year-100, tm_->tm_mon+1, tm_->tm_mday
                   , tm_->tm_hour, tm_->tm_min
                   , file->mName
                   , link);
                   
                char buf2[1024];
                if(gKanjiCode == kUtf)
                    str_cut2(buf, maxx-1, buf2, 1024);
                else
                    cut(buf, buf2, maxx-1);
                           
                mmvprintw(maxy-1, 0, buf2);
            }

            mmove_immediately(maxy-1, maxx-1);
        }
    
        mrefresh();
    }

TEND();    
}

void cut(char* src, char* dest, int len)
{
    int n;
    bool tmpend = false;
    bool kanji = false;
    for(n=0; n<len; n++) {
        if(kanji)
            kanji = false;
        else if(!tmpend && is_kanji(src[n])) {
            kanji = true;
        }

        if(!tmpend && src[n] == 0) tmpend = true;
                    
        if(tmpend)
            dest[n] = ' ';
        else
            dest[n] = src[n];
    }
    
    if(kanji) dest[n-1] = ' ';
    dest[n] = 0;
}

bool gErrMsgCancel = false;

int err_msg(char* msg, ...)
{
    char buf[BUFSIZ];

    va_list args;
    va_start(args, msg);
    int ret = vsprintf(buf, msg, args);
    va_end(args);

    const int maxy = mgetmaxy();
    
    mclear_online(maxy-2);
    mclear_online(maxy-1);
    mmvprintw(maxy-2, 0, buf);
    mrefresh();

    int meta;
    int key = 0;
    if(!gErrMsgCancel) {
        key = mgetch(&meta);
    }
    
    if(key == 3 || key == 7 || key == 27) {
        gErrMsgCancel = true;
    }

    return ret;
}

void msg(char* msg)
{
    const int maxy = mgetmaxy();

    mclear_online(maxy-2);
    mclear_online(maxy-1);
    mmvprintw(maxy-2, 0, "%s", msg);
    mrefresh();
}

int select_str(char* msg, char* str[], int len, int cancel)
{
    int cursor = 0;

    const int maxx = mgetmaxx();
    const int maxy = mgetmaxy();
    
    while(1) {
        /// viwe ///
        mclear_online(maxy-2);
        mclear_online(maxy-1);

        mmove(maxy-2, 0);
        mprintw(msg);
        
//        mmove(maxy-1, 0);
        mprintw(" ");
        for(int i=0; i< len; i++) {
            if(cursor == i) {
                mattron(kCAReverse);
                mprintw("%s", str[i]);
                mattroff();
                mprintw(" ");
            }
            else {
                mprintw("%s ", str[i]);
            }
        }

        mmove_immediately(maxy-1, maxx-1);
        mrefresh();

        /// input ///
        int meta;
        int key = mgetch(&meta);
        if(key == 10 || key == 13) {
            break;
        }
        else if(key == 6 || key == KEY_RIGHT) {
            cursor++;

            if(cursor >= len) cursor = len-1;
        }
        else if(key == 2 || key == KEY_LEFT) {
            cursor--;

            if(cursor < 0) cursor = 0;
        }
        else if(key == 3 || key == 7 || key == 27) {    // CTRL-C -G Escape
            cursor = cancel;
            break;
        }
        else {
            for(int i=0; i< len; i++) {
                if(toupper(key) == toupper(str[i][0])) {
                    cursor = i;
                    goto finished;
                }
            }
        }
    }
finished:

    return cursor;
}

// path2 must have enough space
bool correct_path(char* path, char* path2)
{
M(("path %s", path));

    /// remove two slash ///
    char* p0 = path + strlen(path);
    while(p0 >= path) {
        if(*p0 == '/' && *(p0+1) == '/') {
            *(p0+1) = 0;
        }
        p0--;
    }

    /// add current path ///
    char tmp00[PATH_MAX];
    
    if(path[0] != '/' && path[0] != '~' && path[0] != '$') {
        strcpy(tmp00, ActiveDir()->Path());
        strcat(tmp00, path);
    }
    else {
        strcpy(tmp00, path);
    }

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

    /// expand ~ and env ///
    char* home = getenv("HOME");
    if(home == NULL) {
        strcpy(path2, "/");
        return false;
    }
    
    char tmp0[PATH_MAX];

    if(tmp00[0] == '~' && strlen(tmp00) == 1) {
        strcpy(tmp0, home);
    }
    else if(tmp00[0] == '~' && tmp00[1] == '/') {
        strcpy(tmp0, home);
        strcat(tmp0, "/");
        strcat(tmp0, tmp00 + 2);
    }
    else if(tmp00[0] == '$') {
        char env[256];
        char* p2 = env;

        char* p = tmp00 + 1;
        while(*p != '/' && *p) {
            *p2++ = *p++;
        }
        *p2 = 0;

        char* env_value = getenv(env);
        if(env_value == NULL) {
            strcpy(path2, "/");
            return false;
        }

        if(*p == '/') {
            strcpy(tmp0, env_value);
            strcat(tmp0, p);
        }
        else {
            strcpy(tmp0, env_value);
        }
    }
     else {
        strcpy(tmp0, tmp00);
    }
   
M(("tmp0 %s", tmp0));

   
    /// remove . ///
    char tmp[PATH_MAX];
    char* p = tmp;

    const int len = strlen(tmp0);
    for(int i=0; i<len; i++) {
        if(i+2<len+1 && tmp0[i] == '/'
           && tmp0[i+1] == '.' && (tmp0[i+2]==0||tmp0[i+2]=='/'))
        {
            i++;
        }
        else {
            *p++ = tmp0[i];
        }
    }
    *p = 0;

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

    /// remove .. ///
    char tmp2[PATH_MAX];
    char* p2 = tmp2;
    memset(tmp2, 0, sizeof(tmp2));

    const int len2 = strlen(tmp);
    for(int i=0; i<len2; i++) {
        if(i+3<len2+1 && tmp[i] == '/' && tmp[i+1] == '.' && tmp[i+2] == '.'
           && (tmp[i+3] == 0 || tmp[i+3]=='/')
            )
        {
            i+=2;

            while(*p2 != '/') {
                p2--;

//                if(p2 == tmp2 && *p2 != '/') return false;
                if(p2 < tmp2) return false;
            }
            *p2 = 0;
        }
        else {
            *p2++ = tmp[i];
        }
    }
    *p2 = 0;

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

    if(*tmp2 == 0) {
        strcpy(path2, "/");
    }
    else {
        strcpy(path2, tmp2);
    }

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

    return true;
}
