#include "common.h"

extern "C" {
#include <libgen.h>
#include <signal.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <pwd.h>
}

enum eUndoOp { kUndoNil, kUndoCopy, kUndoMove, kUndoTrashBox };
eUndoOp gUndoOp = kUndoNil;
VALUE gUndoSdir;
VALUE gUndoDdir;
VALUE gUndoFiles;

char gHostName[256];
char gUserName[256];


VALUE mf_keycommand(VALUE self, VALUE rmeta, VALUE rkeycode, VALUE rextension, VALUE rcommand)
{
TBEGIN();

    Check_Type(rmeta, T_FIXNUM);
    Check_Type(rkeycode, T_FIXNUM);
    Check_Type(rextension, T_STRING);
    Check_Type(rcommand, T_STRING);

M(("rcommand %s", RSTRING(rcommand)->ptr));

    int meta = NUM2INT(rmeta);
    int keycode = NUM2INT(rkeycode);
    char* extension = RSTRING(rextension)->ptr;
    char* command = RSTRING(rcommand)->ptr;

    hash_put(gKeyCommand[meta][keycode], extension, STRDUP(command));

//M(("%d %d %s %s (%s)\n", meta, keycode, extension, command, RSTRING(gKeyCommand[meta][keycode][extension])->ptr));


TEND();                 
    return Qnil;
}

VALUE mf_exit(VALUE self)
{
TBEGIN();
    if(gCheckExit) {
        const char* str[] = {
            "Yes", "No"
        };

        int ret = select_str("exit ok?", (char**)str, 2, 1);

        if(ret == 1) return Qnil;

        if(vector_size(gTty) > 0) {
            int ret = select_str("you have running tty. Are you sure to quit?", (char**)str, 2, 1);
            if(ret == 1) return Qnil;
        }
    }

    gMainLoop = false;

TEND();    
    return Qnil;
}

VALUE mf_cursor_move(VALUE self, VALUE rvalue)
{
TBEGIN();

    Check_Type(rvalue, T_FIXNUM);
    
    ActiveDir()->MoveCursor(NUM2INT(rvalue));

TEND();
    
    return Qnil;
}

VALUE mf_cursor_left(VALUE self)
{
TBEGIN();

    if(gRDir->mActive) gLDir->Activate(gRDir);
    
TEND();
    
    return Qnil;
}

VALUE mf_cursor_right(VALUE self)
{
TBEGIN();

    if(gLDir->mActive) gRDir->Activate(gLDir);

TEND();
    
    return Qnil;
}

VALUE mf_cursor_other(VALUE self)
{
TBEGIN();

    if(gLDir->mActive)
        gRDir->Activate(gLDir);
    else
        gLDir->Activate(gRDir); 

TEND();
    
    return Qnil;
}

VALUE mf_dir_move(VALUE self, VALUE rdir)
{
TBEGIN();

    Check_Type(rdir, T_STRING);
    
    ActiveDir()->Move(RSTRING(rdir)->ptr);

TEND();    

    return Qnil;
}

VALUE mf_sdir_move(VALUE self, VALUE rdir)
{
TBEGIN();

    Check_Type(rdir, T_STRING);
    
    SleepDir()->Move(RSTRING(rdir)->ptr);

TEND();    

    return Qnil;
}

VALUE mf_view_nameonly(VALUE self)
{
TBEGIN();

    cDirWnd::gViewNameOnly = !cDirWnd::gViewNameOnly;

    ActiveDir()->MoveCursor(0);
    SleepDir()->MoveCursor(0);

TEND();

    return Qnil;
}

VALUE mf_view_all(VALUE self)
{
TBEGIN();

    cDirWnd::gViewOption = cDirWnd::kAll;

    ActiveDir()->MoveCursor(0);
    SleepDir()->MoveCursor(0);

TEND();

    return Qnil;
}

VALUE mf_view_onedir(VALUE self)
{
TBEGIN();

    cDirWnd::gViewOption = cDirWnd::kOneDir;

    ActiveDir()->MoveCursor(0);
    SleepDir()->MoveCursor(0);

TEND();

    return Qnil;
}

VALUE mf_view_onedir2(VALUE self)
{
TBEGIN();

    cDirWnd::gViewOption = cDirWnd::kOneDir2;

    ActiveDir()->MoveCursor(0);
    SleepDir()->MoveCursor(0);

TEND();

    return Qnil;
}

VALUE mf_view_onedir3(VALUE self)
{
TBEGIN();

    cDirWnd::gViewOption = cDirWnd::kOneDir3;

    ActiveDir()->MoveCursor(0);
    SleepDir()->MoveCursor(0);

TEND();

    return Qnil;
}

VALUE mf_view_onedir5(VALUE self)
{
TBEGIN();

    cDirWnd::gViewOption = cDirWnd::kOneDir5;

    ActiveDir()->MoveCursor(0);
    SleepDir()->MoveCursor(0);

TEND();

    return Qnil;
}

VALUE mf_isearch(VALUE self)
{
TBEGIN();

    gISearch = !gISearch;
    
TEND();

    return Qnil;
}

VALUE mf_is_isearch_on(VALUE self)
{
    if(gISearch)
        return Qtrue;
    else
        return Qfalse;
}

VALUE mf_isearch_off(VALUE self)
{
TBEGIN();

    gISearch = false;
    
TEND();

    return Qnil;
}

VALUE mf_isearch_on(VALUE self)
{
TBEGIN();

    gISearch = true;
    
TEND();

    return Qnil;
}


VALUE mf_option_remain_marks(VALUE self, VALUE boolean)
{
TBEGIN();

    cDirWnd::gRemainMarks = (boolean == Qtrue);

TEND();

    return Qnil;
}

VALUE mf_is_remain_marks(VALUE self)
{
    return cDirWnd::gRemainMarks ? Qtrue: Qfalse;
}


VALUE mf_option_color(VALUE self, VALUE boolean)
{
TBEGIN();

    gColor = (boolean == Qtrue);

TEND();

    return Qnil;
}

VALUE mf_view_color(VALUE self)
{
TBEGIN();

    gColor = !gColor;

TEND();

    return Qnil;
}


VALUE mf_option_bold_exe(VALUE self, VALUE boolean)
{
TBEGIN();

    gBoldExe = (boolean == Qtrue);

TEND();

    return Qnil;
}

VALUE mf_option_bold_dir(VALUE self, VALUE boolean)
{
TBEGIN();

    gBoldDir = (boolean == Qtrue);

TEND();

    return Qnil;
}

VALUE mf_option_color_mark(VALUE self, VALUE color)
{
TBEGIN();
    Check_Type(color, T_FIXNUM);

    gColorMark = NUM2INT(color);
TEND();    

    return Qnil;
}

VALUE mf_option_color_exe(VALUE self, VALUE color)
{
TBEGIN();
    Check_Type(color, T_FIXNUM);

    gColorExe = NUM2INT(color);
TEND();    

    return Qnil;
}

VALUE mf_option_color_dir(VALUE self, VALUE color)
{
TBEGIN();
    Check_Type(color, T_FIXNUM);

    gColorDir = NUM2INT(color);
TEND();    

    return Qnil;
}

VALUE mf_option_color_link(VALUE self, VALUE color)
{
TBEGIN();
    Check_Type(color, T_FIXNUM);

    gColorLink = NUM2INT(color);
TEND();    

    return Qnil;
}

VALUE mf_option_individual_cursor(VALUE self, VALUE boolean)
{
TBEGIN();

    gIndividualCursor = (boolean == Qtrue);

TEND();

    return Qnil;
}

VALUE mf_option_isearch_decision_way(VALUE self, VALUE way)
{
TBEGIN();

    Check_Type(way, T_STRING);

    if(strcmp(RSTRING(way)->ptr, "enter_decision") == 0) {
        gISearchMismatchFinish = false;
        gISearchEnterDecision = true;
    }
    else if(strcmp(RSTRING(way)->ptr, "mismatch_finish") == 0) {
        gISearchMismatchFinish = true;
        gISearchEnterDecision = false;
    }
    else  if(strcmp(RSTRING(way)->ptr, "slash_toggle") == 0) {
        gISearchMismatchFinish = false;
        gISearchEnterDecision = false;
    }

TEND();

    return Qnil;
}

VALUE mf_option_shift_isearch(VALUE self, VALUE boolean)
{
TBEGIN();

    gShiftISearch = (boolean == Qtrue);

TEND();

    return Qnil;
}

VALUE mf_option_check_delete_file(VALUE self, VALUE boolean)
{
TBEGIN();

    gCheckDeleteFile = (boolean == Qtrue);

TEND();

    return Qnil;
}

VALUE mf_option_check_copy_file(VALUE self, VALUE boolean)
{
TBEGIN();

    gCheckCopyFile = (boolean == Qtrue);

TEND();

    return Qnil;
}

VALUE mf_option_check_exit(VALUE self, VALUE boolean)
{
TBEGIN();

    gCheckExit = (boolean == Qtrue);

TEND();

    return Qnil;
}

VALUE mf_option_trashbox_name(VALUE self, VALUE name)
{
TBEGIN();

    Check_Type(name, T_STRING);

    strcpy(gTrashBoxName, RSTRING(name)->ptr);

TEND();

    return Qnil;
}

VALUE mf_option_kanjicode(VALUE self, VALUE name)
{
TBEGIN();

    Check_Type(name, T_STRING);

    if(strcmp(RSTRING(name)->ptr, "sjis") == 0){
        gKanjiCode = kSjis;
    }
    else if(strcmp(RSTRING(name)->ptr, "utf8") == 0) {
        gKanjiCode = kUtf;
    }
    else if(strcmp(RSTRING(name)->ptr, "euc") == 0) {
        gKanjiCode = kEuc;
    }

TEND();

    return Qnil;
}

VALUE mf_option_gnu_screen(VALUE self, VALUE boolean)
{
TBEGIN();

    gGnuScreen = (boolean == Qtrue);

TEND();

    return Qnil;
}

VALUE mf_option_xterm(VALUE self, VALUE boolean)
{
TBEGIN();

    gXterm = (boolean == Qtrue);

TEND();

    return Qnil;
}

VALUE mf_option_pty(VALUE self, VALUE boolean)
{
TBEGIN();

    gPty = (boolean == Qtrue);

TEND();

    return Qnil;
}

VALUE mf_option_remain_cursor(VALUE self, VALUE boolean)
{
TBEGIN();

    gRemainCursor = (boolean == Qtrue);

TEND();

    return Qnil;
}

VALUE mf_option_auto_rehash(VALUE self, VALUE boolean)
{
TBEGIN();

    gAutoRehash = (boolean == Qtrue);

TEND();

    return Qnil;
}

VALUE mf_option_read_dir_history(VALUE self, VALUE boolean)
{
TBEGIN();

    gReadDirHistory = (boolean == Qtrue);

TEND();

    return Qnil;
}

VALUE mf_option_select(VALUE self, VALUE boolean)
{
TBEGIN();

    gSelect = (boolean == Qtrue);

TEND();

    return Qnil;
}

VALUE mf_sort_toggle_dir_up(VALUE self)
{
TBEGIN();

    cDirWnd::gSortDirUp = !cDirWnd::gSortDirUp;

    gLDir->Sort();
    gRDir->Sort();

TEND();

    return Qnil;
}

VALUE mf_sort_name(VALUE self)
{
TBEGIN();

    cDirWnd::gSortKind = cDirWnd::kName;

    gLDir->Sort();
    gRDir->Sort();

TEND();    

    return Qnil;
}

VALUE mf_sort_name_reverse(VALUE self)
{
TBEGIN();

    cDirWnd::gSortKind = cDirWnd::kNameReverse;

    gLDir->Sort();
    gRDir->Sort();

TEND();    

    return Qnil;
}

VALUE mf_sort_ext(VALUE self)
{
TBEGIN();

    cDirWnd::gSortKind = cDirWnd::kExt;

    gLDir->Sort();
    gRDir->Sort();

TEND();
    
    return Qnil;
}

VALUE mf_sort_ext_reverse(VALUE self)
{
TBEGIN();

    cDirWnd::gSortKind = cDirWnd::kExtReverse;

    gLDir->Sort();
    gRDir->Sort();

TEND();
    
    return Qnil;
}

VALUE mf_sort_size(VALUE self)
{
TBEGIN();

    cDirWnd::gSortKind = cDirWnd::kSize;

    gLDir->Sort();
    gRDir->Sort();

TEND();    

    return Qnil;
}

VALUE mf_sort_size_reverse(VALUE self)
{
TBEGIN();

    cDirWnd::gSortKind = cDirWnd::kSizeReverse;

    gLDir->Sort();
    gRDir->Sort();

TEND();    

    return Qnil;
}

VALUE mf_sort_time(VALUE self)
{
TBEGIN();

    cDirWnd::gSortKind = cDirWnd::kTime;

    gLDir->Sort();
    gRDir->Sort();

TEND();    

    return Qnil;
}

VALUE mf_sort_time_reverse(VALUE self)
{
TBEGIN();

    cDirWnd::gSortKind = cDirWnd::kTimeReverse;

    gLDir->Sort();
    gRDir->Sort();

TEND();    

    return Qnil;
}

VALUE mf_sort_user(VALUE self)
{
TBEGIN();

    cDirWnd::gSortKind = cDirWnd::kUser;

    gLDir->Sort();
    gRDir->Sort();

TEND();    

    return Qnil;
}

VALUE mf_sort_user_reverse(VALUE self)
{
TBEGIN();

    cDirWnd::gSortKind = cDirWnd::kUserReverse;

    gLDir->Sort();
    gRDir->Sort();

TEND();    

    return Qnil;
}

VALUE mf_sort_group(VALUE self)
{
TBEGIN();

    cDirWnd::gSortKind = cDirWnd::kGroup;

    gLDir->Sort();
    gRDir->Sort();

TEND();    

    return Qnil;
}

VALUE mf_sort_group_reverse(VALUE self)
{
TBEGIN();

    cDirWnd::gSortKind = cDirWnd::kGroupReverse;

    gLDir->Sort();
    gRDir->Sort();

TEND();    

    return Qnil;
}

VALUE mf_mark_all(VALUE self)
{
TBEGIN();

    ActiveDir()->MarkAll();

TEND();    

    return Qnil;
}

VALUE mf_mark_all_files(VALUE self)
{
TBEGIN();

    ActiveDir()->MarkAllFiles();

TEND();    

    return Qnil;
}

VALUE mf_mark(VALUE self)
{
TBEGIN();

    ActiveDir()->Mark();
    ActiveDir()->MoveCursor(ActiveDir()->Cursor() + 1);

TEND();    
    
    return Qnil;
}

VALUE mf_mark2(VALUE self, VALUE num)
{
TBEGIN();

    Check_Type(num, T_FIXNUM);

    ActiveDir()->Mark2(NUM2INT(num));

TEND();    
    
    return Qnil;
}

VALUE mf_mark_on(VALUE self, VALUE num)
{
TBEGIN();

    Check_Type(num, T_FIXNUM);

    ActiveDir()->Mark3(NUM2INT(num));

TEND();    
    
    return Qnil;
}

VALUE mf_mark_off(VALUE self, VALUE num)
{
TBEGIN();

    Check_Type(num, T_FIXNUM);

    ActiveDir()->MarkOff(NUM2INT(num));

TEND();    
    
    return Qnil;
}

VALUE mf_is_marked(VALUE self, VALUE num)
{
TBEGIN();
    Check_Type(num, T_FIXNUM);

    VALUE result = Qfalse;

    sFile* file = ActiveDir()->File(NUM2INT(num));
    if(file) {
        result = file->mMark ? Qtrue:Qfalse;
    }

TEND();    
    
    return result;
}

VALUE mf_marking(VALUE self)
{
TBEGIN();

TEND();    
    
    return ActiveDir()->Marking() ? Qtrue:Qfalse;
}

VALUE mf_is_marked_sdir(VALUE self, VALUE num)
{
TBEGIN();
    Check_Type(num, T_FIXNUM);

    VALUE result = Qfalse;

    sFile* file = SleepDir()->File(NUM2INT(num));
    if(file) {
        result = file->mMark ? Qtrue:Qfalse;
    }

TEND();    
    
    return result;
}

VALUE mf_marking_sdir(VALUE self)
{
TBEGIN();

TEND();    
    
    return SleepDir()->Marking() ? Qtrue:Qfalse;
}

VALUE mf_cmdline(VALUE self, VALUE command, VALUE position)
{
TBEGIN();

    Check_Type(command, T_STRING);
    Check_Type(position, T_FIXNUM);
    
M(("cmdline %s", RSTRING(command)->ptr));

    cmdline_start(RSTRING(command)->ptr, NUM2INT(position));

TEND();    
    
    return Qnil;
}

VALUE mf_shell(VALUE self, VALUE cmd, VALUE title)
{
TBEGIN();

    Check_Type(cmd, T_STRING);
    Check_Type(title, T_STRING);

    cmdline_run(RSTRING(cmd)->ptr, RSTRING(title)->ptr, false);

TEND();    

    return Qnil;
}

VALUE mf_defmenu(int argc, VALUE* argv, VALUE self)
{
TBEGIN();

    /// check arguments ///
    if(argc%3 != 1) {
        rb_raise(rb_eArgError, "wrong argument in mf_menu");
    }

    /// entry menu ///
    char* menu_name = RSTRING(argv[0])->ptr;
    
    if(hash_item(gMenu, menu_name)) {
        rb_raise(rb_eArgError, "already entried menu \"%s\"", menu_name);
    }

    cMenu* new_menu = new cMenu((char*)menu_name);

    for(int i=1; i<argc; i+=3) {
        Check_Type(argv[i], T_STRING);
        Check_Type(argv[i+1], T_FIXNUM);
        Check_Type(argv[i+2], T_STRING);

        new_menu->Append(RSTRING(argv[i])->ptr, NUM2INT(argv[i+1]), RSTRING(argv[i+2])->ptr);
    }
    
    hash_put(gMenu, menu_name, new_menu);

TEND();

    return Qnil;
}

VALUE mf_menu(VALUE self, VALUE menu_name)
{
TBEGIN();

    Check_Type(menu_name, T_STRING);

    gActiveMenu = (cMenu*)hash_item(gMenu, RSTRING(menu_name)->ptr);
    if(gActiveMenu == NULL) {
        gErrMsgCancel = false; 
        err_msg("not found menu name(%s)", RSTRING(menu_name)->ptr);
        gActiveMenu = NULL;
        return Qnil;
    }
    gActiveMenu->Show();

TEND();    

    return Qnil;
}

VALUE mf_dir_copy(VALUE self)
{
TBEGIN();

    ActiveDir()->Move(SleepDir()->Path());
    
TEND();    
    
    return Qnil;
}

VALUE mf_dir_exchange(VALUE self)
{
TBEGIN();

    cDirWnd* wnd = gLDir;
    gLDir = gRDir;
    gRDir = wnd;

TEND();    
    
    return Qnil;
}

VALUE mf_refresh(VALUE self)
{
TBEGIN();

    gLDir->Reread();
    gRDir->Reread(); 
   
    mclear_immediately();
    mclear();
    mrefresh();

TEND();
    
    return Qnil;
}

VALUE mf_reread(VALUE self)
{
TBEGIN();

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

TEND();
    
    return Qnil;
}

VALUE mf_dir_back(VALUE self)
{
TBEGIN();

    ActiveDir()->MoveBack();

TEND();    

    return Qnil;
}

VALUE mf_sdir_back(VALUE self)
{
TBEGIN();

    SleepDir()->MoveBack();

TEND();    

    return Qnil;
}

VALUE mf_cursor_name(VALUE self)
{
    return rb_str_new2(ActiveDir()->CursorFile()->mName);
}

VALUE mf_cursor_path(VALUE self)
{
    char buf[512];
    sprintf(buf, "%s%s", ActiveDir()->Path(), ActiveDir()->CursorFile()->mName);
    
    return rb_str_new2(buf);
}

VALUE mf_cursor_ext(VALUE self)
{
TBEGIN();
    char* tmp = extname(ActiveDir()->CursorFile()->mName);
    VALUE result = rb_str_new2(tmp);
    FREE(tmp);
TEND();    

    return result;
}

VALUE mf_cursor_noext(VALUE self)
{
TBEGIN();

    char* tmp = noextname(ActiveDir()->CursorFile()->mName);
    VALUE result = rb_str_new2(tmp);
    FREE(tmp);

TEND();    

    return result;
}

VALUE mf_active_dir(VALUE self)
{
    char* tmp = STRDUP(ActiveDir()->Path());
    char* tmp2 = basename(tmp);
    FREE(tmp);
    return rb_str_new2(tmp2);
}

VALUE mf_sleep_dir(VALUE self)
{
    char* tmp = STRDUP(SleepDir()->Path());
    char* tmp2 = basename(tmp);
    FREE(tmp);
    return rb_str_new2(tmp2);
}

VALUE mf_left_dir(VALUE self)
{
    char* tmp = STRDUP(gLDir->Path());
    char* tmp2 = basename(tmp);
    FREE(tmp);
    return rb_str_new2(tmp2);
}

VALUE mf_right_dir(VALUE self)
{
    char* tmp = STRDUP(gRDir->Path());
    char* tmp2 = basename(tmp);
    FREE(tmp);
    return rb_str_new2(tmp2);
}

VALUE mf_adir_path(VALUE self)
{
    char buf[PATH_MAX];
    strcpy(buf, ActiveDir()->Path());
    buf[strlen(buf)-1] = 0;

    return rb_str_new2(buf);
}

VALUE mf_sdir_path(VALUE self)
{
    char buf[PATH_MAX];
    strcpy(buf, SleepDir()->Path());
    buf[strlen(buf)-1] = 0;

    return rb_str_new2(buf);
}

VALUE mf_adir_path2(VALUE self)
{
    char buf[PATH_MAX];
    strcpy(buf, ActiveDir()->Path());

    return rb_str_new2(buf);
}

VALUE mf_sdir_path2(VALUE self)
{
    char buf[PATH_MAX];
    strcpy(buf, SleepDir()->Path());

    return rb_str_new2(buf);
}

VALUE mf_ldir_path(VALUE self)
{
    char buf[PATH_MAX];
    strcpy(buf, gLDir->Path());
    buf[strlen(buf)-1] = 0;

    return rb_str_new2(buf);
}

VALUE mf_rdir_path(VALUE self)
{
    char buf[PATH_MAX];
    strcpy(buf, gRDir->Path());
    buf[strlen(buf)-1] = 0;

    return rb_str_new2(buf);
}

VALUE mf_ldir_path2(VALUE self)
{
    char buf[PATH_MAX];
    strcpy(buf, gLDir->Path());

    return rb_str_new2(buf);
}

VALUE mf_rdir_path2(VALUE self)
{
    char buf[PATH_MAX];
    strcpy(buf, gRDir->Path());

    return rb_str_new2(buf);
}

VALUE mf_adir_mark(VALUE self)
{
    return ActiveDir()->MarkFiles();
}

VALUE mf_sdir_mark(VALUE self)
{
    return SleepDir()->MarkFiles();
}

VALUE mf_is_adir_left(VALUE self)
{
    if(ActiveDir()->IsLeftDir()) {
        return Qtrue;
    }
    else {
        return Qfalse;
    }
}

VALUE mf_is_adir_right(VALUE self)
{
    if(ActiveDir()->IsRightDir()) {
        return Qtrue;
    }
    else {
        return Qfalse;
    }
}

VALUE mf_dir_new(VALUE self)
{
    ActiveDir()->PushBackDir();

    return Qnil;
}

VALUE mf_dir_close(VALUE self)
{
    ActiveDir()->PopDir();
    
    return Qnil;
}

VALUE mf_dir_up(VALUE self)
{
    ActiveDir()->SelectDir();

    return Qnil;
}

VALUE mf_tab_close(VALUE self, VALUE num)
{
    Check_Type(num, T_FIXNUM);

    cDirWnd::CloseTab(NUM2INT(num));

    return Qnil;
}

VALUE mf_tab_up(VALUE self, VALUE num)
{
    Check_Type(num, T_FIXNUM);

    ActiveDir()->UpTab(NUM2INT(num));

    return Qnil;
}

VALUE mf_tab_path(VALUE self, VALUE num)
{
    Check_Type(num, T_FIXNUM);

    char* path = cDirWnd::TabPath(NUM2INT(num));

    if(path) 
        return rb_str_new2(path);
    else
        return Qnil;
}

VALUE mf_tab_max(VALUE self)
{
    return INT2NUM(cDirWnd::TabMax());
}

VALUE mf_tab_new(VALUE self, VALUE path)
{
    Check_Type(path, T_STRING);

    cDirWnd::TabNew(RSTRING(path)->ptr);

    return Qnil;
}

VALUE mf_copy(VALUE self, VALUE sdir, VALUE files, VALUE ddir)
{
TBEGIN();
    if(gCheckCopyFile) {
        const char* str[] = {
            "Yes", "No"
        };

        int ret = select_str("copy ok?", (char**)str, 2, 1);

        if(ret == 1) return Qnil;
    }

    int i = 0;

    gCopyOverride = kNone;
    gErrMsgCancel = false;

    Check_Type(sdir, T_STRING);
    Check_Type(files, T_ARRAY);
    Check_Type(ddir, T_STRING);
    
    /// check argument ///
    struct stat dstat;
    if(stat(RSTRING(ddir)->ptr, &dstat) >= 0) {
        if(S_ISDIR(dstat.st_mode) && RSTRING(ddir)->ptr[strlen(RSTRING(ddir)->ptr)-1] != '/') {
            char tmp[PATH_MAX];
            sprintf(tmp, "%s/", RSTRING(ddir)->ptr);

            ddir = rb_str_new2(tmp);
        }
    }
    
    if(RARRAY(files)->len > 1
        && RSTRING(ddir)->ptr[strlen(RSTRING(ddir)->ptr)-1] != '/')
    {
        err_msg("mf_copy: destination_err(%s)", RSTRING(ddir)->ptr);
        return Qnil;
    }
    
    char path2[PATH_MAX];
    if(!correct_path(RSTRING(ddir)->ptr, path2)) {
        err_msg("mf_move: destination_err(%s)", RSTRING(ddir)->ptr);
        return Qnil;
    }
    ddir = rb_str_new2(path2);

    /// go ///
    for(int i=0; i < RARRAY(files)->len; i++) {
        VALUE item = rb_ary_entry(files, i);
        Check_Type(item, T_STRING);

        if(strcmp(RSTRING(item)->ptr, ".") != 0
            && strcmp(RSTRING(item)->ptr, "..") != 0)
        {
            char spath[PATH_MAX];
            strcpy(spath, RSTRING(sdir)->ptr);
            if(RSTRING(sdir)->ptr[RSTRING(sdir)->len-1] != '/') {
                strcat(spath, "/");
            }
            strcat(spath, RSTRING(item)->ptr);
    
            char dpath[PATH_MAX];
            strcpy(dpath, RSTRING(ddir)->ptr);

            file_copy(spath, dpath, false);
        }
    }
    
    gLDir->Reread();
    gRDir->Reread();
    
    ActiveDir()->ResetMarks();

    gUndoOp = kUndoCopy;
    gUndoSdir = sdir;
    gUndoDdir = ddir;
    gUndoFiles = files;

TEND();    

    return Qnil;
}

VALUE mf_move(VALUE self, VALUE sdir, VALUE files, VALUE ddir)
{
TBEGIN();
    if(gCheckCopyFile) {
        const char* str[] = {
            "Yes", "No"
        };

        int ret = select_str("move ok?", (char**)str, 2, 1);

        if(ret == 1) return Qnil;
    }

    int i = 0;

    gCopyOverride = kNone;
    gErrMsgCancel = false;

    Check_Type(sdir, T_STRING);
    Check_Type(files, T_ARRAY);
    Check_Type(ddir, T_STRING);

    /// check argument ///
    struct stat dstat;
    if(stat(RSTRING(ddir)->ptr, &dstat) >= 0) {
        if(S_ISDIR(dstat.st_mode) && RSTRING(ddir)->ptr[strlen(RSTRING(ddir)->ptr)-1] != '/') {
            char tmp[PATH_MAX];
            sprintf(tmp, "%s/", RSTRING(ddir)->ptr);

            ddir = rb_str_new2(tmp);
        }
    }
    
    if(RARRAY(files)->len > 1
        && RSTRING(ddir)->ptr[strlen(RSTRING(ddir)->ptr)-1] != '/')
    {
        err_msg("mf_move: destination_err(%s)", RSTRING(ddir)->ptr);
        return Qnil;
    }
    
    char path2[PATH_MAX];
    if(!correct_path(RSTRING(ddir)->ptr, path2)) {
        err_msg("mf_move: destination_err(%s)", RSTRING(ddir)->ptr);
        return Qnil;
    }
    ddir = rb_str_new2(path2);
    
    /// go ///
    for(int i=0; i < RARRAY(files)->len; i++) {
        VALUE item = rb_ary_entry(files, i);

        Check_Type(item, T_STRING);

        if(strcmp(RSTRING(item)->ptr, ".") != 0
            && strcmp(RSTRING(item)->ptr, "..") != 0)
        {
            char spath[PATH_MAX];
            strcpy(spath, RSTRING(sdir)->ptr);
            if(RSTRING(sdir)->ptr[RSTRING(sdir)->len-1] != '/') {
                strcat(spath, "/");
            }
            strcat(spath, RSTRING(item)->ptr);

            char dpath[PATH_MAX];
            strcpy(dpath, RSTRING(ddir)->ptr);

            file_copy(spath, dpath, true);
        }
    }

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

    gUndoOp = kUndoMove;
    gUndoSdir = sdir;
    gUndoDdir = ddir;
    gUndoFiles = files;

TEND();    

    return Qnil;
}

VALUE mf_remove(VALUE self, VALUE sdir, VALUE files)
{
TBEGIN();

    gCopyOverride = kNone;
    gErrMsgCancel = false;

    if(gCheckDeleteFile) {
        const char* str[] = {
            "Yes", "No"
        };

        int ret = select_str("delete ok?", (char**)str, 2, 1);

        if(ret == 1) return Qnil;
    }

    Check_Type(sdir, T_STRING);
    Check_Type(files, T_ARRAY);
    
    for(int i= 0; i < RARRAY(files)->len; i++) {
        VALUE item = rb_ary_entry(files, i);
        Check_Type(item, T_STRING);

        if(strcmp(RSTRING(item)->ptr, ".") != 0
            && strcmp(RSTRING(item)->ptr, "..") != 0)
        {
            char spath[PATH_MAX];
            strcpy(spath, RSTRING(sdir)->ptr);
            if(RSTRING(sdir)->ptr[RSTRING(sdir)->len-1] != '/') {
                strcat(spath, "/");
            }
            strcat(spath, RSTRING(item)->ptr);

            file_remove(spath, false);
        }
    }

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

TEND();

    return Qnil;
}

VALUE mf_trashbox(VALUE self, VALUE sdir, VALUE files)
{
TBEGIN();

    gErrMsgCancel = false;
    
    char* home = getenv("HOME");
    if(home == NULL) {
        err_msg("trashbox: $HOME is NULL");
        return Qnil;
    }

    char tbpath[PATH_MAX];
    strcpy(tbpath, gTrashBoxName);

M(("tbpath %s", tbpath));
    
    if(gCheckDeleteFile) {
        const char* str[] = {
            "Yes", "No"
        };

        int ret;
        if(strcmp(RSTRING(sdir)->ptr, tbpath) == 0) {
            ret = select_str("delete ok?", (char**)str, 2, 1);
        }
        else {
            ret = select_str("move to the trashbox ok?", (char**)str, 2, 1);
        }

        if(ret == 1) return Qnil;
    }

    Check_Type(sdir, T_STRING);
    Check_Type(files, T_ARRAY);

    /// make mtrashbox ///
    if(access(tbpath, F_OK) == 0) {
        struct stat tbstat;
        if(stat(tbpath, &tbstat) < 0) {
            err_msg("trashbox: stat err(%s)", tbpath);
            return Qnil;
        }
        if(!S_ISDIR(tbstat.st_mode)) {
            char buf[256];
            sprintf(buf, "trashbox: %s is not directory", gTrashBoxName);
            err_msg(buf);
            return Qnil;
        }
    }
    else {
        if(mkdir(tbpath, 0755) < 0) {
            char buf[256];
            sprintf(buf, "trashbox: can't make %s", gTrashBoxName);
            err_msg(buf);
            return Qnil;
        }
    }

    gCopyOverride = kYesAll;

    strcat(tbpath, "/");

    for(int i=0; i < RARRAY(files)->len; i++) {
        VALUE item = rb_ary_entry(files, i);

        Check_Type(item, T_STRING);

        if(strcmp(RSTRING(item)->ptr, ".") != 0
            && strcmp(RSTRING(item)->ptr, "..") != 0)
        {
            char spath[PATH_MAX];
            strcpy(spath, RSTRING(sdir)->ptr);
            if(RSTRING(sdir)->ptr[RSTRING(sdir)->len-1] != '/') {
                strcat(spath, "/");
            }
            strcat(spath, RSTRING(item)->ptr);

            char dpath[PATH_MAX];
            strcpy(dpath, tbpath);
            strcat(dpath, RSTRING(item)->ptr);

M(("dpath %s", dpath));
            
            if(access(dpath, F_OK) == 0) {
                file_remove(dpath, false);
            }
            
            file_copy(spath, tbpath, true);
        }
    }

    gUndoOp = kUndoTrashBox;
    gUndoSdir = sdir;
    gUndoDdir = Qnil;
    gUndoFiles = files;

    gLDir->Reread();
    gRDir->Reread();
    
    ActiveDir()->ResetMarks();
    
TEND();

    return Qnil;
}

VALUE mf_select_pty(VALUE self, VALUE title)
{
    Check_Type(title, T_STRING);

    return INT2FIX(cmdline_select_pty(RSTRING(title)->ptr) + 1);
}

VALUE mf_kill_pty(VALUE self, VALUE n)
{
TBEGIN();

    Check_Type(n, T_FIXNUM);

    const int m = NUM2INT(n);
    if(m >= 1 && m <= vector_size(gTty)) {
        sMasterTty* tty = (sMasterTty*)vector_item(gTty, m-1);
        kill(tty->mPID, SIGKILL);

        FREE(tty);
        vector_erase(gTty, m-1);
    }

TEND();

    return Qnil;
}

VALUE mf_restore_pty(VALUE self, VALUE n)
{
TBEGIN();

    Check_Type(n, T_FIXNUM);

    const int m = NUM2INT(n);
    if(m >= 1 && m <= vector_size(gTty)) {
        cmdline_restore_pty(m-1);
    }

TEND();

    return Qnil;
}

VALUE mf_set_mask(VALUE self, VALUE mask)
{
    Check_Type(mask, T_STRING);

    ActiveDir()->ChangeMask(RSTRING(mask)->ptr);

    return Qnil;
}

VALUE mf_set_mask_sdir(VALUE self, VALUE mask)
{
    Check_Type(mask, T_STRING);

    SleepDir()->ChangeMask(RSTRING(mask)->ptr);

    return Qnil;
}

const char kCut = 0;
const char kCopy = 1;

VALUE mf_cut(VALUE self)
{
    char buf[8192];
    char* p = buf;

    gErrMsgCancel = false;
    
    const int pid = getpid();
    memcpy(p, &pid, sizeof(pid));
    p += sizeof(pid);
    
    memcpy(p, &kCut, sizeof(kCut));
    p += sizeof(kCut);

    VALUE path2 = mf_adir_path(self);
    sprintf(p, "%s%c", RSTRING(path2)->ptr, 0);
    p += RSTRING(path2)->len+1;

    VALUE files = ActiveDir()->MarkFiles();

    const int n = RARRAY(files)->len;
    memcpy(p, &n, sizeof(n));
    p += sizeof(n);

    for(int i=0; i < RARRAY(files)->len; i++) {
        VALUE item = rb_ary_entry(files, i);
        Check_Type(item, T_STRING);

        sprintf(p, "%s%c", RSTRING(item)->ptr, 0);
        p += RSTRING(item)->len + 1;
    }

    /// write ///    
    char* home = getenv("HOME");
    if(home == NULL) {
        err_msg("cut: $HOME is null");
        return Qnil;
    }

    char path[1024];
    sprintf(path, "%s/.mfiler-cutpast", home);

    int file = open(path, O_WRONLY|O_CREAT|O_TRUNC, 0644);
    if(file < 0) {
        err_msg("cut: open err(%s)", path);
        return Qnil;
    }
    if(write(file, buf, p - buf) < 0) {
        err_msg("cut: write err");
        close(file);
        return Qnil;
    }
    close(file);
    
    ActiveDir()->ResetMarks();

    return Qnil;
}

VALUE mf_copy2(VALUE self)
{
    char buf[8192];
    char* p = buf;

    gErrMsgCancel = false;

    const int pid = getpid();
    memcpy(p, &pid, sizeof(pid));
    p += sizeof(pid);
    
    memcpy(p, &kCopy, sizeof(kCopy));
    p += sizeof(kCopy);

    VALUE path2 = mf_adir_path(self);
    sprintf(p, "%s%c", RSTRING(path2)->ptr, 0);
    p += RSTRING(path2)->len+1;

    VALUE files = ActiveDir()->MarkFiles();

    const int n = RARRAY(files)->len;
    memcpy(p, &n, sizeof(n));
    p += sizeof(n);

    for(int i=0; i < RARRAY(files)->len; i++) {
        VALUE item = rb_ary_entry(files, i);
        Check_Type(item, T_STRING);

        sprintf(p, "%s%c", RSTRING(item)->ptr, 0);
        p += RSTRING(item)->len + 1;
    }

    /// write ///    
    char* home = getenv("HOME");
    if(home == NULL) {
        err_msg("copy2: $HOME is null");
        return Qnil;
    }

    char path[1024];
    sprintf(path, "%s/.mfiler-cutpast", home);

    int file = open(path, O_WRONLY|O_CREAT|O_TRUNC, 0644);
    if(file < 0) {
        err_msg("copy2: open err(%s)", path);
        return Qnil;
    }
    if(write(file, buf, p - buf) < 0) {
        err_msg("copy2: write err");
        close(file);
        return Qnil;
    }
    close(file);
    
    ActiveDir()->ResetMarks();

    return Qnil;
}

VALUE mf_past(VALUE self)
{
    char buf[8192];

    gErrMsgCancel = false;
        
    /// read ///
    char* home = getenv("HOME");
    if(home == NULL) {
        err_msg("past: $HOME is null");
        return Qnil;
    }

    char path[1024];
    sprintf(path, "%s/.mfiler-cutpast", home);

    if(access(path, F_OK) != 0) {
        err_msg("past: there are no cutted or copied files");
        return Qnil;
    }
    
    struct stat stat_;
    if(stat(path, &stat_) < 0) {
        err_msg("past: stat err");
        return Qnil;
    }
        
    int file = open(path, O_RDONLY);
    if(file < 0) {
        err_msg("past: open err");
        return Qnil;
    }
    
    if(read(file, buf, stat_.st_size) < 0) {
        err_msg("past: read err");
        close(file);
        return Qnil;
    }
    close(file);
    
    /// reconstruct from file buffer ///
    char* p = buf;

    int pid = *(int*)p;
    p += sizeof(int);

    char cut_mode = *(char*)p;
    p++;

M(("CutMode %d", cut_mode));    

    char buf2[PATH_MAX];
    char* p2 = buf2;
    while(*p) {
        *p2++ = *p++;
    }
    *p2 = 0;
    p++;
    
    VALUE cut_dir = rb_str_new2(buf2);

M(("CutDir %s", buf2));    

    VALUE cut_files = rb_ary_new();

    int len = *(int*)p;
    p += sizeof(int);

M(("Len %d", len));    

    for(int i=0; i < len; i++) {
        char buf3[PATH_MAX];
        char* p3 = buf3;
        while(*p) {
            *p3++ = *p++;
        }
        *p3 = 0;
        p++;

M(("File %s", buf3));            
        rb_ary_push(cut_files, rb_str_new2(buf3));
    }

    
    switch(cut_mode) {
    case kCopy:
        mf_copy(self, cut_dir, cut_files, mf_adir_path(self));
        break;

    case kCut: {
        mf_move(self, cut_dir, cut_files, mf_adir_path(self));
        }
        break;
    }

    if(unlink(path) < 0) {
        err_msg("past: unlink err(%s)", path);
        return Qnil;
    }

    /// refresh ///
    char buf3[256];

    sprintf(buf3, "mfiler -r %d", pid);
    system(buf3);
    
    ActiveDir()->ResetMarks();

    return Qnil;
}

VALUE mf_malias(VALUE self, VALUE alias, VALUE arg_num, VALUE command)
{
TBEGIN();
    Check_Type(alias, T_STRING);
    Check_Type(arg_num, T_FIXNUM);
    Check_Type(command, T_STRING);

    cmdline_alias_add(RSTRING(alias)->ptr, NUM2INT(arg_num), RSTRING(command)->ptr);
    
TEND();
    
    return Qnil;
}

VALUE mf_rehash(VALUE self)
{
TBEGIN();

    cmdline_rehash();
    
TEND();
    
    return Qnil;
}

VALUE mf_help(VALUE self)
{
TBEGIN();

    help_start();
    
TEND();
    
    return Qnil;
}

VALUE mf_set_xterm(VALUE self, VALUE xterm, VALUE opt_title, VALUE opt_eval, VALUE opt_extra)
{
TBEGIN();
    Check_Type(xterm, T_STRING);
    Check_Type(opt_title, T_STRING);
    Check_Type(opt_eval, T_STRING);
   Check_Type(opt_extra, T_STRING);

    gXtermPrgName = xterm;
    gXtermOptTitle = opt_title;
    gXtermOptEval = opt_eval;
    gXtermOptExtra = opt_extra;

TEND();
    
    return Qnil;
}

VALUE mf_view_option(VALUE self)
{
    switch(cDirWnd::gViewOption) {
    case cDirWnd::kAll:
        return rb_str_new2("all");

    case cDirWnd::kOneDir:
        return rb_str_new2("one_dir");

    case cDirWnd::kOneDir2:
        return rb_str_new2("one_dir2");

    case cDirWnd::kOneDir3:
        return rb_str_new2("one_dir3");

    case cDirWnd::kOneDir5:
        return rb_str_new2("one_dir5");
    }
}

VALUE mf_cursor(VALUE self)
{
    return INT2FIX(ActiveDir()->Cursor());
}

VALUE mf_file_name(VALUE self, int num)
{
    Check_Type(num, T_FIXNUM);

    sFile* file = ActiveDir()->File(NUM2INT(num));
    if(file) {
        return rb_str_new2(file->mName);
    }
    else {
        return Qnil;
    }
}

VALUE mf_cursor_max(VALUE self)
{
    return INT2FIX(ActiveDir()->CursorMax());
}

VALUE mf_scrolltop(VALUE self)
{
    return INT2FIX(ActiveDir()->ScrollTop());
}

VALUE mf_set_scrolltop(VALUE self, VALUE value)
{
    Check_Type(value, T_FIXNUM);

    ActiveDir()->SetScrollTop(NUM2INT(value));
    
    return Qnil;
}

VALUE mf_cursor_x(VALUE self)
{
    return INT2FIX(ActiveDir()->CursorX());
}

VALUE mf_cursor_y(VALUE self)
{
    return INT2FIX(ActiveDir()->CursorY());
}

VALUE mf_cursor_maxx(VALUE self)
{
    return INT2FIX(ActiveDir()->CursorMaxX());
}

VALUE mf_cursor_maxy(VALUE self)
{
    return INT2FIX(ActiveDir()->CursorMaxY());
}

VALUE mf_xterm_next(VALUE self)
{
    gXtermNext = !gXtermNext;

    return Qnil;
}

VALUE mf_keymap(VALUE self, VALUE key, VALUE p1, VALUE p2, VALUE p3, VALUE p4, VALUE p5, VALUE p6, VALUE p7, VALUE p8, VALUE p9, VALUE p10)
{
    char keys[kKeyMapKeysMax];

    Check_Type(key, T_FIXNUM);
    Check_Type(p1, T_FIXNUM);
    Check_Type(p2, T_FIXNUM);
    Check_Type(p3, T_FIXNUM);
    Check_Type(p4, T_FIXNUM);
    Check_Type(p5, T_FIXNUM);
    Check_Type(p6, T_FIXNUM);
    Check_Type(p7, T_FIXNUM);
    Check_Type(p8, T_FIXNUM);
    Check_Type(p9, T_FIXNUM);
    Check_Type(p10, T_FIXNUM);
   

    keys[0] = NUM2INT(p1);
    keys[1] = NUM2INT(p2);
    keys[2] = NUM2INT(p3);
    keys[3] = NUM2INT(p4);
    keys[4] = NUM2INT(p5);
    keys[5] = NUM2INT(p6);
    keys[6] = NUM2INT(p7);
    keys[7] = NUM2INT(p8);
    keys[8] = NUM2INT(p9);
    keys[9] = NUM2INT(p10);
    madd_keymap(NUM2INT(key), keys);
    
    return Qnil;
}

VALUE mf_completion(VALUE self, VALUE all_candidate, VALUE editing, VALUE sqort, VALUE dqort, VALUE last_sqort, VALUE last_dqort, VALUE add_space)
{
TBEGIN();
    Check_Type(all_candidate, T_ARRAY);
    Check_Type(editing, T_STRING);

    bool add_space2 = add_space == Qtrue ? true: false;
    bool sqort2 = sqort == Qtrue ? true:false;
    bool dqort2 = dqort == Qtrue ? true:false;
    bool last_sqort2 = last_sqort == Qtrue ? true:false;
    bool last_dqort2 = last_dqort == Qtrue ? true:false;
        
    cmdline_completion(all_candidate, RSTRING(editing)->ptr, sqort2, dqort2, last_sqort2, last_dqort2, add_space2);

TEND();    

    return Qnil;
}

VALUE mf_completion2(VALUE self, VALUE all_candidate, VALUE editing, VALUE sqort, VALUE dqort, VALUE last_sqort, VALUE last_dqort, VALUE add_space, VALUE candidate_len)
{
TBEGIN();
    Check_Type(all_candidate, T_ARRAY);
    Check_Type(editing, T_STRING);
    Check_Type(candidate_len, T_FIXNUM);

    bool add_space2 = add_space == Qtrue ? true: false;
    bool sqort2 = sqort == Qtrue ? true:false;
    bool dqort2 = dqort == Qtrue ? true:false;
    bool last_sqort2 = last_sqort == Qtrue ? true:false;
    bool last_dqort2 = last_dqort == Qtrue ? true:false;

    cmdline_completion2(all_candidate, RSTRING(editing)->ptr, sqort2, dqort2, last_sqort2, last_dqort2, add_space2, NUM2INT(candidate_len));

TEND();    

    return Qnil;
}

VALUE mf_minitscr(VALUE self)
{
TBEGIN();

    minitscr();

TEND();

    return Qnil;
}

VALUE mf_mendwin(VALUE self)
{
TBEGIN();

    mendwin();

TEND();

    return Qnil;
}

VALUE mf_mmove(VALUE self, VALUE y, VALUE x)
{
TBEGIN();

    Check_Type(y, T_FIXNUM);
    Check_Type(x, T_FIXNUM);

    mmove(NUM2INT(y), NUM2INT(x));

TEND();

    return Qnil;
}

VALUE mf_mmove_immediately(VALUE self, VALUE y, VALUE x)
{
TBEGIN();

    Check_Type(y, T_FIXNUM);
    Check_Type(x, T_FIXNUM);

    mmove_immediately(NUM2INT(y), NUM2INT(x));

TEND();

    return Qnil;
}

VALUE mf_mprintw(int argc, VALUE* argv, VALUE self)
{
TBEGIN();

    if(argc < 1) {
        fprintf(stderr, "invalid arguments in mprintw(%d)", argc);
        exit(1);
    }

    /// get str ///
    VALUE str = rb_f_sprintf(argc, argv);
    int ret = mprintw("%s", RSTRING(str)->ptr);

TEND();

    return INT2NUM(ret);
}

//%r minitscr(); mclear(); mmvprintw(0,0,"aaa"); mrefresh(); mgetch(); mendwin();

VALUE mf_mmvprintw(int argc, VALUE* argv, VALUE self)
{
TBEGIN();

    if(argc < 3) {
        fprintf(stderr, "invalid arguments in mmvprintw(%d)", argc);
        exit(1);
    }
        
    /// get x, y ///
    Check_Type(argv[0], T_FIXNUM);
    Check_Type(argv[1], T_FIXNUM);

    int x = NUM2INT(argv[0]);
    int y = NUM2INT(argv[1]);

    /// get str ///
    int new_argc = argc-2;
    VALUE* new_argv = argv + 2;
    
    VALUE str = rb_f_sprintf(new_argc, new_argv);
    int ret = mmvprintw(x, y, "%s", RSTRING(str)->ptr);

TEND();

    return INT2NUM(ret);
}

VALUE mf_mattron(VALUE self, VALUE attrs)
{
TBEGIN();

    Check_Type(attrs, T_FIXNUM);

    mattron(NUM2INT(attrs));

TEND();
    return Qnil;
}

VALUE mf_mattroff(VALUE self)
{
TBEGIN();

    mattroff();

TEND();
    return Qnil;
}

VALUE mf_mgetmaxx(VALUE self)
{
TBEGIN();

    int ret = mgetmaxx();
    
TEND();
    return INT2NUM(ret);
}

VALUE mf_mgetmaxy(VALUE self)
{
TBEGIN();

    int ret = mgetmaxy();
    
TEND();
    return INT2NUM(ret);
}

VALUE mf_mclear_immediately(VALUE self)
{
TBEGIN();

    mclear_immediately();
    
TEND();
    return Qnil;
}

VALUE mf_mclear(VALUE self)
{
TBEGIN();

    mclear();
    
TEND();
    return Qnil;
}

VALUE mf_mclear_online(VALUE self, VALUE y)
{
TBEGIN();

    Check_Type(y, T_FIXNUM);

    mclear_online(NUM2INT(y));
    
TEND();
    return Qnil;
}

VALUE mf_mbox(VALUE self, VALUE y, VALUE x, VALUE width, VALUE height)
{
TBEGIN();

    Check_Type(y, T_FIXNUM);
    Check_Type(x, T_FIXNUM);
    Check_Type(width, T_FIXNUM);
    Check_Type(height, T_FIXNUM);

    mbox(NUM2INT(y), NUM2INT(x), NUM2INT(width), NUM2INT(height));
    
TEND();
    return Qnil;
}

VALUE mf_mrefresh(VALUE self)
{
TBEGIN();

    mrefresh();
    
TEND();
    return Qnil;
}

VALUE mf_mkbuf_exist(VALUE self)
{
TBEGIN();

    int result = mkbuf_exist();
    
TEND();
    return INT2NUM(result);
}

VALUE mf_mgetch(VALUE self)
{
TBEGIN();

    int meta;
    int key = mgetch(&meta);
    
    VALUE result = rb_ary_new();
    rb_ary_push(result, INT2NUM(meta));
    rb_ary_push(result, INT2NUM(key));
    
TEND();

    return result;
}

VALUE mf_mis_curses(VALUE self)
{
TBEGIN();

    int result = mis_curses();
    
TEND();

    return result ? Qtrue:Qfalse;
}

VALUE mf_hostname(VALUE self)
{
TBEGIN();
TEND();

    return rb_str_new2(gHostName);
}

VALUE mf_username(VALUE self)
{
TBEGIN();
TEND();

    return rb_str_new2(gUserName);
}

VALUE mf_set_shell(VALUE self, VALUE name, VALUE path, VALUE opt_eval)
{
TBEGIN();
    Check_Type(name, T_STRING);
    Check_Type(path, T_STRING);
    Check_Type(opt_eval, T_STRING);

    strcpy(gShellName, RSTRING(name)->ptr);
    strcpy(gShellPath, RSTRING(path)->ptr);
    strcpy(gShellOptEval, RSTRING(opt_eval)->ptr);
    
TEND();

    return Qnil;
}

VALUE mf_message(int argc, VALUE* argv, VALUE self)
{
TBEGIN();
    if(argc < 1) {
        err_msg("invalid arguments in message(%d)", argc);
    }

    int ret = 0;
    if(mis_curses()) {
        gErrMsgCancel = false; 
        VALUE str = rb_f_sprintf(argc, argv);
        ret = err_msg("%s", RSTRING(str)->ptr);
    }
    else {
        printf("\nmessage() needs curses. please add %%Q\n");
    }

TEND();

    return INT2NUM(ret);
}

VALUE mf_select_string(VALUE self, VALUE message, VALUE strings, VALUE cancel)
{
TBEGIN();
    Check_Type(message, T_STRING);
    Check_Type(strings, T_ARRAY);
    Check_Type(cancel, T_FIXNUM);

    int ret = -1;
    if(mis_curses()) {
        char** str = (char**)MALLOC(sizeof(char*)*RARRAY(strings)->len);
    
        for(int i=0; i < RARRAY(strings)->len; i++) {
            VALUE item = rb_ary_entry(strings, i);
            Check_Type(item, T_STRING);

            str[i] = RSTRING(item)->ptr;
        }
    
        ret = select_str(RSTRING(message)->ptr, str, RARRAY(strings)->len, NUM2INT(cancel));

        FREE(str);
   }
    else {
        printf("\nselect_string() needs curses. please add %%Q\n");
    }

TEND();        

    return INT2NUM(ret);
}


VALUE mf_history_size(VALUE self, VALUE size)
{
TBEGIN();

    Check_Type(size, T_FIXNUM);

    gHistorySize = NUM2INT(size);

TEND();

    return Qnil;
}

VALUE mf_sort_kind(VALUE self)
{
    return rb_str_new2(cDirWnd::kSortName[cDirWnd::gSortKind]);
}

VALUE mf_undo(VALUE self)
{
    switch(gUndoOp) {
        case kUndoNil:
            break;

        case kUndoCopy: {
            bool tmp = gCheckDeleteFile;
            gCheckDeleteFile = false;
            mf_remove(self, gUndoDdir, gUndoFiles);
            gCheckDeleteFile = tmp;

            gUndoOp = kUndoNil;
            gUndoSdir = Qnil;
            gUndoDdir = Qnil;
            gUndoFiles = Qnil;
            }
            break;

        case kUndoMove: {
            bool tmp = gCheckCopyFile;
            gCheckCopyFile = false;
            mf_move(self, gUndoDdir, gUndoFiles, gUndoSdir);
            gCheckCopyFile = tmp;

            gUndoOp = kUndoNil;
            gUndoSdir = Qnil;
            gUndoDdir = Qnil;
            gUndoFiles = Qnil;
            }
            break;

        case kUndoTrashBox: {
            bool tmp = gCheckCopyFile;
            gCheckCopyFile = false;
            mf_move(self, rb_str_new2(gTrashBoxName), gUndoFiles, gUndoSdir);
            gCheckCopyFile = tmp;

            gUndoOp = kUndoNil;
            gUndoSdir = Qnil;
            gUndoDdir = Qnil;
            gUndoFiles = Qnil;
            }
            break;
    }
}

void command_init()
{
TBEGIN();

    gUndoOp = kUndoNil;
    gUndoSdir = Qnil;
    gUndoDdir = Qnil;
    gUndoFiles = Qnil;

    gethostname(gHostName, 256);

    struct passwd* uname = getpwuid(getuid());
    strcpy(gUserName, uname->pw_name);

    rb_global_variable(&gUndoSdir);
    rb_global_variable(&gUndoDdir);
    rb_global_variable(&gUndoFiles);

    rb_define_global_function("mf_exit", RUBY_METHOD_FUNC(mf_exit), 0);
    rb_define_global_function("keycommand", RUBY_METHOD_FUNC(mf_keycommand), 4);
    rb_define_global_function("cursor_move", RUBY_METHOD_FUNC(mf_cursor_move), 1);
    rb_define_global_function("cursor_left", RUBY_METHOD_FUNC(mf_cursor_left), 0);
    rb_define_global_function("cursor_right", RUBY_METHOD_FUNC(mf_cursor_right), 0);
    rb_define_global_function("cursor_other", RUBY_METHOD_FUNC(mf_cursor_other), 0);
    rb_define_global_function("dir_move", RUBY_METHOD_FUNC(mf_dir_move), 1);
    rb_define_global_function("sdir_move", RUBY_METHOD_FUNC(mf_sdir_move), 1);
    
    rb_define_global_function("sort_name", RUBY_METHOD_FUNC(mf_sort_name), 0);
    rb_define_global_function("sort_name_reverse", RUBY_METHOD_FUNC(mf_sort_name_reverse), 0);
    rb_define_global_function("sort_ext", RUBY_METHOD_FUNC(mf_sort_ext), 0);
    rb_define_global_function("sort_ext_reverse", RUBY_METHOD_FUNC(mf_sort_ext_reverse), 0);
    rb_define_global_function("sort_size_reverse", RUBY_METHOD_FUNC(mf_sort_size_reverse), 0);
    rb_define_global_function("sort_size", RUBY_METHOD_FUNC(mf_sort_size), 0);
    rb_define_global_function("sort_time", RUBY_METHOD_FUNC(mf_sort_time), 0);
    rb_define_global_function("sort_time_reverse", RUBY_METHOD_FUNC(mf_sort_time_reverse), 0);
    rb_define_global_function("sort_user", RUBY_METHOD_FUNC(mf_sort_user), 0);
    rb_define_global_function("sort_user_reverse", RUBY_METHOD_FUNC(mf_sort_user_reverse), 0);
    rb_define_global_function("sort_group", RUBY_METHOD_FUNC(mf_sort_group), 0);
    rb_define_global_function("sort_group_reverse", RUBY_METHOD_FUNC(mf_sort_group_reverse), 0);
    rb_define_global_function("sort_toggle_dir_up", RUBY_METHOD_FUNC(mf_sort_toggle_dir_up), 0);
    
    rb_define_global_function("view_all", RUBY_METHOD_FUNC(mf_view_all), 0);
    rb_define_global_function("view_nameonly", RUBY_METHOD_FUNC(mf_view_nameonly), 0);
    rb_define_global_function("view_onedir", RUBY_METHOD_FUNC(mf_view_onedir), 0);
    rb_define_global_function("view_onedir2", RUBY_METHOD_FUNC(mf_view_onedir2), 0);
    rb_define_global_function("view_onedir3", RUBY_METHOD_FUNC(mf_view_onedir3), 0);
    rb_define_global_function("view_onedir5", RUBY_METHOD_FUNC(mf_view_onedir5), 0);

    rb_define_global_function("isearch", RUBY_METHOD_FUNC(mf_isearch), 0);

    rb_define_global_function("option_individual_cursor", RUBY_METHOD_FUNC(mf_option_individual_cursor), 1);
    rb_define_global_function("option_isearch_decision_way", RUBY_METHOD_FUNC(mf_option_isearch_decision_way), 1);
 
    rb_define_global_function("option_remain_marks", RUBY_METHOD_FUNC(mf_option_remain_marks), 1);
    rb_define_global_function("is_remain_marks", RUBY_METHOD_FUNC(mf_is_remain_marks), 0);
   rb_define_global_function("option_color", RUBY_METHOD_FUNC(mf_option_color), 1);
    rb_define_global_function("option_check_delete_file", RUBY_METHOD_FUNC(mf_option_check_delete_file), 1);
    rb_define_global_function("option_check_copy_file", RUBY_METHOD_FUNC(mf_option_check_copy_file), 1);
    rb_define_global_function("option_check_exit", RUBY_METHOD_FUNC(mf_option_check_exit), 1);
    rb_define_global_function("option_shift_isearch", RUBY_METHOD_FUNC(mf_option_shift_isearch), 1);
    rb_define_global_function("option_bold_exe", RUBY_METHOD_FUNC(mf_option_bold_exe), 1);
    rb_define_global_function("option_bold_dir", RUBY_METHOD_FUNC(mf_option_bold_dir), 1);
    rb_define_global_function("option_color_mark", RUBY_METHOD_FUNC(mf_option_color_mark), 1);
    rb_define_global_function("option_color_exe", RUBY_METHOD_FUNC(mf_option_color_exe), 1);
    rb_define_global_function("option_color_dir", RUBY_METHOD_FUNC(mf_option_color_dir), 1);
    rb_define_global_function("option_color_link", RUBY_METHOD_FUNC(mf_option_color_link), 1);
    
    rb_define_global_function("option_trashbox_name", RUBY_METHOD_FUNC(mf_option_trashbox_name), 1);
    rb_define_global_function("option_gnu_screen", RUBY_METHOD_FUNC(mf_option_gnu_screen), 1);
    rb_define_global_function("option_xterm", RUBY_METHOD_FUNC(mf_option_xterm), 1);
    rb_define_global_function("option_pty", RUBY_METHOD_FUNC(mf_option_pty), 1);
    rb_define_global_function("option_kanjicode", RUBY_METHOD_FUNC(mf_option_kanjicode), 1);
    rb_define_global_function("option_remain_cursor", RUBY_METHOD_FUNC(mf_option_remain_cursor), 1);
    rb_define_global_function("option_auto_rehash", RUBY_METHOD_FUNC(mf_option_auto_rehash), 1);
    rb_define_global_function("option_read_dir_history", RUBY_METHOD_FUNC(mf_option_read_dir_history), 1);
    
    rb_define_global_function("option_select", RUBY_METHOD_FUNC(mf_option_select), 1);
    rb_define_global_function("mark_all", RUBY_METHOD_FUNC(mf_mark_all), 0);
    rb_define_global_function("mark_all_files", RUBY_METHOD_FUNC(mf_mark_all_files), 0);
    rb_define_global_function("mark", RUBY_METHOD_FUNC(mf_mark), 0);
    rb_define_global_function("mark2", RUBY_METHOD_FUNC(mf_mark2), 1);
    rb_define_global_function("mark_on", RUBY_METHOD_FUNC(mf_mark_on), 1);
    rb_define_global_function("mark_off", RUBY_METHOD_FUNC(mf_mark_off), 1);
    rb_define_global_function("cmdline", RUBY_METHOD_FUNC(mf_cmdline), 2);
    rb_define_global_function("shell", RUBY_METHOD_FUNC(mf_shell), 2);
    rb_define_global_function("menu", RUBY_METHOD_FUNC(mf_menu), 1);
    rb_define_global_function("defmenu", RUBY_METHOD_FUNC(mf_defmenu), -1);
    
    rb_define_global_function("dir_exchange", RUBY_METHOD_FUNC(mf_dir_exchange), 0);
    rb_define_global_function("dir_copy", RUBY_METHOD_FUNC(mf_dir_copy), 0);
    rb_define_global_function("refresh", RUBY_METHOD_FUNC(mf_refresh), 0);
    rb_define_global_function("reread", RUBY_METHOD_FUNC(mf_reread), 0);
    rb_define_global_function("dir_back", RUBY_METHOD_FUNC(mf_dir_back), 0);
    rb_define_global_function("sdir_back", RUBY_METHOD_FUNC(mf_sdir_back), 0);

    rb_define_global_function("dir_new", RUBY_METHOD_FUNC(mf_dir_new), 0);
    rb_define_global_function("dir_close", RUBY_METHOD_FUNC(mf_dir_close), 0);
    rb_define_global_function("dir_up", RUBY_METHOD_FUNC(mf_dir_up), 0);

    rb_define_global_function("cursor_name", RUBY_METHOD_FUNC(mf_cursor_name), 0);
    rb_define_global_function("cursor_path", RUBY_METHOD_FUNC(mf_cursor_path), 0);
    rb_define_global_function("cursor_ext", RUBY_METHOD_FUNC(mf_cursor_ext), 0);
    rb_define_global_function("cursor_noext", RUBY_METHOD_FUNC(mf_cursor_noext), 0);
    rb_define_global_function("active_dir", RUBY_METHOD_FUNC(mf_active_dir), 0);
    rb_define_global_function("sleep_dir", RUBY_METHOD_FUNC(mf_sleep_dir), 0);
    rb_define_global_function("adir_path", RUBY_METHOD_FUNC(mf_adir_path), 0);
    rb_define_global_function("sdir_path", RUBY_METHOD_FUNC(mf_sdir_path), 0);
    rb_define_global_function("adir_path2", RUBY_METHOD_FUNC(mf_adir_path2), 0);
    rb_define_global_function("sdir_path2", RUBY_METHOD_FUNC(mf_sdir_path2), 0);
    rb_define_global_function("left_dir", RUBY_METHOD_FUNC(mf_left_dir), 0);
    rb_define_global_function("right_dir", RUBY_METHOD_FUNC(mf_right_dir), 0);
    rb_define_global_function("ldir_path", RUBY_METHOD_FUNC(mf_ldir_path), 0);
    rb_define_global_function("rdir_path", RUBY_METHOD_FUNC(mf_rdir_path), 0);
    rb_define_global_function("ldir_path2", RUBY_METHOD_FUNC(mf_ldir_path2), 0);
    rb_define_global_function("rdir_path2", RUBY_METHOD_FUNC(mf_rdir_path2), 0);
    
    rb_define_global_function("adir_mark", RUBY_METHOD_FUNC(mf_adir_mark), 0);
    rb_define_global_function("sdir_mark", RUBY_METHOD_FUNC(mf_sdir_mark), 0);
    
    rb_define_global_function("copy", RUBY_METHOD_FUNC(mf_copy), 3);
    rb_define_global_function("remove", RUBY_METHOD_FUNC(mf_remove), 2);
    rb_define_global_function("move", RUBY_METHOD_FUNC(mf_move), 3);
    rb_define_global_function("trashbox", RUBY_METHOD_FUNC(mf_trashbox), 2);
    
    rb_define_global_function("select_pty", RUBY_METHOD_FUNC(mf_select_pty), 1);
    rb_define_global_function("kill_pty", RUBY_METHOD_FUNC(mf_kill_pty), 1);
    rb_define_global_function("restore_pty", RUBY_METHOD_FUNC(mf_restore_pty), 1);
    rb_define_global_function("set_mask", RUBY_METHOD_FUNC(mf_set_mask), 1);
    rb_define_global_function("set_mask_sdir", RUBY_METHOD_FUNC(mf_set_mask_sdir), 1);
    rb_define_global_function("cut", RUBY_METHOD_FUNC(mf_cut), 0);
    rb_define_global_function("copy2", RUBY_METHOD_FUNC(mf_copy2), 0);
    rb_define_global_function("past", RUBY_METHOD_FUNC(mf_past), 0);

    rb_define_global_function("is_adir_right", RUBY_METHOD_FUNC(mf_is_adir_right), 0);
    rb_define_global_function("is_adir_left", RUBY_METHOD_FUNC(mf_is_adir_left), 0);

    rb_define_global_function("is_isearch_on", RUBY_METHOD_FUNC(mf_is_isearch_on), 0);
    rb_define_global_function("isearch_off", RUBY_METHOD_FUNC(mf_isearch_off), 0);
    rb_define_global_function("isearch_on", RUBY_METHOD_FUNC(mf_isearch_on), 0);
    rb_define_global_function("view_option", RUBY_METHOD_FUNC(mf_view_option), 0);
    rb_define_global_function("malias", RUBY_METHOD_FUNC(mf_malias), 3);
    rb_define_global_function("rehash", RUBY_METHOD_FUNC(mf_rehash), 0);

    rb_define_global_function("help", RUBY_METHOD_FUNC(mf_help), 0);
    rb_define_global_function("set_xterm", RUBY_METHOD_FUNC(mf_set_xterm), 4);
    rb_define_global_function("cursor", RUBY_METHOD_FUNC(mf_cursor), 0);
    rb_define_global_function("file_name", RUBY_METHOD_FUNC(mf_file_name), 1);
    rb_define_global_function("cursor_max", RUBY_METHOD_FUNC(mf_cursor_max), 0);
    rb_define_global_function("scrolltop", RUBY_METHOD_FUNC(mf_scrolltop), 0);
    rb_define_global_function("set_scrolltop", RUBY_METHOD_FUNC(mf_set_scrolltop), 1);
    rb_define_global_function("cursor_x", RUBY_METHOD_FUNC(mf_cursor_x), 0);
    rb_define_global_function("cursor_y", RUBY_METHOD_FUNC(mf_cursor_y), 0);
    rb_define_global_function("cursor_maxx", RUBY_METHOD_FUNC(mf_cursor_maxx), 0);
    rb_define_global_function("cursor_maxy", RUBY_METHOD_FUNC(mf_cursor_maxy), 0);
    rb_define_global_function("xterm_next", RUBY_METHOD_FUNC(mf_xterm_next), 0);
    rb_define_global_function("keymap", RUBY_METHOD_FUNC(mf_keymap), 11);
    rb_define_global_function("completion", RUBY_METHOD_FUNC(mf_completion), 7);
    rb_define_global_function("completion2", RUBY_METHOD_FUNC(mf_completion2), 8);
    rb_define_global_function("minitscr", RUBY_METHOD_FUNC(mf_minitscr), 0);
    rb_define_global_function("mendwin", RUBY_METHOD_FUNC(mf_mendwin), 0);
    rb_define_global_function("mmove", RUBY_METHOD_FUNC(mf_mmove), 2);
    rb_define_global_function("mmove_immediately", RUBY_METHOD_FUNC(mf_mmove_immediately), 2);
    rb_define_global_function("mmvprintw", RUBY_METHOD_FUNC(mf_mmvprintw), -1);
    rb_define_global_function("mprintw", RUBY_METHOD_FUNC(mf_mprintw), -1);
    rb_define_global_function("mattron", RUBY_METHOD_FUNC(mf_mattron), 1);
    rb_define_global_function("mattroff", RUBY_METHOD_FUNC(mf_mattroff), 0);
    rb_define_global_function("mgetmaxx", RUBY_METHOD_FUNC(mf_mgetmaxx), 0);
    rb_define_global_function("mgetmaxy", RUBY_METHOD_FUNC(mf_mgetmaxy), 0);
    rb_define_global_function("mclear_immediately", RUBY_METHOD_FUNC(mf_mclear_immediately), 0);
    rb_define_global_function("mclear", RUBY_METHOD_FUNC(mf_mclear), 0);
    rb_define_global_function("mclear_online", RUBY_METHOD_FUNC(mf_mclear_online), 1);
    rb_define_global_function("mbox", RUBY_METHOD_FUNC(mf_mbox), 4);
    rb_define_global_function("mrefresh", RUBY_METHOD_FUNC(mf_mrefresh), 0);
    rb_define_global_function("mkbuf_exist", RUBY_METHOD_FUNC(mf_mkbuf_exist), 0);
    rb_define_global_function("mgetch", RUBY_METHOD_FUNC(mf_mgetch), 0);
    rb_define_global_function("mis_curses", RUBY_METHOD_FUNC(mf_mis_curses), 0);
    rb_define_global_function("hostname", RUBY_METHOD_FUNC(mf_hostname), 0);
    rb_define_global_function("username", RUBY_METHOD_FUNC(mf_username), 0);
    rb_define_global_function("set_shell", RUBY_METHOD_FUNC(mf_set_shell), 3);
    rb_define_global_function("message", RUBY_METHOD_FUNC(mf_message), -1);
    rb_define_global_function("history_size", RUBY_METHOD_FUNC(mf_history_size), 1);
    rb_define_global_function("select_string", RUBY_METHOD_FUNC(mf_select_string), 3);
    rb_define_global_function("sort_kind", RUBY_METHOD_FUNC(mf_sort_kind), 0);
    rb_define_global_function("undo", RUBY_METHOD_FUNC(mf_undo), 0);
    rb_define_global_function("view_color", RUBY_METHOD_FUNC(mf_view_color), 0);
    rb_define_global_function("tab_close", RUBY_METHOD_FUNC(mf_tab_close), 1);
    rb_define_global_function("tab_up", RUBY_METHOD_FUNC(mf_tab_up), 1);
    rb_define_global_function("tab_path", RUBY_METHOD_FUNC(mf_tab_path), 1);
    rb_define_global_function("tab_max", RUBY_METHOD_FUNC(mf_tab_max), 0);
    rb_define_global_function("tab_new", RUBY_METHOD_FUNC(mf_tab_new), 1);
    rb_define_global_function("is_marked", RUBY_METHOD_FUNC(mf_is_marked), 1);
    rb_define_global_function("marking", RUBY_METHOD_FUNC(mf_marking), 0);
    rb_define_global_function("is_marked_sdir", RUBY_METHOD_FUNC(mf_is_marked_sdir), 1);
    rb_define_global_function("marking_sdir", RUBY_METHOD_FUNC(mf_marking_sdir), 0);

TEND();    
}

void command_final()
{
TBEGIN();

TEND();
}
