#include "common.h"

#include "config.h"

#if defined(HAVE_CURSES_H)
#include <curses.h>
#elif defined(HAVE_NCURSES_H)
#include <ncurses.h>
#elif defined(HAVE_NCURSES_NCURSES_H)
#include <ncurses/ncurses.h>
#endif

#include <unistd.h>
#include <sys/stat.h>
#include <limits.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/select.h>
#include <termios.h>
#include <stdarg.h>
#include <time.h>
#include <ctype.h>
#include <locale.h>
#include <pwd.h>
#include <dirent.h>
#include <stdlib.h>
#include <fcntl.h>
#include <termios.h>

#ifdef __LINUX__

#include <limits.h>
/*
#define __USE_POSIX
#define __USE_XOPEN_EXTENDED
#define __USE_POSIX199309
#define __USE_UNIX98
*/
#include <signal.h>
/*
#undef __USE_POSIX
#undef __USE_XOPEN_EXTENDED
#undef __USE_POSIX199309
#undef __USE_UNIX98
*/
#include <sys/wait.h>

#else

#include <signal.h>
#include <sys/stat.h>
#include <sys/wait.h>

#endif
/*
#if !defined(__DARWIN__)
extern int kill (__pid_t __pid, int __sig) __THROW;
extern int setenv(const char *name, const char *value, int overwrite);
extern void srandom (unsigned int __seed) __THROW;
#endif
*/

int gMainLoop = -1;
void (*gView)() = NULL;          // 登録描写関数

char gTempDir[PATH_MAX];         // 一時ディレクトリ
char gHomeDir[PATH_MAX];         // ホームディレクトリ

///////////////////////////////////////////////////
// キーボード入力処理
///////////////////////////////////////////////////
static void input(int meta, int key)
{
    if(key == 26) {         // CTRL-Z
        mendwin();
        mreset_tty();
        kill(getpid(), SIGSTOP);
        minitscr();
    }
    else if(gCmdLineActive) {
        /// 補完候補選択 ///
        if(vector_size(gCCandidate) > 0)
            cmdline_completion_input(meta, key);

        /// 普通のコマンドライン ///
        else
            cmdline_input(meta, key);
    }
    else if(gActiveMenu) {
        /// メニュー ///
        menu_input(gActiveMenu, meta, key);
    }
    /// インクリメンタルサーチ ///
    else if(gISearch) {
        isearch_input(meta, key);
    }
    else {
        /// ファイラー ///
        filer_input(meta, key);
    }
}

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

    const int size = saphire_job_num();
    int i;
    for(i=0; i<size; i++) {
        char* title = saphire_job_title(i);

        const int x = (maxx/size)*i;
        char buf[BUFSIZ];
        snprintf(buf, BUFSIZ, "[%d]%s", i+1, title);
        char buf2[BUFSIZ];
        
        str_cut(kUtf8, buf, maxx/size, buf2, BUFSIZ);

        mmvprintw(maxy-3, x, "%s", buf2);
    }
}

void view(BOOL cmdline_draw)
{
    if(gCmdLineActive) {
        /// 補完候補選択画面 ///
        if(vector_size(gCCandidate)) {
            cmdline_completion_view();
            if(cmdline_draw) cmdline_view();
        }
        /// ヒストリー選択画面 ///
        else if(vector_size(gHCandidate)) {
            cmdline_history_view();
            if(cmdline_draw) cmdline_view();
        }

        /// ファイラ画面 ///
        else {
            filer_view(0);
            filer_view(1);
            job_view();

            if(cmdline_draw) cmdline_view();
        }
    }
    else if(gISearch) {
        filer_view(0);
        filer_view(1);
        job_view();

        isearch_view();
    }
    else if(gActiveMenu) {
        filer_view(0);
        filer_view(1);
        job_view();

        if(cmdline_draw) cmdline_view();
        menu_view(gActiveMenu);
    }
    else {
        filer_view(0);
        filer_view(1);
        job_view();

        if(cmdline_draw) cmdline_view();
    }

    if(gView) gView();
}

///////////////////////////////////////////////////
// ランタイムスクリプト実行
///////////////////////////////////////////////////
static void read_rc_file_compiled()
{
    char rc_fname[PATH_MAX];

    snprintf(rc_fname, PATH_MAX, "%s/mfiler3.sao", SYSCONFDIR);
    if(access(rc_fname, R_OK) == 0) {
        sRFd* pipein = sRFd_new(STDIN_FILENO);
        sWFd* pipeout = sWFd_new(STDOUT_FILENO);
        saphire_init_stack_frame();
        int rcode = saphire_load_obj(rc_fname
            , pipeout, pipein, STDERR_FILENO, NULL);
        saphire_sweep();
        sWFd_flash(pipeout);

        if(rcode < 0) {
            fprintf(stderr, "%s\n", string_c_str(gErrMsg));
            exit(1);
        }
    }
    else {
        fprintf(stderr, "can't find %s/mfiler3.sao file\n", SYSCONFDIR);
        exit(1);
    }

    snprintf(rc_fname, PATH_MAX, "%s/mfiler3.sao", gHomeDir);

    if(access(rc_fname, R_OK) == 0) {
        sRFd* pipein = sRFd_new(STDIN_FILENO);
        sWFd* pipeout = sWFd_new(STDOUT_FILENO);
        saphire_init_stack_frame();
        int rcode = saphire_load_obj(rc_fname
            , pipeout, pipein, STDERR_FILENO, NULL);
        saphire_sweep();
        sWFd_flash(pipeout);
        if(rcode < 0) {
            fprintf(stderr, "%s\n", string_c_str(gErrMsg));
            exit(1);
        }
    }
}

///////////////////////////////////////////////////
// exit時に実行される関数
///////////////////////////////////////////////////
static void atexit_fun()
{
}

///////////////////////////////////////////////////
// シグナル処理
///////////////////////////////////////////////////
static void sig_winch(int s)
{
    if(gMainLoop == -1) {
        mclear_immediately();       // 画面の再描写
        view(TRUE);
        mrefresh();
    }
}

static void sig_cont(int s)
{
    if(gMainLoop == -1) {
        mclear_immediately();       // 画面の再描写
        view(TRUE);
        mrefresh();
    }
}


void set_signal_mfiler()
{
    saphire_set_signal();

    struct sigaction sa4;

    memset(&sa4, 0, sizeof(sa4));
    sa4.sa_handler = SIG_IGN;
    if(sigaction(SIGCONT, &sa4, NULL) < 0) {
        perror("sigaction4");
        exit(1);
    }

    struct sigaction sa5;

    memset(&sa5, 0, sizeof(sa5));
    sa5.sa_handler = sig_winch;
    if(sigaction(SIGWINCH, &sa5, NULL) < 0) {
        perror("sigaction5");
        exit(1);
    }
}

///////////////////////////////////////////////////
// メイン関数
///////////////////////////////////////////////////
#if defined(ENABLE_SOCKET)
static void main_arg_e(char* send_cmd, int pid)
{
    /// ソケットを送る ///
    char soc_name[PATH_MAX];
    snprintf(soc_name, PATH_MAX, "%s/socket%d", gTempDir, pid);

    if(access(soc_name, F_OK) == 0) {
        /// コマンドをソケットで送る ///
        int soc = socket(AF_UNIX, SOCK_DGRAM, 0);
        if(soc < 0) {
            perror("socket");
            exit(1);
        }

        struct sockaddr_un addr;
        addr.sun_family = AF_UNIX;
        xstrncpy(addr.sun_path, soc_name, sizeof(addr.sun_path));
        if(connect(soc, (struct sockaddr *)&addr, sizeof(addr)) == 0) {
            write(soc, send_cmd, strlen(send_cmd)+1);
        }

        close(soc);
        
        /// コマンド結果をパイプで受けとる ///
        char pipe_name[PATH_MAX];
        snprintf(pipe_name, PATH_MAX, "%s/pipe_%d", gTempDir, pid);

        if(access(pipe_name, F_OK) == 0) {
            int pipe = open(pipe_name, O_RDONLY);
            
            string_obj* str = STRING_NEW("");
            while(1) {
                char buf[BUFSIZ];
                int r = read(pipe, buf, BUFSIZ);

                if(r < 0) {
                    perror("read");
                    exit(1);
                }

                if(r == 0) {
                    break;
                }

                string_push_back(str, buf);
            }
            
            printf("%s", string_c_str(str));
        }
    }
}
#endif

static void usage()
{
#if defined(ENABLE_SOCKET)
    printf("usage mfiler3 [-e [pid] command] [-E [pid] command] [-c command] [--version ] [ filer initial directory or script file]\n\n");
    printf("-e : send a command to running mfiler3. mfiler3 run the command, and send result to this tty.\n");
    printf("-E : send a command to running mfiler3, mfiler3 run the command, and send result to mfiler3 tty.\n");
#else
    printf("usage mfiler3 [-c command] [--version ] [ filer initial directory or script file]\n\n");
#endif
    printf("-c : eval a command on mfiler3\n");
    printf("--version : display mfiler3 version\n");

    exit(0);
}

static void version()
{
    printf("mfiler3 version %s with shell scripting system saphire version %s.", getenv("VERSION"), getenv("SAPHIRE_VERSION"));
    puts("This program is a traditional Japanese style 2pain file manager with a embedded shell scripting system \"saphire\". mfiler3 is developped by Daisuke Minato.");
    puts("compiled with");

#if defined(HAVE_MIGEMO_H)
    puts("+migemo");
#else
    puts("-migemo");
#endif
    puts("+oniguruma");
    puts("+saphire");
    puts("+math");
    puts("+curses");
    
    exit(0);
}

static void init_hooks()
{
    sRFd* pipein = sRFd_new(STDIN_FILENO);
    sWFd* pipeout = sWFd_new(STDOUT_FILENO);
    int rcode = saphire_shell(" def mcd_hook { true }; def atexit_hook { true }; def activate_hook { true }; def cursor_move_hook { true }; def cmdline_hook { true }", NULL, pipeout, pipein, STDERR_FILENO);
    saphire_sweep();
    sWFd_flash(pipeout);

}

static void set_mfenv()
{
    setenv("#", "-1", 1);
    setenv("VIEW_OPTION", "2pain", 1);
    setenv("SYSCONFDIR", SYSCONFDIR, 1);
    setenv("MF3DOCDIR", DOCDIR, 1);
    setenv("PROMPT", "can't read run time script '.mfiler3'. press q key to quit", 1);
    setenv("VIEW_FILE_SIZE", "Normal", 1);

    setenv("MF3HOME", gHomeDir, 1);
    setenv("MF3TEMP", gTempDir, 1);
    setenv("DATADIR", DATADIR, 1);
    setenv("DOTDIR_MASK", "0", 1);

    char buf[128];

    snprintf(buf, 128, "%d", KEY_UP);
    setenv("key_up", buf, 1);
    snprintf(buf, 128, "%d", KEY_RIGHT);
    setenv("key_right", buf, 1);
    snprintf(buf, 128, "%d", KEY_DOWN);
    setenv("key_down", buf, 1);
    snprintf(buf, 128, "%d", KEY_LEFT);
    setenv("key_left", buf, 1);
    snprintf(buf, 128, "%d", KEY_IC);
    setenv("key_insert", buf, 1);
    snprintf(buf, 128, "%d", KEY_DC);
    setenv("key_delete", buf, 1);
    snprintf(buf, 128, "%d", KEY_HOME);
    setenv("key_home", buf, 1);
    snprintf(buf, 128, "%d", KEY_END);
    setenv("key_end", buf, 1);
    snprintf(buf, 128, "%d", KEY_PPAGE);
    setenv("key_pageup", buf, 1);
    snprintf(buf, 128, "%d", KEY_NPAGE);
    setenv("key_pagedown", buf, 1);
    snprintf(buf, 128, "%d", KEY_F(13));
    setenv("key_meta_left", buf, 1);
    snprintf(buf,128, "%d", KEY_F(14));
    setenv("key_meta_right", buf, 1);
    snprintf(buf,128, "%d", KEY_F(15));
    setenv("key_meta_up", buf, 1);
    snprintf(buf,128, "%d", KEY_F(16));
    setenv("key_meta_down", buf, 1);
    snprintf(buf,128, "%d", KEY_F(17));
    setenv("key_ctrl_left", buf, 1);
    snprintf(buf,128, "%d", KEY_F(18));
    setenv("key_ctrl_right", buf, 1);
    snprintf(buf,128, "%d", KEY_F(19));
    setenv("key_ctrl_up", buf, 1);
    snprintf(buf,128, "%d", KEY_F(20));
    setenv("key_ctrl_down", buf, 1);
    snprintf(buf,128, "%d", KEY_F(21));
    setenv("key_ctrl_delete", buf, 1);
    snprintf(buf,128, "%d", 10);
    setenv("key_enter", buf, 1);
    snprintf(buf,128, "%d", KEY_BACKSPACE);
    setenv("key_backspace", buf, 1);
    snprintf(buf,128, "%d", KEY_F(1));
    setenv("key_f1", buf, 1);
    snprintf(buf,128, "%d", KEY_F(2));
    setenv("key_f2", buf, 1);
    snprintf(buf,128, "%d", KEY_F(3));
    setenv("key_f3", buf, 1);
    snprintf(buf,128, "%d", KEY_F(4));
    setenv("key_f4", buf, 1);
    snprintf(buf,128, "%d", KEY_F(5));
    setenv("key_f5", buf, 1);
    snprintf(buf,128, "%d", KEY_F(6));
    setenv("key_f6", buf, 1);
    snprintf(buf,128, "%d", KEY_F(7));
    setenv("key_f7", buf, 1);
    snprintf(buf,128, "%d", KEY_F(8));
    setenv("key_f8", buf, 1);
    snprintf(buf,128, "%d", KEY_F(9));
    setenv("key_f9", buf, 1);
    snprintf(buf,128, "%d", KEY_F(10));
    setenv("key_f10", buf, 1);
    snprintf(buf,128, "%d", KEY_F(11));
    setenv("key_f11", buf, 1);
    snprintf(buf,128, "%d", KEY_F(12));
    setenv("key_f12", buf, 1);
    snprintf(buf,128, "%d", 'a');
    setenv("key_a", buf, 1);
    snprintf(buf,128, "%d", 'b');
    setenv("key_b", buf, 1);
    snprintf(buf,128, "%d", 'c');
    setenv("key_c", buf, 1);
    snprintf(buf,128, "%d", 'd');
    setenv("key_d", buf, 1);
    snprintf(buf,128, "%d", 'e');
    setenv("key_e", buf, 1);
    snprintf(buf,128, "%d", 'f');
    setenv("key_f", buf, 1);
    snprintf(buf,128, "%d", 'g');
    setenv("key_g", buf, 1);
    snprintf(buf,128, "%d", 'h');
    setenv("key_h", buf, 1);
    snprintf(buf,128, "%d", 'i');
    setenv("key_i", buf, 1);
    snprintf(buf,128, "%d", 'j');
    setenv("key_j", buf, 1);
    snprintf(buf,128, "%d", 'k');
    setenv("key_k", buf, 1);
    snprintf(buf,128, "%d", 'l');
    setenv("key_l", buf, 1);
    snprintf(buf,128, "%d", 'm');
    setenv("key_m", buf, 1);
    snprintf(buf,128, "%d", 'n');
    setenv("key_n", buf, 1);
    snprintf(buf,128, "%d", 'o');
    setenv("key_o", buf, 1);
    snprintf(buf,128, "%d", 'p');
    setenv("key_p", buf, 1);
    snprintf(buf,128, "%d", 'q');
    setenv("key_q", buf, 1);
    snprintf(buf,128, "%d", 'r');
    setenv("key_r", buf, 1);
    snprintf(buf,128, "%d", 's');
    setenv("key_s", buf, 1);
    snprintf(buf,128, "%d", 't');
    setenv("key_t", buf, 1);
    snprintf(buf,128, "%d", 'u');
    setenv("key_u", buf, 1);
    snprintf(buf,128, "%d", 'v');
    setenv("key_v", buf, 1);
    snprintf(buf,128, "%d", 'w');
    setenv("key_w", buf, 1);
    snprintf(buf,128, "%d", 'x');
    setenv("key_x", buf, 1);
    snprintf(buf,128, "%d", 'y');
    setenv("key_y", buf, 1);
    snprintf(buf,128, "%d", 'z');
    setenv("key_z", buf, 1);
    snprintf(buf,128, "%d", 'A');
    setenv("key_A", buf, 1);
    snprintf(buf,128, "%d", 'B');
    setenv("key_B", buf, 1);
    snprintf(buf,128, "%d", 'C');
    setenv("key_C", buf, 1);
    snprintf(buf,128, "%d", 'D');
    setenv("key_D", buf, 1);
    snprintf(buf,128, "%d", 'E');
    setenv("key_E", buf, 1);
    snprintf(buf,128, "%d", 'F');
    setenv("key_F", buf, 1);
    snprintf(buf,128, "%d", 'G');
    setenv("key_G", buf, 1);
    snprintf(buf,128, "%d", 'H');
    setenv("key_H", buf, 1);
    snprintf(buf,128, "%d", 'I');
    setenv("key_I", buf, 1);
    snprintf(buf,128, "%d", 'J');
    setenv("key_J", buf, 1);
    snprintf(buf,128, "%d", 'K');
    setenv("key_K", buf, 1);
    snprintf(buf,128, "%d", 'L');
    setenv("key_L", buf, 1);
    snprintf(buf,128, "%d", 'M');
    setenv("key_M", buf, 1);
    snprintf(buf,128, "%d", 'N');
    setenv("key_N", buf, 1);
    snprintf(buf,128, "%d", 'O');
    setenv("key_O", buf, 1);
    snprintf(buf,128, "%d", 'P');
    setenv("key_P", buf, 1);
    snprintf(buf,128, "%d", 'Q');
    setenv("key_Q", buf, 1);
    snprintf(buf,128, "%d", 'R');
    setenv("key_R", buf, 1);
    snprintf(buf,128, "%d", 'S');
    setenv("key_S", buf, 1);
    snprintf(buf,128, "%d", 'T');
    setenv("key_T", buf, 1);
    snprintf(buf,128, "%d", 'U');
    setenv("key_U", buf, 1);
    snprintf(buf,128, "%d", 'V');
    setenv("key_V", buf, 1);
    snprintf(buf,128, "%d", 'W');
    setenv("key_W", buf, 1);
    snprintf(buf,128, "%d", 'X');
    setenv("key_X", buf, 1);
    snprintf(buf,128, "%d", 'Y');
    setenv("key_Y", buf, 1);
    snprintf(buf,128, "%d", 'Z');
    setenv("key_Z", buf, 1);
    snprintf(buf,128, "%d", 32);
    setenv("key_space", buf, 1);
    snprintf(buf,128, "%d", 0);
    setenv("key_ctrl_space", buf, 1);
    snprintf(buf,128, "%d", 1);
    setenv("key_ctrl_a", buf, 1);
    snprintf(buf,128, "%d", 2);
    setenv("key_ctrl_b", buf, 1);
    snprintf(buf,128, "%d", 3);
    setenv("key_ctrl_c", buf, 1);
    snprintf(buf,128, "%d", 4);
    setenv("key_ctrl_d", buf, 1);
    snprintf(buf,128, "%d", 5);
    setenv("key_ctrl_e", buf, 1);
    snprintf(buf,128, "%d", 6);
    setenv("key_ctrl_f", buf, 1);
    snprintf(buf,128, "%d", 7);
    setenv("key_ctrl_g", buf, 1);
    snprintf(buf,128, "%d", 8);
    setenv("key_ctrl_h", buf, 1);
    snprintf(buf,128, "%d", 9);
    setenv("key_ctrl_i", buf, 1);
    snprintf(buf,128, "%d", 10);
    setenv("key_ctrl_j", buf, 1);
    snprintf(buf,128, "%d", 11);
    setenv("key_ctrl_k", buf, 1);
    snprintf(buf,128, "%d", 12);
    setenv("key_ctrl_l", buf, 1);
    snprintf(buf,128, "%d", 13);
    setenv("key_ctrl_m", buf, 1);
    snprintf(buf,128, "%d", 14);
    setenv("key_ctrl_n", buf, 1);
    snprintf(buf,128, "%d", 15);
    setenv("key_ctrl_o", buf, 1);
    snprintf(buf,128, "%d", 16);
    setenv("key_ctrl_p", buf, 1);
    snprintf(buf,128, "%d", 17);
    setenv("key_ctrl_q", buf, 1);
    snprintf(buf,128, "%d", 18);
    setenv("key_ctrl_r", buf, 1);
    snprintf(buf,128, "%d", 19);
    setenv("key_ctrl_s", buf, 1);
    snprintf(buf,128, "%d", 20);
    setenv("key_ctrl_t", buf, 1);
    snprintf(buf,128, "%d", 21);
    setenv("key_ctrl_u", buf, 1);
    snprintf(buf,128, "%d", 22);
    setenv("key_ctrl_v", buf, 1);
    snprintf(buf,128, "%d", 23);
    setenv("key_ctrl_w", buf, 1);
    snprintf(buf,128, "%d", 24);
    setenv("key_ctrl_x", buf, 1);
    snprintf(buf,128, "%d", 25);
    setenv("key_ctrl_y", buf, 1);
    snprintf(buf,128, "%d", 26);
    setenv("key_ctrl_z", buf, 1);
    snprintf(buf,128, "%d", 27);
    setenv("key_escape", buf, 1);
    snprintf(buf,128, "%d", 9);
    setenv("key_tab", buf, 1);
    snprintf(buf,128, "%d", '0');
    setenv("key_0", buf, 1);
    snprintf(buf,128, "%d", '1');
    setenv("key_1", buf, 1);
    snprintf(buf,128, "%d", '2');
    setenv("key_2", buf, 1);
    snprintf(buf,128, "%d", '3');
    setenv("key_3", buf, 1);
    snprintf(buf,128, "%d", '4');
    setenv("key_4", buf, 1);
    snprintf(buf,128, "%d", '5');
    setenv("key_5", buf, 1);
    snprintf(buf,128, "%d", '6');
    setenv("key_6", buf, 1);
    snprintf(buf,128, "%d", '7');
    setenv("key_7", buf, 1);
    snprintf(buf,128, "%d", '8');
    setenv("key_8", buf, 1);
    snprintf(buf,128, "%d", '9');
    setenv("key_9", buf, 1);
    snprintf(buf,128, "%d", '!');
    setenv("key_exclam", buf, 1);
    snprintf(buf,128, "%d", '"');
    setenv("key_dquote", buf, 1);
    snprintf(buf,128, "%d", '#');
    setenv("key_sharp", buf, 1);
    snprintf(buf,128, "%d", '$');
    setenv("key_dollar", buf, 1);
    snprintf(buf,128, "%d", '%');
    setenv("key_percent", buf, 1);
    snprintf(buf,128, "%d", '&');
    setenv("key_and", buf, 1);
    snprintf(buf,128, "%d", '\'');
    setenv("key_squote", buf, 1);
    snprintf(buf,128, "%d", '(');
    setenv("key_lparen", buf, 1);
    snprintf(buf,128, "%d", ')');
    setenv("key_rparen", buf, 1);
    snprintf(buf,128, "%d", '~');
    setenv("key_tilda", buf, 1);
    snprintf(buf,128, "%d", '=');
    setenv("key_equal", buf, 1);
    snprintf(buf,128, "%d", '-');
    setenv("key_minus", buf, 1);
    snprintf(buf,128, "%d", '^');
    setenv("key_cup", buf, 1);
    snprintf(buf,128, "%d", '|');
    setenv("key_vbar", buf, 1);
    snprintf(buf,128, "%d", '\\');
    setenv("key_backslash", buf, 1);
    snprintf(buf,128, "%d", '@');
    setenv("key_atmark", buf, 1);
    snprintf(buf,128, "%d", '`');
    setenv("key_bapostrophe", buf, 1);
    snprintf(buf,128, "%d", '{');
    setenv("key_lcurly", buf, 1);
    snprintf(buf,128, "%d", '[');
    setenv("key_lbrack", buf, 1);
    snprintf(buf,128, "%d", '+');
    setenv("key_plus", buf, 1);
    snprintf(buf,128, "%d", ';');
    setenv("key_semicolon", buf, 1);
    snprintf(buf,128, "%d", '*');
    setenv("key_star", buf, 1);
    snprintf(buf,128, "%d", ':');
    setenv("key_colon", buf, 1);
    snprintf(buf,128, "%d", '}');
    setenv("key_rcurly", buf, 1);
    snprintf(buf,128, "%d", ']');
    setenv("key_rbrack", buf, 1);
    snprintf(buf,128, "%d", '<');
    setenv("key_lss", buf, 1);
    snprintf(buf,128, "%d", ',');
    setenv("key_comma", buf, 1);
    snprintf(buf,128, "%d", '>');
    setenv("key_gtr", buf, 1);
    snprintf(buf,128, "%d", '.');
    setenv("key_dot", buf, 1);
    snprintf(buf,128, "%d", '/');
    setenv("key_slash", buf, 1);
    snprintf(buf,128, "%d", '?');
    setenv("key_qmark", buf, 1);
    snprintf(buf,128, "%d", '_');
    setenv("key_underbar", buf, 1);
    snprintf(buf,128, "%d", 0);
    setenv("nometa", buf, 1);
    snprintf(buf,128, "%d", 1);
    setenv("meta", buf, 1);
    snprintf(buf,128, "%d", kCAReverse);
    setenv("ma_reverse", buf, 1);
    snprintf(buf,128, "%d", kCABold);
    setenv("ma_bold", buf, 1);
    snprintf(buf,128, "%d", kCAUnderline);
    setenv("ma_underline", buf, 1);
    snprintf(buf,128, "%d", kCAWhite);
    setenv("ma_white", buf, 1);
    snprintf(buf,128, "%d", kCABlue);
    setenv("ma_blue", buf, 1);
    snprintf(buf,128, "%d", kCACyan);
    setenv("ma_cyan", buf, 1);
    snprintf(buf,128, "%d", kCAGreen);
    setenv("ma_green", buf, 1);
    snprintf(buf,128, "%d", kCAYellow);
    setenv("ma_yellow", buf, 1);
    snprintf(buf,128, "%d", kCAMagenta);
    setenv("ma_magenta", buf, 1);
    snprintf(buf,128, "%d", kCARed);
    setenv("ma_red", buf, 1);
}

int main(int argc, char* argv[])
{
    /// メモリリークの監視を開始 ///
    CHECKML_BEGIN();

#if defined(HAVE_GC_H) || defined(HAVE_GC_GC_H)
    /// GC 開始 ///
    GC_init();
#endif

    /// ファイラでもスクリプトでも共通の環境変数を初期化 ///
    setenv("VERSION", "4.4.3", 1);

    /// mfiler3のホームディレクトリを保存しておく ///
    char* home = getenv("HOME");
    if(home == NULL) {
        fprintf(stderr, "$HOME is NULL.exit");
        exit(1);
    }
    snprintf(gHomeDir, PATH_MAX, "%s/.mfiler3", home);

    if(access(gHomeDir, F_OK) != 0) {
        char buf[PATH_MAX];
        snprintf(buf, PATH_MAX, "mkdir -p %s", gHomeDir);
        if(system(buf) < 0) {
            fprintf(stderr, "can't make directory (%s)", gHomeDir);
            exit(1);
        }
    }

    if(chmod(gHomeDir, S_IRUSR|S_IWUSR|S_IXUSR) < 0) {
        fprintf(stderr, "can't chmod %s", gHomeDir);
        exit(1);
    }

    /// mfiler3の一時ディレクトリを保存しておく ///
    snprintf(gTempDir, PATH_MAX, "%s/temp", gHomeDir);
    if(access(gTempDir, F_OK) != 0) {
        if(mkdir(gTempDir, 0700) < 0) {
            fprintf(stderr, "can't mkdir %s", gTempDir);
            exit(1);
        }
    }

    if(chmod(gTempDir, S_IRUSR|S_IWUSR|S_IXUSR) < 0) {
        fprintf(stderr, "can't chmod %s", gTempDir);
        exit(1);
    }

    /// 乱数初期化 ///
    srandom(1000);

    /// 端末の設定を保存 ///
    //msave_ttysettings();

    /// 端末を初期化 ///
    mreset_tty();

    /// オプション処理 ///
    char send_cmd[BUFSIZ];          // コマンドがセットされていたら
    char file_name[PATH_MAX];       // ファイル名が引数の場合
    file_name[0] = 0;

    int i;
    int pid;
    for(i=1 ; i<argc; i++){
        // ファイル名
        if(argv[i][0] != '-') {         // オプションじゃなければ
            xstrncpy(file_name, argv[i], PATH_MAX);     // ファイル名とみなす
        }
        // 引数
        else {
            if(strcmp(argv[i], "--version") == 0) version();

            switch (argv[i][1]){
#if defined(ENABLE_SOCKET)
                case 'E':
                    if(i+2 < argc && atoi(argv[i+1]) != 0) {
                        pid = atoi(argv[i+1]);
                        snprintf(send_cmd, BUFSIZ, "E%s", argv[i+2]);
                        i+=2;
                    }
                    else if(i+1 < argc) {
                        pid = -1;
                        snprintf(send_cmd, BUFSIZ, "E%s", argv[i+1]);
                        i++;
                    }
                    else {
                        usage();
                    }
                    break;

                case 'e':
                    if(i+2 < argc && atoi(argv[i+1]) != 0) {
                        pid = atoi(argv[i+1]);
                        snprintf(send_cmd, BUFSIZ, "e%s", argv[i+2]);
                        i+=2;
                    }
                    else if(i+1 < argc) {
                        pid = -1;
                        snprintf(send_cmd, BUFSIZ, "e%s", argv[i+1]);
                        i++;
                    }
                    else {
                        usage();
                    }
                    break;
#endif

                case 'c':
                    if(i+1 < argc) {
                        snprintf(send_cmd, BUFSIZ, "c%s", argv[i+1]);
                        i++;
                    }
                    else {
                        usage();
                    }
                    break;

                default:
                    usage();
            }
        }
    }

#if defined(ENABLE_SOCKET)
    /// オプションで指定されているなら動いている ///
    /// mfiler3にメッセージを送る  ///
    if(send_cmd[0] == 'E') {
        if(pid != -1) {
            char soc_name[PATH_MAX];
            snprintf(soc_name, PATH_MAX, "%s/socket%d", gTempDir, pid);

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

                struct sockaddr_un addr;
                addr.sun_family = AF_UNIX;
                xstrncpy(addr.sun_path, soc_name, sizeof(addr.sun_path));
                if(connect(soc, (struct sockaddr *)&addr
                        , sizeof(addr)) == 0) 
                {
                    write(soc, send_cmd, strlen(send_cmd)+1);
                }

                close(soc);
            }
            else {
                fprintf(stderr, "no mfiler3 process\n");
                return 1;
            }
            
            return 0;
        }
        else {
            DIR* dir = opendir(gTempDir);
            if(dir == NULL) {
                fprintf(stderr, "Tempolary directory doesn't exist");
                exit(1);
            }

            struct dirent* entry;
            while(entry = readdir(dir)) {
                if(strstr(entry->d_name, "socket")) {
                    char soc_name[PATH_MAX];
                    snprintf(soc_name, PATH_MAX, "%s/%s", gTempDir, entry->d_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;
                    xstrncpy(addr.sun_path, soc_name, sizeof(addr.sun_path));
                    if(connect(soc, (struct sockaddr *)&addr
                            , sizeof(addr)) == 0) 
                    {
                        write(soc, send_cmd, strlen(send_cmd)+1);
                    }

                    close(soc);
                }
            }
            
            return 0;
        }
    }

    /// オプションで指定されているなら動いている ///
    /// mfiler3にメッセージを送りコマンド結果を得る  ///
    if(send_cmd[0] == 'e') {
        if(gTerminalKanjiCode == kTKUtf8) 
            setlocale(LC_CTYPE, "ja_JP.UTF-8");

        saphire_init(kATOptC, FALSE, kRSObject, FALSE); 

        if(pid != -1) {
            main_arg_e(send_cmd, pid);
        }
        else {
            saphire_set_signal();
            string_obj* ret = STRING_NEW("");
            sRFd* rfd = sRFd_new(0);
            saphire_shell3(ret, " pidof mfiler3", "pidof mfiler3", rfd);
            saphire_sweep();

            char* p = string_c_str(ret);
            int pid = 0;
            while(*p) {
                if(*p >= '0' && *p <= '9') {
                    int pid = 0;
                    while(*p >= '0' && *p <= '9') {
                        pid = pid * 10 + *p++ - '0';
                    }
                    main_arg_e(send_cmd, pid);
                }
                else {
                    p++;
                }
            }
        }

        saphire_final();

        return 0;
    }
#endif

    /// 標準入力と出力が制御端末かどうか確認 ///
    if(!isatty(0) || !isatty(1)) {
        fprintf(stderr, "standard input is not a tty\n");
        return 1;
    }

    /// 環境変数設定 ///
    set_mfenv();

    /// モジュールの初期化 ///
    cmdline_init();
    menu_init();
    filer_init();
    saphire_init(kATConsoleApp, TRUE, kRSObject, FALSE);
    cmdline_completion_init();
    commands_init();

    /// 初期キーバインドと初期フック ///
    filer_add_keycommand("filer", 0, 'q', "*", "quit", "system");
    filer_add_keycommand("filer", 0, 3, "*", "quit", "system"); // CTRL-C

    /// ソケットの初期化 ///
    char soc_name[PATH_MAX];
    snprintf(soc_name, PATH_MAX, "%s/socket%d", gTempDir, 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;
    xstrncpy(addr.sun_path, soc_name, sizeof(addr.sun_path));
    if(bind(soc, (struct sockaddr *)&addr, sizeof(addr)) == -1) {
        perror("bind");
        return 1;
    }

    /// 名前付きパイプ初期化 ///
    char pipe_name[PATH_MAX];
    snprintf(pipe_name, PATH_MAX, "%s/pipe_%d", gTempDir, getpid());

    mkfifo(pipe_name, 0600);

    /// シグナルを設定 ///
    set_signal_mfiler();

    /// localeの設定 ///
    if(gTerminalKanjiCode == kTKUtf8) setlocale(LC_CTYPE, "ja_JP.UTF-8");

    /// atexit登録 ///
    atexit(atexit_fun);

    /// フックの初期化 ///
    init_hooks();

    /// ランタイムスクリプト実行 ///
    read_rc_file_compiled();

    /// ディレクトリを作成 ///
    if(vector_size(gDirs) == 0) {
        if(strcmp(file_name, "") == 0) {
            char cwd[PATH_MAX];
            mygetcwd(cwd, PATH_MAX);
            xstrncat(cwd, "/", PATH_MAX);

            if(filer_new_dir(cwd, FALSE, ".+") < 0) {
                fprintf(stderr, "invalid current directory\n");
                exit(1);
            }
            if(filer_new_dir(cwd, FALSE, ".+") < 0) {
                fprintf(stderr, "invalid current directory\n");
                exit(1);
            }
        }
        else {
            if(filer_new_dir(file_name, FALSE, ".+") < 0) {
                fprintf(stderr, "invalid directory(%s)\n", file_name);
                exit(1);
            }
            if(filer_new_dir(file_name, FALSE, ".+") < 0) {
                fprintf(stderr, "invalid directory(%s)\n", file_name);
                exit(1);
            }
        }
    }
    else if(vector_size(gDirs) == 1) {
        if(strcmp(file_name, "") == 0) {
            char cwd[PATH_MAX];
            mygetcwd(cwd, PATH_MAX);

            if(filer_new_dir(cwd, FALSE, ".+") < 0) {
                fprintf(stderr, "invalid current directory\n");
                exit(1);
            }
        }
        else {
            if(filer_new_dir(file_name, FALSE, ".+") < 0) {
                fprintf(stderr, "invalid directory(%s)\n", file_name);
                exit(1);
            }
        }
    }

    filer_activate(0);

    /// リハッシュ ///
    //saphire_job_done = ;
    saphire_rehash();

    /// モジュールの初期化(ランタイムスクリプト以降の方が良い奴 ///
    isearch_init();
    
    /// カーシスの起動 ///
    minitscr();

    /// メインループ ///
    gMainLoop = -1;

    fd_set mask;
    fd_set read_ok;

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

    while(gMainLoop == -1) {
        /// 描写 ///
        mclear();
        view(TRUE);
        mrefresh();

        /// selectで入力待ち ///
        read_ok = mask;
        if(!mkbuf_exist()) {     // キーボードのバッファがないならselect
            if(saphire_job_num() > 0) {
                struct timeval tv;
                tv.tv_sec = 5;
                tv.tv_usec = 0;

                select(soc + 1, &read_ok, NULL, NULL, &tv);
            }
            else {
                select(soc + 1, &read_ok, NULL, NULL, NULL);
            }
        }


        /// キー処理 ///
        if(FD_ISSET(0, &read_ok) || mkbuf_exist()) {    // キー入力があったか、キーボードのバッファがあるなら
            /// コマンドライン入力中 ///
            if(gCmdLineActive) {
                while(1) {
                    /// 漢字入力のためselectで入力を見張る ///
                    struct timeval tv;
                    tv.tv_sec = 0;
                    tv.tv_usec = 0;

                    read_ok = mask;

                    select(1, &read_ok, NULL, NULL, &tv);

                    /// キー入力中 ///
                    if(FD_ISSET(0, &read_ok) || mkbuf_exist()) {
                        int meta;
                        int key = mgetch(&meta);

                        if(key == -1) {
                        }
                        else if(meta) {
                            input(1, key);
                        }
                        /// normal key ///
                        else {
                           input(0, key);
                        }
                    }
                    /// キー入力が終わった ///
                    else {
                        break;
                    }
                }
            }

            /// その他 ///
            else {
                int meta;
                int key = mgetch_nonblock(&meta);

                /// ESCキー ///
                if(key == -1) {
                }
                else if(meta) {
                    input(1, key);
                }
                
                /// meta key ///
                else if(key >= kKeyMetaFirst 
                        && key <= kKeyMetaFirst + 127) 
                {
                    input(1, key - kKeyMetaFirst );
                }

                /// normal key ///
                else {
                   input(0, key);
                }
            }
        }

#if defined(ENABLE_SOCKET)
        /// UNIX Domainソケット処理 ///
        else if(FD_ISSET(soc, &read_ok)) {
            char buf[BUFSIZ];
            
            struct sockaddr_un caddr;
            socklen_t caddr_len;
            int len = recvfrom(soc, buf, BUFSIZ, 0, (struct sockaddr *)&caddr, &caddr_len);


            if(buf[0] == 'E') {
                mclear();
                mmove(0,0);
                mrefresh();

                mendwin();
                mreset_tty();

                saphire_set_signal();
                sRFd* pipein = sRFd_new(STDIN_FILENO);
                sWFd* pipeout = sWFd_new(STDOUT_FILENO);
                int rcode = saphire_shell(buf + 1, "mfiler3 -e", pipeout, pipein, STDERR_FILENO);
                saphire_sweep();
                sWFd_flash(pipeout);

                putp(tigetstr("rev"));
                printf("HIT ANY KEY");
                putp(tigetstr("sgr0"));
                fflush(stdout);

                minitscr();

                if(strcmp(getenv("VIEW_OPTION"), "2pain") == 0) {
                    filer_reread(0);
                    filer_reread(1);
                }
                else {
                    filer_reread(adir());
                }
            
                int meta;
                mgetch(&meta);

                mclear_immediately();
            }
            else if(buf[0] == 'e') {
                saphire_set_signal();
                string_obj* str = STRING_NEW("");
                sRFd* rfd = sRFd_new(0);
                saphire_shell3(str, buf + 1, "mfiler3 -E");
                saphire_sweep();
                saphire_sweep();

                int pipe = open(pipe_name, O_WRONLY);
                write(pipe, string_c_str(str), string_size(str) +1);
                close(pipe);
            }
        }
#endif

        /// ジョブ処理 ///
        saphire_wait_background_job();
    }

    /// ごみソケットとパイプの掃除 ///
    string_obj* ret = STRING_NEW("");
    sRFd* rfd = sRFd_new(0);
    saphire_shell3(ret, " pidof mfiler3", "clean mfiler3 sockets", rfd);
    saphire_sweep();

    DIR* dir = opendir(gTempDir);
    if(dir == NULL) {
        fprintf(stderr, "Tempolary directory doesn't exist");
        exit(1);
    }

    struct dirent* entry;
    while(entry = readdir(dir)) {
        if(strstr(entry->d_name, "socket")) {
            int pid = atoi(strstr(entry->d_name, "socket") + 6);

            if(pid != getpid()) {
                char pid_buf[BUFSIZ];
                snprintf(pid_buf, BUFSIZ, "%d", pid);
                if(strstr(string_c_str(ret), pid_buf) == NULL) {
                    char path[PATH_MAX];
                    snprintf(path, PATH_MAX, "%s/%s", gTempDir, entry->d_name);
                    unlink(path);
                }
            }
        }
        else if(strstr(entry->d_name, "pipe_")) {
            int pid = atoi(strstr(entry->d_name, "pipe_") + 5);

            if(pid != getpid()) {
                char pid_buf[BUFSIZ];
                snprintf(pid_buf, BUFSIZ, "%d", pid);
                if(strstr(string_c_str(ret), pid_buf) == NULL) {
                    char path[PATH_MAX];
                    snprintf(path, PATH_MAX, "%s/%s", gTempDir, entry->d_name);
                    unlink(path);
                }
            }
        }
    }

    closedir(dir);

    /// カーシス終わり ///
    mendwin();

    /// ヒストリの書き込み ///
    cmdline_write_history();

    /// フック呼び出し ///
    puts(""); // 見た目上のため
    sRFd* pipein = sRFd_new(STDIN_FILENO);
    sWFd* pipeout = sWFd_new(STDOUT_FILENO);
    int rcode = saphire_shell("atexit_hook", NULL, pipeout, pipein, STDERR_FILENO);
    saphire_sweep();
    sWFd_flash(pipeout);
    saphire_kill_all_jobs();

    /// モジュール解放 ///
    cmdline_final();
    cmdline_completion_final();
    isearch_final();
    mcurses_final();
    kanji_final();
    menu_final();
    filer_final();
    saphire_final();
    commands_final();

    /// ソケット解放 ///
    unlink(soc_name);

    /// パイプ開放 ///
    unlink(pipe_name);

    mreset_tty();
    //mrestore_ttysettings();

    /// メモリリーク検出 ///
    CHECKML_END();

    /// 改行 ///
    puts("");

    mreset_tty();

    return gMainLoop;
}

