#include "saphire.h"
#include <string.h>
#include <strings.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
#include <libgen.h>
#include <dirent.h>
#include <readline/readline.h>
#include <readline/history.h>
#include <oniguruma.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <pwd.h>

#include "config.h"
#include "saphire_extra.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 <limits.h>

#if !defined(HAVE_ONIGUCHAR)
#define OnigUChar unsigned char
#endif

void saphire_get_quoted_fname_readline_ver(char* file, string_obj* ret);

typedef struct {
    string_obj* mName;

    struct stat mStat;                // stat()のキャッシュ
} sFile;

sFile* sFile_new(char* name, struct stat* stat_)
{
    sFile* self = MALLOC(sizeof(sFile));

    self->mName = STRING_NEW(name);
    self->mStat = *stat_;

    return self;
}

void sFile_delete(sFile* self)
{
    string_delete(self->mName);
    FREE(self);
}

static BOOL sort_name(void* left, void* right)
{
    sFile* l = (sFile*) left;
    sFile* r = (sFile*) right;

    char* lfname = string_c_str(l->mName);
    char* rfname = string_c_str(r->mName);

    if(strcmp(lfname, ".") == 0) return TRUE;
    if(strcmp(lfname, "..") == 0) {
        if(strcmp(rfname, ".") == 0) return FALSE;

        return TRUE;          
    }
    if(strcmp(rfname, ".") == 0) return FALSE;
    if(strcmp(rfname, "..") == 0) return FALSE;

    if(!S_ISDIR(l->mStat.st_mode) && !S_ISDIR(r->mStat.st_mode)
        || S_ISDIR(l->mStat.st_mode) && S_ISDIR(r->mStat.st_mode) )
    {
        return strcasecmp(lfname, rfname) < 0;
    }

    if(S_ISDIR(l->mStat.st_mode))
        return TRUE;
    else
        return FALSE;
}









//////////////////////////////////////////////////////////////////////
// static variable
//////////////////////////////////////////////////////////////////////
static vector_obj* files = NULL;

//////////////////////////////////////////////////////////////////////
// readline
//////////////////////////////////////////////////////////////////////
static BOOL name_sort(void* left, void* right)
{
    string_obj* l = (string_obj*)left;
    string_obj* r = (string_obj*) right;

    char* lfname = string_c_str(l);
    char* rfname = string_c_str(r);

    if(strcmp(lfname, ".") == 0) return TRUE;
    if(strcmp(lfname, "..") == 0) {
        if(strcmp(rfname, ".") == 0) return FALSE;

        return TRUE;          
    }
    if(strcmp(rfname, ".") == 0) return FALSE;
    if(strcmp(rfname, "..") == 0) return FALSE;

    return strcasecmp(lfname, rfname) < 0;
}
void saphire_get_quoted_fname_readline_ver(char* file, string_obj* ret)
{
    string_obj* tmp = STRING_NEW("");
    saphire_get_quoted_fname(file, tmp);

    /// ~のクォートを除外 ///
    char* p = string_c_str(tmp);
    
    while(*p) {
        if(*p == '\\' && *(p+1) == '~') {
            p++;
            string_push_back2(ret, *p++);
        }
        else {
            string_push_back2(ret, *p++);
        }
    }

    string_delete(tmp);
}


static string_obj* gEditingDir;
static BOOL read_one_argument(char* p, string_obj* buf)
{
    /// 単語の区切り記号かどうかのテーブル 127以下なら
    static unsigned char table[] = { 
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0,
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
        1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 
        1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0 
    };

    BOOL squote = FALSE;
    BOOL dquote = FALSE;
    while(*p && (p - rl_line_buffer < rl_point)) {
        /// 空文字
        if(!squote && !dquote && *p == '"' && *(p+1) == '"') {
            p+=2;
            string_put(buf, "");
        }
        else if(!dquote && !squote && *p == '\'' && *(p+1) == '\'') {
            p+=2;
            string_put(buf, "");
        }
        else if(*p == '\\' && *(p+1) == 't') {
            string_push_back2(buf, '\t');

            p+=2;
        }
        else if(*p == '\\' && *(p+1) == 'n') {
            string_push_back2(buf, '\n');

            p+=2;
        }
        else if(*p == '\\' && *(p+1) == 'r') {
            string_push_back2(buf, '\r');

            p+=2;
        }
        else if(!squote && !dquote && *p == '\\') {
            p++;

            if(*p != 0) {
                string_push_back2(buf, *p);
                p++;
            }
            else {
                break;
            }
        }
        else if(!dquote && *p == '\'') {
            p++;
            squote = !squote;
        }
        else if(!squote && *p == '"') {
            dquote = !dquote;
            p++;
        }
        else if(squote || dquote) {
            if(*p == 0) {
                break;
            }
            else {
                string_push_back2(buf, *p);

                p++;
            }
        }
        else if(((unsigned char)*p) > 127) {
            string_push_back2(buf, *p);
            p++;
        }
        else if(table[*p]) {
            string_put(buf, "");
            p++;
        }
        else {
            string_push_back2(buf, *p);
            p++;
        }
    }
}

void parentname2(char* result, int result_size, char* path)
{
    char* p;

    if(*path == 0) {
        xstrncpy(result, "", result_size);
        return;
    }
    
    if(strcmp(path, "/") == 0) {
        xstrncpy(result, "/", result_size);
        return;
    }

    for(p=path + strlen(path)-1; p != path-1; p--) {
        if(*p == '/') {
            memcpy(result, path, p-path);
            result[p-path] = '/';
            result[p-path+1] = 0;
            
            return;
        }
    }

    xstrncpy(result, "", result_size);
}


#if defined(HAVE_MIGEMO_H)
#include <migemo.h>

migemo* gMigemo;
static regex_t* gReg;

void migemo_init()
{
    char buf[PATH_MAX];
    char migemodir[PATH_MAX];
    gMigemo = migemo_open(NULL);

    snprintf(migemodir, PATH_MAX, "%s", SYSTEM_MIGEMODIR);

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

void migemo_final()
{
    onig_end();
    migemo_close(gMigemo);
    if(gReg) onig_free(gReg);
    gReg = NULL;
}

/// 先頭から同じ文字列があるかどうか調べる。
/// 0 == 同じ文字は0文字
/// 1 == 同じ文字は1文字
/// 文字列はmbsとしてみる
int str_headsame(char* str1, char* str2)
{
    char* p1 = str1;
    char* p2 = str2;

    while(*p1 && *p2 && *p1 == *p2) {
        p1++;
        p2++;
    }

    return p1 - str1;
}


char* readline_file_completion_migemo_match(char* text, char* file)
{
    int same_point = str_headsame(text, file);

    OnigUChar * p;
    if(same_point == 0) {
        p = migemo_query(gMigemo, text);
    }
    /// text側がfileの部分なら判定するまでも無くマッチ ///
    else if(same_point == strlen(text)) {
        char tmp[PATH_MAX];
        xstrncpy(tmp, file, PATH_MAX);
        string_obj* tmp2 = STRING_NEW("");
        saphire_get_quoted_fname_readline_ver(tmp, tmp2);
        char* ret = strdup(string_c_str(tmp2));
        string_delete(tmp2);

        return ret;
    }
    else {
        p = migemo_query(gMigemo, text+same_point);
    }


    if(p == NULL) {
        return NULL;
    }

    static regex_t* reg;

    int r;
    OnigErrorInfo err_info;

    char* p2 = MALLOC(strlen(p)*2);

    char* _p = p;
    char* _p2 = p2;

    while(*_p) {
        if(*_p == '+') {
            *_p2++ = '\\';
            *_p2++ = *_p++;
        }
        else {
            *_p2++ = *_p++;
        }
    }
    *_p2 = 0;

    if(gKanjiCode == kUtf8) {
        r = onig_new(&reg, p2, p2 + strlen((char*)p2), ONIG_OPTION_DEFAULT, ONIG_ENCODING_UTF8, ONIG_SYNTAX_DEFAULT,  &err_info);
    }
    else if(gKanjiCode == kEucjp) {
        r = onig_new(&reg, p2, p2 + strlen((char*)p2), ONIG_OPTION_DEFAULT, ONIG_ENCODING_EUC_JP, ONIG_SYNTAX_DEFAULT, &err_info);
    }
    else if(gKanjiCode == kSjis) {
        r = onig_new(&reg, p2, p2 + strlen((char*)p2), ONIG_OPTION_DEFAULT, ONIG_ENCODING_SJIS, ONIG_SYNTAX_DEFAULT, &err_info);
    }

    FREE(p2);

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

    if(r != ONIG_NORMAL) {
        return NULL;
    }

    OnigRegion* region = onig_region_new();
    
    OnigUChar* file2;
    if(same_point == 0) {
        file2 = (OnigUChar*)file;
    }
    else {
        file2 = (OnigUChar*)file + same_point;
    }

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

    onig_region_free(region, 1);

    onig_free(reg);

    if(r2 == 0) {
        char tmp[PATH_MAX];
        xstrncpy(tmp, file, PATH_MAX);
        string_obj* tmp2 = STRING_NEW("");
        saphire_get_quoted_fname_readline_ver(tmp, tmp2);
        char* ret = strdup(string_c_str(tmp2));
        string_delete(tmp2);

        return ret;
    }

    return NULL;
}


char* readline_file_completion_migemo(const char* text, int rstat)
{
    /// クォートを除外 ///
    char* text2 = MALLOC(strlen(text) +1);
    char* _p = (char*)text;
    char* _p2 = text2;

    while(*_p) {
        if(*_p == '\\') {
            _p++;
        }
        else {
            *_p2++ = *_p++;
        }
    }
    *_p2 = 0;

    static int index, wordlen;

    /// 初期化 ///
    if(rstat == 0) {
        wordlen = strlen(text2);
        index = 0;

        // ディレクトリのパートを得る
        string_put(gEditingDir, "");
        read_one_argument(rl_line_buffer, gEditingDir);
        char* tmp = MALLOC(string_length(gEditingDir) + 1);
        parentname2(tmp, string_length(gEditingDir) + 1, string_c_str(gEditingDir));
        string_put(gEditingDir, tmp);
        FREE(tmp);

        /// チルダから始まっている場合 ///
        if(string_c_str(gEditingDir)[0] == '~') {
            char* p = string_c_str(gEditingDir);
            p++;
            string_obj* user = STRING_NEW("");
            while(*p && *p != '/') {
                string_push_back2(user, *p);
                p++;
            }

            /// ユーザー名からホームディレクトリに展開
            string_obj* gEditingDir2 = STRING_NEW("");

            if(string_length(user) == 0) {
                char* home = getenv("HOME");
                if(home) {
                    string_put(gEditingDir2, home);
                }
                else {
                    struct passwd* pw = getpwuid(getuid());
                    if(pw) {
                        string_put(gEditingDir2, pw->pw_dir);
                    }
                    else {
                        string_put(gEditingDir2, "~");
                    }
                }
            }
            else {
                struct passwd* pw = getpwnam(string_c_str(user));
                if(pw) {
                    string_put(gEditingDir2, pw->pw_dir);
                }
                else {
                    string_put(gEditingDir2, "~");
                    string_push_back(gEditingDir2, string_c_str(user));
                }
            }

            string_push_back(gEditingDir2, p);

            string_delete(user);

            string_put(gEditingDir, string_c_str(gEditingDir2));
            string_delete(gEditingDir2);
        }

        /// 補完候補を初期化 ///
        if(files == NULL) {
            files = VECTOR_NEW(50);
        }
        else {
            int i;
            for(i=0; i<vector_size(files); i++) {
                string_delete(vector_item(files, i));
            }
            vector_clear(files);
        }

        /// 補完候補をディレクトリから読み込み ///
        if(strcmp(string_c_str(gEditingDir), "") != 0) {
            DIR* dir = opendir(string_c_str(gEditingDir));

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

                        struct stat _stat;
                        xstrncpy(buf, string_c_str(gEditingDir), PATH_MAX);
                        xstrncat(buf, entry->d_name, PATH_MAX-1);
                        stat(buf, &_stat);

#if defined(__DARWIN__)
                        {
                        char* src = entry->d_name;
                        int des_size = strlen(src)*2;
                        char* des = MALLOC(des_size);
                        if(is_all_ascii(src)) {
                            xstrncpy(des, src, des_size);
                        }
                        else {
                            /// コンバート ///
                            if(kanji_convert(src, des, des_size, kUtf8Mac, kUtf8) < 0) {
                                xstrncpy(des, src, des_size);
                            }
                        }
                        xstrncpy(buf, des, PATH_MAX);

                        FREE(des);
                        }
#else
                        xstrncpy(buf, entry->d_name, PATH_MAX);
#endif

                        if(S_ISDIR(_stat.st_mode)) {
                            xstrncat(buf, "/", PATH_MAX-1);
                        }

                        vector_add(files, STRING_NEW(buf));
                    }
                }

                closedir(dir);

                (void)vector_sort(files,name_sort);
            }
        }
        else {
            DIR* dir = opendir(".");

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

                        struct stat _stat;
                        xstrncpy(buf, "./", PATH_MAX);
                        xstrncat(buf, entry->d_name, PATH_MAX-1);
                        stat(buf, &_stat);

#if defined(__DARWIN__)
                        {
                        char* src = entry->d_name;
                        int des_size = strlen(src)*2;
                        char* des = MALLOC(des_size);
                        if(is_all_ascii(src)) {
                            xstrncpy(des, src, des_size);
                        }
                        else {
                            /// コンバート ///
                            if(kanji_convert(src, des, des_size, kUtf8Mac, kUtf8) < 0) {
                                xstrncpy(des, src, des_size);
                            }
                        }
                        xstrncpy(buf, des, PATH_MAX);
                        FREE(des);
                        }
#else
                        xstrncpy(buf, entry->d_name, PATH_MAX);
#endif
                        if(S_ISDIR(_stat.st_mode)) {
                            xstrncat(buf, "/", PATH_MAX-1);
                        }
                        vector_add(files, STRING_NEW(buf));
                    }
                }

                closedir(dir);

                (void)vector_sort(files, name_sort);
            }
        }
    }

    while(index < vector_size(files)) {
        char* file = string_c_str(vector_item(files, index));
        index++;

        /// ファイル名の入力がない補完は判定しなくても全てマッチ ///
        if(strcmp(text2, "") == 0) {
            char path[PATH_MAX];
            xstrncpy(path, file, PATH_MAX);

            string_obj* tmp2 = STRING_NEW("");
            saphire_get_quoted_fname_readline_ver(path, tmp2);
            char* ret = strdup(string_c_str(tmp2));
            string_delete(tmp2);

            struct stat stat_;
            xstrncpy(path, string_c_str(gEditingDir), PATH_MAX);
            xstrncat(path, file, PATH_MAX);
            if(stat(path, &stat_) == 0) {
                if(S_ISDIR(stat_.st_mode)) {
                    rl_completion_append_character= 0;
                }
                else {
                    rl_completion_append_character= ' ';
                }
            }
            else {
                rl_completion_append_character= ' ';
            }

            if(ret) {
                FREE(text2);
                return ret;
            }
        }

        /// アスキー文字同士の補完 ///
        else if(is_all_ascii((char*)text2) && is_all_ascii(file)) {
            if(!strncmp(text2, file, wordlen)) {
                char path[PATH_MAX];
                xstrncpy(path, file, PATH_MAX);

                string_obj* tmp2 = STRING_NEW("");
                saphire_get_quoted_fname(path, tmp2);
                char* ret = strdup(string_c_str(tmp2));
                string_delete(tmp2);

                xstrncpy(path, string_c_str(gEditingDir), PATH_MAX);
                xstrncat(path, file, PATH_MAX);
                struct stat stat_;
                if(stat(path, &stat_) == 0) {
                    if(S_ISDIR(stat_.st_mode)) {
                        rl_completion_append_character= 0;
                    }
                    else {
                        rl_completion_append_character= ' ';
                    }
                }
                else {
                    rl_completion_append_character= ' ';
                }

                if(ret) { 
                    FREE(text2);
                    return ret; 
                }
            }
        }
        /// migemo 補完 ///
        else {
            char* ret = readline_file_completion_migemo_match((char*)text2 , file);
            if(ret) {
                struct stat stat_;
                char path[PATH_MAX];
                xstrncpy(path, string_c_str(gEditingDir), PATH_MAX);
                xstrncat(path, file, PATH_MAX);
                if(stat(path, &stat_) == 0) {
                    if(S_ISDIR(stat_.st_mode)) {
                        rl_completion_append_character= 0;
                    }
                    else {
                        rl_completion_append_character= ' ';
                    }
                }
                else {
                    rl_completion_append_character= ' ';
                }

                FREE(text2);
                return ret;
            }
        }
    }

    if(index == 0 && vector_size(files) == 0) {
        index ++;
        FREE(text2);
        rl_completion_append_character= 0;
        return strdup("");
    }


    FREE(text2);
    return NULL;
}
#endif

char* readline_program_completion(const char* text, int stat)
{
    static int index, wordlen;

    if(stat == 0) {
        wordlen = strlen(text);
        index = 0;
    }

    while(index < vector_size(gSaphireProgCompletions)) {
        char* program = string_c_str(vector_item(gSaphireProgCompletions, index));
        index++;

        if(!strncmp(text, program, wordlen)) {
            return strdup(program);
        }
    }

    return NULL;
}

char* readline_env_completion(const char* text, int stat)
{
    static int index, wordlen;

    if(stat == 0) {
        wordlen = strlen(text);
        index = 0;
    
        saphire_make_var_completion_list();
    }
    while(1) {
        if(index < vector_size(gSaphireVarCompletions)) {
            char* env = string_c_str(vector_item(gSaphireVarCompletions, index));
            int env2_size = strlen(env) + 10;
            char* env2 = malloc(env2_size);

            if(*text == '$' && *(text+1) != '$') {
                xstrncpy(env2, "$", env2_size);
                xstrncat(env2, env, env2_size);
            }
            else if(*text == '$' && *(text+1) == '$') {
                xstrncpy(env2, "$$", env2_size);
                xstrncat(env2, env, env2_size);
            }
            else if(*text == '@' && *(text+1) != '@') {
                xstrncpy(env2, "@", env2_size);
                xstrncat(env2, env, env2_size);
            }
            else if(*text == '@' && *(text+1) == '@') {
                xstrncpy(env2, "@@", env2_size);
                xstrncat(env2, env, env2_size);
            }

            index++;

            if(strncmp(text, env2, wordlen) == 0) {
                return env2;
            }

            free(env2);
        }
        else {
            break;
        }
    }

    return NULL;
}

char* readline_file_completion(const char* text, int rstat)
{
    /// クォートを除外 ///
    char* text2 = MALLOC(strlen(text) +1);
    char* _p = (char*)text;
    char* _p2 = text2;

    while(*_p) {
        if(*_p == '\\') {
            _p++;
        }
        else {
            *_p2++ = *_p++;
        }
    }
    *_p2 = 0;

    static int index, wordlen;

    /// 初期化 ///
    if(rstat == 0) {
        wordlen = strlen(text2);
        index = 0;

        // ディレクトリのパートを得る
        string_put(gEditingDir, "");
        read_one_argument(rl_line_buffer, gEditingDir);
        char* tmp = MALLOC(string_length(gEditingDir) + 1);
        parentname2(tmp, string_length(gEditingDir) + 1, string_c_str(gEditingDir));
        string_put(gEditingDir, tmp);
        FREE(tmp);

        /// チルダから始まっている場合 ///
        if(string_c_str(gEditingDir)[0] == '~') {
            char* p = string_c_str(gEditingDir);
            p++;
            string_obj* user = STRING_NEW("");
            while(*p && *p != '/') {
                string_push_back2(user, *p);
                p++;
            }

            /// ユーザー名からホームディレクトリに展開
            string_obj* gEditingDir2 = STRING_NEW("");

            if(string_length(user) == 0) {
                char* home = getenv("HOME");
                if(home) {
                    string_put(gEditingDir2, home);
                }
                else {
                    struct passwd* pw = getpwuid(getuid());
                    if(pw) {
                        string_put(gEditingDir2, pw->pw_dir);
                    }
                    else {
                        string_put(gEditingDir2, "~");
                    }
                }
            }
            else {
                struct passwd* pw = getpwnam(string_c_str(user));
                if(pw) {
                    string_put(gEditingDir2, pw->pw_dir);
                }
                else {
                    string_put(gEditingDir2, "~");
                    string_push_back(gEditingDir2, string_c_str(user));
                }
            }

            string_push_back(gEditingDir2, p);

            string_delete(user);

            string_put(gEditingDir, string_c_str(gEditingDir2));
            string_delete(gEditingDir2);
        }

        /// 補完候補を初期化 ///
        if(files == NULL) {
            files = VECTOR_NEW(50);
        }
        else {
            int i;
            for(i=0; i<vector_size(files); i++) {
                string_delete(vector_item(files, i));
            }
            vector_clear(files);
        }

        /// 補完候補をディレクトリから読み込み ///
        if(strcmp(string_c_str(gEditingDir), "") != 0) {
            DIR* dir = opendir(string_c_str(gEditingDir));

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

                        struct stat _stat;
                        xstrncpy(buf, string_c_str(gEditingDir), PATH_MAX);
                        xstrncat(buf, entry->d_name, PATH_MAX);
                        stat(buf, &_stat);

#if defined(__DARWIN__)
                        {
                        char* src = entry->d_name;
                        int des_size = strlen(src)*2;
                        char* des = MALLOC(des_size);
                        if(is_all_ascii(src)) {
                            xstrncpy(des, src, des_size);
                        }
                        else {
                            /// コンバート ///
                            if(kanji_convert(src, des, des_size, kUtf8Mac, kUtf8) < 0) {
                                xstrncpy(des, src, des_size);
                            }
                        }
                        xstrncpy(buf, des, PATH_MAX);
                        FREE(des);
                        }
#else
                        xstrncpy(buf, entry->d_name, PATH_MAX);
#endif
                        if(S_ISDIR(_stat.st_mode)) {
                            xstrncat(buf, "/", PATH_MAX);
                        }

                        vector_add(files, STRING_NEW(buf));
                    }
                }

                closedir(dir);

                (void)vector_sort(files,name_sort);
            }
        }
        else {
            DIR* dir = opendir(".");

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

                        struct stat _stat;
                        xstrncpy(buf, "./", PATH_MAX);
                        xstrncat(buf, entry->d_name, PATH_MAX);
                        stat(buf, &_stat);

#if defined(__DARWIN__)
                        {
                        char* src = entry->d_name;
                        int des_size = strlen(src)*2;
                        char* des = MALLOC(des_size);
                        if(is_all_ascii(src)) {
                            xstrncpy(des, src, des_size);
                        }
                        else {
                            /// コンバート ///
                            if(kanji_convert(src, des, des_size, kUtf8Mac, kUtf8) < 0) {
                                xstrncpy(des, src, des_size);
                            }
                        }
                        xstrncpy(buf, des, PATH_MAX);
                        FREE(des);
                        }
#else
                        xstrncpy(buf, entry->d_name, PATH_MAX);
#endif
                        if(S_ISDIR(_stat.st_mode)) {
                            xstrncat(buf, "/", PATH_MAX);
                        }
                        vector_add(files, STRING_NEW(buf));
                    }
                }

                closedir(dir);

                (void)vector_sort(files, name_sort);
            }
        }
    }

    while(index < vector_size(files)) {
        char* file = string_c_str(vector_item(files, index));
        index++;

        /// ファイル名の入力がない補完は判定しなくても全てマッチ ///
        if(strcmp(text2, "") == 0) {
            char path[PATH_MAX];
            xstrncpy(path, file, PATH_MAX);

            string_obj* tmp2 = STRING_NEW("");
            saphire_get_quoted_fname_readline_ver(path, tmp2);
            char* ret = strdup(string_c_str(tmp2));
            string_delete(tmp2);

            struct stat stat_;
            xstrncpy(path, string_c_str(gEditingDir),  PATH_MAX);
            xstrncat(path, file, PATH_MAX);
            if(stat(path, &stat_) == 0) {
                if(S_ISDIR(stat_.st_mode)) {
                    rl_completion_append_character= 0;
                }
                else {
                    rl_completion_append_character= ' ';
                }
            }
            else {
                rl_completion_append_character= ' ';
            }

            if(ret) {
                FREE(text2);
                return ret;
            }
        }

        /// アスキー文字同士の補完 ///
//        else if(is_all_ascii((char*)text2) && is_all_ascii(file)) {
        else {
            if(!strncmp(text2, file, wordlen)) {
                char path[PATH_MAX];
                xstrncpy(path, file, PATH_MAX);

                string_obj* tmp2 = STRING_NEW("");
                saphire_get_quoted_fname(path, tmp2);
                char* ret = strdup(string_c_str(tmp2));
                string_delete(tmp2);

                xstrncpy(path, string_c_str(gEditingDir), PATH_MAX);
                xstrncat(path, file, PATH_MAX);
                struct stat stat_;
                if(stat(path, &stat_) == 0) {
                    if(S_ISDIR(stat_.st_mode)) {
                        rl_completion_append_character= 0;
                    }
                    else {
                        rl_completion_append_character= ' ';
                    }
                }
                else {
                    rl_completion_append_character= ' ';
                }

                if(ret) { 
                    FREE(text2);
                    return ret; 
                }
            }
        }
    }

    if(index == 0 && vector_size(files) == 0) {
        index ++;
        FREE(text2);
        rl_completion_append_character= 0;
        return strdup("");
    }


    FREE(text2);
    return NULL;
}

char** readline_on_complete(const char* text, int start, int end)
{
//printf("\n\ntext (%s) start %d end %d rl_line_buffer %s\n\n", text, start, end, rl_line_buffer);
    
    BOOL program = TRUE;
    BOOL env = FALSE;
    BOOL user = FALSE;

    char* p = rl_line_buffer;

    while(*p == ' ' || *p == '\t' || *p == '\n') {
        p++;
    }

    BOOL squote = FALSE;
    BOOL dquote = FALSE;
    while(p < rl_line_buffer + end) {
        // クォート
        if(*p == '\\' && *(p+1) == 't') {
            p+=2;
            env = FALSE;
            user = FALSE;
        }
        else if(*p == '\\' && *(p+1) == 'n') {
            p+=2;
            env = FALSE;
            user = FALSE;
        }
        else if(*p == '\\' && *(p+1) == 'r') {
            p+=2;
            env = FALSE;
            user = FALSE;
        }
        else if(*p == '\\') {
            p+=2;
            env = FALSE;
            user = FALSE;
        }

        // シングルクォート
        else if(!dquote && *p == '\'') {
            p++;
            squote = !squote;
            env = FALSE;
            user = FALSE;
        }
        // ダブルクォート
        else if(!squote && *p == '"') {
            p++;
            dquote = !dquote;
            env = FALSE;
            user = FALSE;
        }
        // 環境変数 $$()
        else if(!squote && *p == '$' && *(p+1) == '$' && *(p+2) == '(') {
            p+=3;

            program = TRUE;

            while(*p == ' ' || *p == '\t' || *p == '\n') {
                p++;
            }
            env = FALSE;
            user = FALSE;
        }
        // 環境変数 $()
        else if(!squote && *p == '$' && *(p+1) == '(') {
            p+=2;

            program = TRUE;
            while(*p == ' ' || *p == '\t' || *p == '\n') {
                p++;
            }
            env = FALSE;
            user = FALSE;
        }
        else if(!dquote && !squote && *p == ')') {
            p++;
            program = FALSE;
            while(*p == ' ' || *p == '\t' || *p == '\n') {
                p++;
            }
            env = FALSE;
            user = FALSE;
        }

        // 環境変数 $$
        else if(!squote && *p == '$' && *(p+1) == '$') {
            p+=2;
            env = TRUE;
            user = FALSE;
        }
        // 環境変数 $
        else if(!squote && *p == '$') {
            p++;
            env = TRUE;
            user = FALSE;
        }

        // 環境変数 @
        else if(!squote && *p == '@' && *(p+1) == '@') {
            p+=2;
            env = TRUE;
            user = FALSE;
        }
        // 環境変数 @@
        else if(!squote && *p == '@') {
            p++;
            env = TRUE;
            user = FALSE;
        }
        /// シングルクォート、ダブルクォート中 ///
        else if(squote || dquote) {
            p++;
        }
        else if(*p == '~') {
            p++;
            env = FALSE;
            user = TRUE;
        }
        /// グロブ ///
        else if(*p == '*' || *p == '?' || *p =='[' && (*(p+1) != ' ' && *(p+1) != '\t' && *(p+1) == '\n')) 
        {
            p++;
            env = FALSE;
            user = FALSE;
        }
/*
        else if(*p == '-' && *(p+1) == '>') {
            p+=2;
            program = TRUE;
            env = FALSE;
            user = FALSE;
        }
*/
        else if(*p == '<' && program) {
            p++;

            program = FALSE;
            env = FALSE;
            user = FALSE;
        }
        /// 単語の区切り /////////////////////////////////////////////////
    
        /// 空白 ///
        else if(*p == ' ' || *p == '\t') {
            while(*p == ' ' || *p == '\t') {
                p++;
            }

            if(program) program = FALSE;
            env = FALSE;
            user = FALSE;
        }
        /// ブロック
        else if(*p == ':' || *p == '{') {
            p++;
            program = TRUE;
            while(*p == ' ' || *p == '\t' || *p == '\n') {
                p++;
            }
            env = FALSE;
            user = FALSE;
        }
        
        /// コマンドの区切り ///////////////////////////////////////////
        
        /// パイプ ///
        else if(*p == '|' && *(p+1) == '~') {
            p+=2;

            program = TRUE;
            while(*p == ' ' || *p == '\t' || *p == '\n') {
                p++;
            }
            env = FALSE;
            user = FALSE;
        }
        
        /// パイプ ///
        else if(*p == '|' && *(p+1) != '|') {
            p++;

            program = TRUE;
            while(*p == ' ' || *p == '\t' || *p == '\n') {
                p++;
            }
            env = FALSE;
            user = FALSE;
        }
        
        /// 文(statment)の区切り系 ///////////////////////////////////
    
        /// アンドアンド ///
        else if(*p == '&' && *(p+1) == '&') {
            p+=2;

            program = TRUE;
            while(*p == ' ' || *p == '\t' || *p == '\n') {
                p++;
            }
            env = FALSE;
            user = FALSE;
        }
        /// オアオア ///
        else if(*p == '|' && *(p+1) == '|') {
            p+=2;

            program = TRUE;
            while(*p == ' ' || *p == '\t' || *p == '\n') {
                p++;
            }
            env = FALSE;
            user = FALSE;
        }
        /// バックグラウンド ///
        else if(*p == '&' && *(p+1) != '&') {
            p++;

            program = TRUE;
            while(*p == ' ' || *p == '\t' || *p == '\n') {
                p++;
            }
            env = FALSE;
            user = FALSE;
        }
        /// セミコロン ///
        else if(*p == ';') {
            p++;

            program = TRUE;
            while(*p == ' ' || *p == '\t' || *p == '\n') {
                p++;
            }
            env = FALSE;
            user = FALSE;
        }
        /// 改行 ///
        else if(*p == '\n') {
            p++;

            program = TRUE;
            while(*p == ' ' || *p == '\t' || *p == '\n') {
                p++;
            }
            env = FALSE;
            user = FALSE;
        }
        /// コメント ///
        else if(*p == '#') {
            p++;

            while(*p) {
                if(*p == '\n') {
                    p++;
                    program = TRUE;
                    break;
                }
                else {
                    p++;
                }
            }
            while(*p == ' ' || *p == '\t' || *p == '\n') {
                p++;
            }
            env = FALSE;
            user = FALSE;
        }
        else if(*p == '/') {
            env = FALSE;
            user = FALSE;
            program = FALSE;
            p++;
        }
        /// 終了 ///
        else if(*p == 0) {
            break;
        }
        
        /// 構文 ////////////////////////////////////////////////////////

        /// サブシェル ///
        else if(*p == '(') {
            p++;
            program = TRUE;
            env = FALSE;
            user = FALSE;
            while(*p == ' ' || *p == '\t' || *p == '\n') {
                p++;
            }
        }
        else {
            p++;
        }
    }

    if(user) {
        rl_completion_append_character= 0;
        return rl_completion_matches(text, rl_username_completion_function);
    }
    else if(env) {
        rl_completion_append_character= ' ';
        return rl_completion_matches(text, readline_env_completion);
    }
    else if(program) {
        rl_completion_append_character= ' ';
        return rl_completion_matches(text, readline_program_completion);
    }
#if defined(HAVE_MIGEMO_H)
    else {
        return rl_completion_matches(text, readline_file_completion_migemo);
    }
#else
    else {
        return rl_completion_matches(text, readline_file_completion);
    }
#endif

    return NULL;
}




////////////////////////////////////////////////////////////
// lftp, bashよりコピー
////////////////////////////////////////////////////////////
static bool shell_cmd;
static bool quote_glob;
static bool inhibit_tilde;

/* mbschr.c - strchr(3) that handles multibyte characters. */

/* Copyright (C) 2002 Free Software Foundation, Inc.

   This file is part of GNU Bash, the Bourne Again SHell.

   Bash is free software: you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation, either version 3 of the License, or
   (at your option) any later version.

   Bash is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with Bash.  If not, see <http://www.gnu.org/licenses/>.
*/

#include <config.h>

#ifdef HAVE_STDLIB_H
#  include <stdlib.h>
#endif

#undef mbschr

/* In some locales, the non-first byte of some multibyte characters have
   the same value as some ascii character.  Faced with these strings, a
   legacy strchr() might return the wrong value. */

char *
#if defined (PROTOTYPES)
mbschr (const char *s, int c)
#else
mbschr (s, c)
     const char *s;
     int c;
#endif
{
#if HANDLE_MULTIBYTE
  char *pos;
  mbstate_t state;
  size_t strlength, mblength;

  /* The locale encodings with said weird property are BIG5, BIG5-HKSCS,
     GBK, GB18030, SHIFT_JIS, and JOHAB.  They exhibit the problem only
     when c >= 0x30.  We can therefore use the faster bytewise search if
     c <= 0x30. */
  if ((unsigned char)c >= '0' && MB_CUR_MAX > 1)
    {
      pos = (char *)s;
      memset (&state, '\0', sizeof(mbstate_t));
      strlength = strlen (s);

      while (strlength > 0)
	{
	  mblength = mbrlen (pos, strlength, &state);
	  if (mblength == (size_t)-2 || mblength == (size_t)-1 || mblength == (size_t)0)
	    mblength = 1;

	  if (mblength == 1 && c == (unsigned char)*pos)
	    return pos;

	  strlength -= mblength;
	  pos += mblength;
	}

      return ((char *)NULL);
    }
  else
#endif
  return (strchr (s, c));
}

int
file_exists (fn)
     char *fn;
{
  struct stat sb;

  return (stat (fn, &sb) == 0);
}

static char *
quote_word_break_chars (text)
     char *text;
{
  char *ret, *r, *s;
  int l;

  l = strlen (text);
  ret = (char *)malloc ((2 * l) + 1);
  for (s = text, r = ret; *s; s++)
    {
      /* Pass backslash-quoted characters through, including the backslash. */
      if (*s == '\\')
	{
	  *r++ = '\\';
	  *r++ = *++s;
	  if (*s == '\0')
	    break;
	  continue;
	}
      //OK, we have an unquoted character.  Check its presence in rl_completer_word_break_characters. 

      if (mbschr (rl_completer_word_break_characters, *s)) *r++ = '\\';
      // XXX -- check for standalone tildes here and backslash-quote them
      //if (s == text && *s == '~' && file_exists (text)) *r++ = '\\';
      *r++ = *s;

    }
  *r = '\0';
  return ret;
}

#define COMPLETE_DQUOTE  1
#define COMPLETE_SQUOTE  2
#define COMPLETE_BSQUOTE 3

static int completion_quoting_style = COMPLETE_BSQUOTE;

char *
sh_single_quote (string)
     char *string;
{
  register int c;
  char *result, *r, *s;

  result = (char *)malloc (3 + (4 * strlen (string)));
  r = result;
  *r++ = '\'';

  for (s = string; s && (c = *s); s++)
    {
      *r++ = c;

      if (c == '\'')
	{
	  *r++ = '\\';	/* insert escaped single quote */
	  *r++ = '\'';
	  *r++ = '\'';	/* start new quoted string */
	}
    }

  *r++ = '\'';
  *r = '\0';

  return (result);
}

/* Quote STRING using double quotes.  Return a new string. */
/*
char *
sh_double_quote (string)
     char *string;
{
  register unsigned char c;
  char *result, *r, *s;

  result = (char *)malloc (3 + (2 * strlen (string)));
  r = result;
  *r++ = '"';

  for (s = string; s && (c = *s); s++)
    {
      ///Backslash-newline disappears within double quotes, so don't add one. 
      if ((sh_syntaxtab[c] & CBSDQUOTE) && c != '\n')
	*r++ = '\\';
      else if (c == CTLESC || c == CTLNUL)
	*r++ = CTLESC;   /// could be '\\'? 

      *r++ = c;
    }

  *r++ = '"';
  *r = '\0';

  return (result);
}
*/

/* Turn S into a simple double-quoted string.  If FLAGS is non-zero, quote
   double quote characters in S with backslashes. */
char *
sh_mkdoublequoted (s, slen, flags)
     const char *s;
     int slen, flags;
{
  char *r, *ret;
  int rlen;

  rlen = (flags == 0) ? slen + 3 : (2 * slen) + 1;
  ret = r = (char *)malloc (rlen);
  
  *r++ = '"';
  while (*s)
    {
      if (flags && *s == '"')
	*r++ = '\\';
      *r++ = *s++;
    }
  *r++ = '"';
  *r = '\0';

  return ret;
}

/* Remove backslashes that are quoting characters that are special between
   double quotes.  Return a new string.  XXX - should this handle CTLESC
   and CTLNUL? */
/*
char *
sh_un_double_quote (string)
     char *string;
{
  register int c, pass_next;
  char *result, *r, *s;

  r = result = (char *)malloc (strlen (string) + 1);

  for (pass_next = 0, s = string; s && (c = *s); s++)
    {
      if (pass_next)
	{
	  *r++ = c;
	  pass_next = 0;
	  continue;
	}
      if (c == '\\' && (sh_syntaxtab[(unsigned char) s[1]] & CBSDQUOTE))
	{
	  pass_next = 1;
	  continue;
	}
      *r++ = c;
    }

  *r = '\0';
  return result;
}
*/

/* Quote special characters in STRING using backslashes.  Return a new
   string.  NOTE:  if the string is to be further expanded, we need a
   way to protect the CTLESC and CTLNUL characters.  As I write this,
   the current callers will never cause the string to be expanded without
   going through the shell parser, which will protect the internal
   quoting characters. */
char *
sh_backslash_quote (string)
     char *string;
{
  int c;
  char *result, *r, *s;

  result = (char *)malloc (2 * strlen (string) + 1);

  for (r = result, s = string; s && (c = *s); s++)
    {
      switch (c)
	{
	case ' ': case '\t': case '\n':		/* IFS white space */
	case '\'': case '"': case '\\':		/* quoting chars */
	case '|': case '&': case ';':		/* shell metacharacters */
	case '(': case ')': case '<': case '>':
	case '!': case '{': case '}':		/* reserved words */
	case '*': case '[': case '?': case ']':	/* globbing chars */
	case '^':
	case '$': case '`':			/* expansion chars */
	case ',':				/* brace expansion */
	  *r++ = '\\';
	  *r++ = c;
	  break;
#if 0
	case '~':				/* tilde expansion */
	  if (s == string || s[-1] == '=' || s[-1] == ':')
	    *r++ = '\\';
	  *r++ = c;
	  break;

	case CTLESC: case CTLNUL:		/* internal quoting characters */
	  *r++ = CTLESC;			/* could be '\\'? */
	  *r++ = c;
	  break;
#endif

	case '#':				/* comment char */
	  if (s == string)
	    *r++ = '\\';
	  /* FALLTHROUGH */
	default:
	  *r++ = c;
	  break;
	}
    }

  *r = '\0';
  return (result);
}

/* Filename quoting for completion. */
/* A function to strip quotes that are not protected by backquotes.  It
   allows single quotes to appear within double quotes, and vice versa.
   It should be smarter. */
static char *
bash_dequote_filename (const char *text, int quote_char)
{
  char *ret;
  const char *p;
  char *r;
  int l, quoted;

  l = strlen (text);
  ret = (char*)malloc (l + 1);
  for (quoted = quote_char, p = text, r = ret; p && *p; p++)
    {
      /* Allow backslash-quoted characters to pass through unscathed. */
      if (*p == '\\')
	{
	  *r++ = *++p;
	  if (*p == '\0')
	    break;
	  continue;
	}
      /* Close quote. */
      if (quoted && *p == quoted)
        {
          quoted = 0;
          continue;
        }
      /* Open quote. */
      if (quoted == 0 && (*p == '\'' || *p == '"'))
        {
          quoted = *p;
          continue;
        }
      *r++ = *p;
    }
  *r = '\0';
  return ret;
}




static int skip_quoted(const char *s, int i, char q)
{
   while(s[i] && s[i]!=q)
   {
      if(s[i]=='\\' && s[i+1])
	 i++;
      i++;
   }
   if(s[i])
      i++;
   return i;
}


int lftp_char_is_quoted(const char *string,int eindex)
{
  int i, pass_next;

  for (i = pass_next = 0; i <= eindex; i++)
    {
      if (pass_next)
        {
          pass_next = 0;
          if (i >= eindex)
            return 1;
          continue;
        }
      else if (string[i] == '"' || string[i] == '\'')
        {
	  char quote = string[i];
          i = skip_quoted (string, ++i, quote);
          if (i > eindex)
            return 1;
          i--;  /* the skip functions increment past the closing quote. */
        }
      else if (string[i] == '\\')
        {
          pass_next = 1;
          continue;
        }
    }
  return (0);
}










/* Quote STRING using double quotes.  Return a new string. */
static char *
double_quote (char *string)
{
  register int c;
  char *result, *r, *s;

  result = (char *)malloc (3 + (2 * strlen (string)));
  r = result;
  *r++ = '"';

  for (s = string; s && (c = *s); s++)
    {
      switch (c)
        {
	case '$':
	case '`':
	  if(!shell_cmd)
	     goto def;
	case '"':
	case '\\':
	  *r++ = '\\';
	default: def:
	  *r++ = c;
	  break;
        }
    }

  *r++ = '"';
  *r = '\0';

  return (result);
}


/* Return a new string which is the single-quoted version of STRING.
   Used by alias and trap, among others. */
static char *
single_quote (char *string)
{
  register int c;
  char *result, *r, *s;

  result = (char *)malloc (3 + (4 * strlen (string)));
  r = result;
  *r++ = '\'';

  for (s = string; s && (c = *s); s++)
    {
      *r++ = c;

      if (c == '\'')
	{
	  *r++ = '\\';	/* insert escaped single quote */
	  *r++ = '\'';
	  *r++ = '\'';	/* start new quoted string */
	}
    }

  *r++ = '\'';
  *r = '\0';

  return (result);
}

/* Quote special characters in STRING using backslashes.  Return a new
   string. */
static char *
backslash_quote (char *string)
{
  int c;
  char *result, *r, *s;

  result = (char*)malloc (2 * strlen (string) + 1);

  for (r = result, s = string; s && (c = *s); s++)
    {
      switch (c)
	{
 	case '(': case ')':
 	case '{': case '}':			/* reserved words */
 	case '^':
 	case '$': case '`':			/* expansion chars */
	  if(!shell_cmd)
	    goto def;
 	case '*': case '[': case '?': case ']':	/* globbing chars */
	  if(!shell_cmd && !quote_glob)
	    goto def;
	case ' ': case '\t': case '\n':		/* IFS white space */
	case '"': case '\'': case '\\':		/* quoting chars */
	case '|': case '&': case ';':		/* shell metacharacters */
	case '<': case '>': case '!':
	  *r++ = '\\';
	  *r++ = c;
	  break;
/*
	case '~':				// tilde expansion 
	  if (s == string && inhibit_tilde)
	    *r++ = '.', *r++ = '/';
	  goto def;
*/
	case '#':				/* comment char */
	  if(!shell_cmd)
	    goto def;
	  if (s == string)
	    *r++ = '\\';
	  /* FALLTHROUGH */
	default: def:
	  *r++ = c;
	  break;
	}
    }

  *r = '\0';
  return (result);
}

/* Quote a filename using double quotes, single quotes, or backslashes
   depending on the value of completion_quoting_style.  If we're
   completing using backslashes, we need to quote some additional
   characters (those that readline treats as word breaks), so we call
   quote_word_break_chars on the result. */
static char *
bash_quote_filename (char *s, int rtype, char *qcp)
{
  char *rtext, *mtext, *ret;
  int rlen, cs;

  rtext = (char *)NULL;

  /* If RTYPE == MULT_MATCH, it means that there is
     more than one match.  In this case, we do not add
     the closing quote or attempt to perform tilde
     expansion.  If RTYPE == SINGLE_MATCH, we try
     to perform tilde expansion, because single and double
     quotes inhibit tilde expansion by the shell. */

  mtext = s;
#if 0
  if (mtext[0] == '~' && rtype == SINGLE_MATCH)
    mtext = bash_tilde_expand (s);
#endif

  cs = completion_quoting_style;
  /* Might need to modify the default completion style based on *qcp,
     since it's set to any user-provided opening quote. */
  if (*qcp == '"')
    cs = COMPLETE_DQUOTE;
  else if (*qcp == '\'')
    cs = COMPLETE_SQUOTE;
#if defined (BANG_HISTORY)
  else if (*qcp == '\0' && history_expansion && cs == COMPLETE_DQUOTE &&
	   history_expansion_inhibited == 0 && strchr (mtext, '!'))
    cs = COMPLETE_BSQUOTE;

  if (*qcp == '"' && history_expansion && cs == COMPLETE_DQUOTE &&
        history_expansion_inhibited == 0 && strchr (mtext, '!'))
    {
      cs = COMPLETE_BSQUOTE;
      *qcp = '\0';
    }
#endif

  switch (cs)
    {
    case COMPLETE_DQUOTE:
      rtext = double_quote (mtext);
      break;
    case COMPLETE_SQUOTE:
      rtext = single_quote (mtext);
      break;
    case COMPLETE_BSQUOTE:
      rtext = backslash_quote (mtext);
      break;
    }

  if (mtext != s)
    free (mtext);

  /* We may need to quote additional characters: those that readline treats
     as word breaks that are not quoted by backslash_quote. */
  if (rtext && cs == COMPLETE_BSQUOTE)
    {
      mtext = quote_word_break_chars (rtext);
      free (rtext);
      rtext = mtext;
    }

  /* Leave the opening quote intact.  The readline completion code takes
     care of avoiding doubled opening quotes. */
  rlen = strlen (rtext);
  ret = (char*)malloc (rlen + 1);
  strcpy (ret, rtext);

  /* If there are multiple matches, cut off the closing quote. */
  if (rtype == MULT_MATCH && cs != COMPLETE_BSQUOTE)
    ret[rlen - 1] = '\0';
  free (rtext);
  return ret;
}






// pathの最後は/をつける
static BOOL readline_select_file_read(vector_obj* v, char* path)
{
    DIR* dir = opendir(path);
    if(dir == NULL) {
        return FALSE;
    }
    
    struct dirent* entry;
    while(entry = readdir(dir)) {
        char fpath[PATH_MAX];
        xstrncpy(fpath, path, PATH_MAX);
        xstrncat(fpath, entry->d_name, PATH_MAX);

        struct stat stat_;
        memset(&stat_, 0, sizeof(struct stat));
        if(stat(fpath, &stat_) < 0) {
            continue;
        }

        sFile* file = sFile_new(entry->d_name, &stat_);

        vector_add(v, file);
    }
    
    closedir(dir);

    /// ソート ///
    vector_sort(v, sort_name);

    return TRUE;
}

static void str_cut2(enum eKanjiCode code, char* mbs, int termsize, char* dest_mbs, int dest_byte)
{
    if(code == kUtf8) {
        int i;
        int n;

        wchar_t* wcs 
            = (wchar_t*)MALLOC(sizeof(wchar_t)*(termsize+1)*MB_CUR_MAX);
        wchar_t* tmp 
            = (wchar_t*)MALLOC(sizeof(wchar_t)*(termsize+1)*MB_CUR_MAX);

        if(mbstowcs(wcs, mbs, (termsize+1)*MB_CUR_MAX) == -1) {
            mbstowcs(wcs, "?????", (termsize+1)*MB_CUR_MAX);
        }

        i = 0;
        while(1) {
            if(i < wcslen(wcs)) {
                tmp[i] = wcs[i];
                tmp[i+1] = 0;
            }
            else {
                tmp[i] = ' ';
                tmp[i+1] = 0;
            }

            n = wcswidth(tmp, wcslen(tmp));
            if(n < 0) {
                xstrncpy(dest_mbs, "?????", dest_byte);

                FREE(wcs);
                FREE(tmp);

                return;
            }
            else {
                if(n > termsize) {
                    tmp[i] = 0;

                    if(wcswidth(tmp, wcslen(tmp)) != termsize) {
                        tmp[i] = ' ';
                        tmp[i+1] = 0;
                    }
                    break;
                }
            }

            i++;
        }

        wcstombs(dest_mbs, tmp, dest_byte);

        FREE(wcs);
        FREE(tmp);
    }
    else {
        int n;
        BOOL tmpend = FALSE;
        BOOL kanji = FALSE;
        for(n=0; n<termsize && n<dest_byte-1; n++) {
            if(kanji)
                kanji = FALSE;
            else if(!tmpend && is_kanji(code, mbs[n])) {
                kanji = TRUE;
            }

            if(!tmpend && mbs[n] == 0) tmpend = TRUE;
                        
            if(tmpend)
                dest_mbs[n] = ' ';
            else
                dest_mbs[n] = mbs[n];
        }
        
        if(kanji) dest_mbs[n-1] = ' ';
        dest_mbs[n] = 0;
    }
}


static int readline_select_file(int count, int key)
{
    msave_ttysettings();       // 端末の設定の保存
    msave_screen();
    minitscr();

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

    vector_obj* v = VECTOR_NEW(10);
    char cwd_path[PATH_MAX];
    mygetcwd(cwd_path, PATH_MAX);
    xstrncat(cwd_path, "/", PATH_MAX);

    char current_dir[PATH_MAX];
    xstrncpy(current_dir, cwd_path, PATH_MAX);
    readline_select_file_read(v, current_dir);

    vector_obj* mark_files = VECTOR_NEW(10);

    if(vector_size(v) > 0) {
        int cursor = 0;
        while(1) {
            /// 描写 ///
            mclear();
            mmvprintw(0,0,"%s", current_dir);

            int page = cursor/((maxy-1)*4);
            int n = page*4*(maxy-1);
            while(n < page*4*(maxy-1)+(maxy-1)*4 && n < vector_size(v)) {
                sFile* file = vector_item(v, n);

                int attrs = 0;
                if(n == cursor) {
                    attrs |= kCAReverse;
                }
                if(S_ISDIR(file->mStat.st_mode)) {
                    attrs |= kCACyan;
                    attrs |= kCABold;
                }
                if(attrs) mattron(attrs);

                char file2[PATH_MAX];
                str_cut2(kUtf8, string_c_str(file->mName)
                        , maxx/4-2, file2, PATH_MAX);

                int y = (n-page*4*(maxy-1)) / 4 + 1;
                int x = ((n-page*4*(maxy-1)) % 4)*maxx/4;

                char full_path[PATH_MAX];
                xstrncpy(full_path, current_dir, PATH_MAX);
                xstrncat(full_path, string_c_str(file->mName), PATH_MAX);

                BOOL marked = FALSE;
                int i;
                for(i=0; i<vector_size(mark_files); i++) {
                    string_obj* mark_file = vector_item(mark_files, i);

                    if(strcmp(string_c_str(mark_file), full_path) == 0) {
                        marked = TRUE;
                        break;
                    }
                }

                if(marked) 
                    mmvprintw(y, x, "*%s", file2);
                else
                    mmvprintw(y, x, " %s", file2);

                if(attrs) {
                    mattroff();
                }

                n++;
            }
            mrefresh();

            /// インプット ///
            int meta;
            int key = mgetch(&meta);

            if(key == 14 || key == KEY_DOWN) {
                cursor+=4;
            }
            else if(key == 16 || key == KEY_UP) {
                cursor-=4;
            }
            else if(key == 6 || key == KEY_RIGHT) {
                cursor++;
            }
            else if(key == 2 || key == KEY_LEFT) {
                cursor--;
            }
            else if(key == 4 || key == KEY_NPAGE) {
                cursor += (maxy-1)*4;
            }
            else if(key == 21 || key == KEY_PPAGE) {
                cursor -= (maxy-1)*4;
            }
            else if(key == 12) {
                mclear();
                mrefresh();
            }
            else if(key == ' ') {
                sFile* cursor_file = vector_item(v, cursor);

                string_obj* cursor_mfile = NULL;

                char cursor_path[PATH_MAX];
                xstrncpy(cursor_path, current_dir, PATH_MAX);
                xstrncat(cursor_path, string_c_str(cursor_file->mName), PATH_MAX);

                int i;
                for(i=0; i<vector_size(mark_files); i++) {
                    string_obj* mfile = vector_item(mark_files, i);

                    if(strcmp(string_c_str(mfile), cursor_path) == 0) {
                        cursor_mfile = mfile;
                        break;
                    }
                }

                if(!cursor_mfile) {
                    vector_add(mark_files, STRING_NEW(cursor_path));
                }
                else {
                    vector_erase(mark_files
                        , vector_index(mark_files, cursor_mfile));
                    string_delete(cursor_mfile);
                }

                cursor++;
            }
            else if(key == '\\') {
                xstrncpy(current_dir, "/", PATH_MAX);

                int i;
                for(i=0; i<vector_size(v); i++) {
                    sFile_delete(vector_item(v, i));
                }
                vector_clear(v);

                readline_select_file_read(v, current_dir);
                cursor = 0;
            }
            else if(key == '\t' || key == 'w') {
                if(vector_size(mark_files) == 0) {
                    sFile* cursor_file = vector_item(v, cursor);

                    /// 現在のカレントディレクトリのパスなら
                    /// ディレクトリ名を取る
                    char cursor_path[PATH_MAX];
                    xstrncpy(cursor_path, current_dir, PATH_MAX);
                    xstrncat(cursor_path, string_c_str(cursor_file->mName), PATH_MAX);

                    /// カレントのファイル、子ディレクトリのファイルなら
                    /// 相対ディレクトリ
                    if(memcmp(cursor_path
                            , cwd_path, strlen(cwd_path)) == 0 )
                    {
                        char cursor_path2[PATH_MAX];
                        memcpy(cursor_path2
                            , cursor_path+strlen(cwd_path)
                            , strlen(cursor_path) - strlen(cwd_path)+1);

                        string_obj* quoted_fname = STRING_NEW("");
                        saphire_get_quoted_fname_readline_ver(cursor_path2, quoted_fname);
                        rl_insert_text(string_c_str(quoted_fname));
                        string_delete(quoted_fname);
                    }
                    else {
                        string_obj* quoted_fname = STRING_NEW("");
                        saphire_get_quoted_fname_readline_ver(cursor_path, quoted_fname);
                        rl_insert_text(string_c_str(quoted_fname));
                        string_delete(quoted_fname);
                    }
                }
                else {
                    int j;
                    for(j=0; j<vector_size(mark_files); j++) {
                        char full_path[PATH_MAX];
                        xstrncpy(full_path
                            , string_c_str(vector_item(mark_files, j))
                            , PATH_MAX);

                        if(strstr(full_path, cwd_path) == full_path) {
                            char full_path2[PATH_MAX];
                            memcpy(full_path2
                                , full_path + strlen(cwd_path) 
                                , strlen(full_path) - strlen(cwd_path));
                            full_path2[strlen(full_path) - strlen(cwd_path)] = 0;

                            string_obj* quoted_fname = STRING_NEW("");
                            saphire_get_quoted_fname_readline_ver
                                (full_path2, quoted_fname);
                            rl_insert_text(string_c_str(quoted_fname));
                            string_delete(quoted_fname);

                            if(j<vector_size(mark_files)-1) 
                                rl_insert_text(" ");
                        }
                        else {
                            string_obj* quoted_fname = STRING_NEW("");
                            saphire_get_quoted_fname_readline_ver(full_path, quoted_fname);
                            rl_insert_text(string_c_str(quoted_fname));
                            string_delete(quoted_fname);

                            if(j<vector_size(mark_files)-1) 
                                rl_insert_text(" ");
                        }
                    }
                }
                break;
            }
            else if(key == 'q' || key == 3 | key == 27 || key == 7) {
                break;
            }
            else if(key == 10 || key == 13) {
                sFile* cursor_file = vector_item(v, cursor);

                if(strcmp(string_c_str(cursor_file->mName), ".") != 0
                    && S_ISDIR(cursor_file->mStat.st_mode)) 
                {
                    char path2[PATH_MAX];
                    if(correct_path(current_dir
                            , string_c_str(cursor_file->mName), path2, PATH_MAX)) 
                    {
                        xstrncpy(current_dir, path2, PATH_MAX);
                        if(current_dir[strlen(current_dir)-1] != '/') {
                            xstrncat(current_dir, "/", PATH_MAX);
                        }

                        int i;
                        for(i=0; i<vector_size(v); i++) {
                            sFile_delete(vector_item(v, i));
                        }
                        vector_clear(v);

                        readline_select_file_read(v, current_dir);
                        cursor = 0;
                    }
                }
            }
            else if(key == 8 || key == KEY_BACKSPACE) {    // CTRL-H
                char parent_path[PATH_MAX];
                parentname(parent_path, PATH_MAX, current_dir);
                xstrncpy(current_dir, parent_path, PATH_MAX);

                int i;
                for(i=0; i<vector_size(v); i++) {
                    sFile_delete(vector_item(v, i));
                }
                vector_clear(v);

                readline_select_file_read(v, current_dir);
                cursor = 0;
            }

            /// 修正 ///
            if(cursor < 0) {
                cursor = 0;
            }
            if(cursor >= vector_size(v)) {
                cursor = vector_size(v)-1;
            }
        }
    }

    int i;
    for(i=0; i<vector_size(v); i++) {
        sFile_delete(vector_item(v, i));
    }
    vector_delete(v);

    for(i=0; i<vector_size(mark_files); i++) {
        string_delete(vector_item(mark_files, i));
    }
    vector_delete(mark_files);

//mclear();
//mrefresh();
    mendwin();
    mrestore_screen();
    mrestore_ttysettings();    // 端末の設定の復帰

    return 0;
}







void readline_init()
{
    rl_attempted_completion_function = readline_on_complete;
    rl_completer_quote_characters = "\"'";
    rl_completer_word_break_characters = " \t\n\"'|{}!&;()<>=%/";
    rl_filename_quote_characters = " \\\t\n\"|$*?[]!<>{}&;#()'%";
    rl_completion_append_character= ' ';
    rl_char_is_quoted_p = (rl_linebuf_func_t*)lftp_char_is_quoted;
    rl_filename_quoting_function = bash_quote_filename;
    rl_filename_dequoting_function = (rl_dequote_func_t*)bash_dequote_filename;

#if defined(HAVE_MIGEMO_H)
    migemo_init();
#endif

    gEditingDir = STRING_NEW("");

    rl_bind_key('x'-'a'+1, readline_select_file);
}

void readline_final()
{
    string_delete(gEditingDir);
    int i;
    if(files) {
        int i;
        for(i=0; i<vector_size(files); i++) {
            string_delete(vector_item(files, i));
        }
        vector_delete(files);
    }
#if defined(HAVE_MIGEMO_H)
    migemo_final();
#endif
}
