예제 #1
0
//------------------------------------------------------------------------------
void add_to_history(const char* line)
{
    int dupe_mode;
    const unsigned char* c;

    // Maybe we shouldn't add this line to the history at all?
    c = (const unsigned char*)line;
    if (isspace(*c) && get_clink_setting_int("history_ignore_space") > 0)
    {
        return;
    }

    // Skip leading whitespace
    while (*c)
    {
        if (!isspace(*c))
        {
            break;
        }

        ++c;
    }

    // Skip empty lines
    if (*c == '\0')
    {
        return;
    }

    // Check if the line's a duplicate of and existing history entry.
    dupe_mode = get_clink_setting_int("history_dupe_mode");
    if (dupe_mode > 0)
    {
        int where = find_duplicate(c);
        if (where >= 0)
        {
            if (dupe_mode > 1)
            {
                remove_history(where);
            }
            else
            {
                return;
            }
        }
    }

    // All's well. Add the line.
    using_history();
    add_history(line);
    ++g_new_history_count;
}
예제 #2
0
//------------------------------------------------------------------------------
void save_history()
{
    int max_history;
    char buffer[512];

    get_history_file_name(buffer, sizeof(buffer));

    // Get max history size.
    max_history = get_clink_setting_int("history_file_lines");
    max_history = (max_history == 0) ? INT_MAX : max_history;
    if (max_history < 0)
    {
        unlink(buffer);
        return;
    }

    // Write new history to the file, and truncate to our maximum.
    if (append_history(g_new_history_count, buffer) != 0)
    {
        write_history(buffer);
    }

    if (max_history != INT_MAX)
    {
        history_truncate_file(buffer, max_history);
    }

    g_new_history_count = 0;
}
예제 #3
0
파일: rl.c 프로젝트: HTshandou/clink
//------------------------------------------------------------------------------
void save_history()
{
    int max_history;
    char buffer[1024];
    const char* c;

    if (get_clink_setting_int("persist_history") == 0)
    {
        return;
    }

    get_history_file_name(buffer, sizeof(buffer));

    // Get max history size.
    c = rl_variable_value("history-size");
    max_history = (c != NULL) ? atoi(c) : 1000;

    // Write new history to the file, and truncate to our maximum.
    if (append_history(g_new_history_count, buffer) != 0)
    {
        write_history(buffer);
    }

    history_truncate_file(buffer, max_history);
}
예제 #4
0
파일: rl.c 프로젝트: HTshandou/clink
//------------------------------------------------------------------------------
static void load_history()
{
    char buffer[1024];

    if (get_clink_setting_int("persist_history"))
    {
        get_history_file_name(buffer, sizeof(buffer));
        read_history(buffer);
    }

    using_history();
}
예제 #5
0
//------------------------------------------------------------------------------
int getc_impl(FILE* stream)
{
    int printable;
    int alt;
    int i;
    while (1)
    {
        wchar_t wc[2];
        char utf8[4];

        alt = 0;
        i = GETWCH_IMPL(&alt);

        // MSB is set if value represents a printable character.
        printable = (i & 0x80000000);
        i &= ~printable;

        // Treat esc like cmd.exe does - clear the line.
        if (i == 0x1b)
        {
            if (rl_editing_mode == emacs_mode &&
                get_clink_setting_int("esc_clears_line"))
            {
                using_history();
                rl_delete_text(0, rl_end);
                rl_point = 0;
                rl_redisplay();
                continue;
            }
        }

        // Mask off top bits, they're used to track ALT key state.
        if (i < 0x80 || (i == 0xe0 && !printable))
        {
            break;
        }

        // Convert to utf-8 and insert directly into rl's line buffer.
        wc[0] = (wchar_t)i;
        wc[1] = L'\0';
        WideCharToMultiByte(CP_UTF8, 0, wc, -1, utf8, sizeof(utf8), NULL, NULL);

        rl_insert_text(utf8);
        rl_redisplay();
    }

    alt = RL_ISSTATE(RL_STATE_MOREINPUT) ? 0 : alt;
    alt = alt ? 0x80 : 0;
    return i|alt;
}
예제 #6
0
파일: lua.c 프로젝트: genba/clink
//------------------------------------------------------------------------------
static int get_setting_int(lua_State* state)
{
    int i;
    const char* c;

    if (lua_gettop(state) == 0)
    {
        return 0;
    }

    if (lua_isnil(state, 1) || !lua_isstring(state, 1))
    {
        return 0;
    }

    c = lua_tostring(state, 1);
    i = get_clink_setting_int(c);
    lua_pushinteger(state, i);

    return 1;
}
예제 #7
0
파일: rl.c 프로젝트: HTshandou/clink
//------------------------------------------------------------------------------
static void display_matches(char** matches, int match_count, int longest)
{
    int i;
    char** new_matches;
    CONSOLE_SCREEN_BUFFER_INFO csbi;
    WORD text_attrib;
    HANDLE std_out_handle;
    wchar_t buffer[512];
    int show_matches = 2;
    int match_colour;

    // Process matches and recalculate the longest match length.
    new_matches = match_display_filter(matches, match_count);

    longest = 0;
    for (i = 0; i < (match_count + 1); ++i)
    {
        int len = (int)strlen(new_matches[i]);
        longest = (len > longest) ? len : longest;
    }

    std_out_handle = GetStdHandle(STD_OUTPUT_HANDLE);
    GetConsoleScreenBufferInfo(std_out_handle, &csbi);

    // Get the console's current colour settings
    match_colour = get_clink_setting_int("match_colour");
    if (match_colour == -1)
    {
        // Pick a suitable foreground colour, check fg isn't the same as bg, and set.
        text_attrib = csbi.wAttributes;
        text_attrib ^= 0x08;

        if ((text_attrib & 0xf0) == (text_attrib & 0x0f))
        {
            text_attrib ^= FOREGROUND_INTENSITY;
        }
    }
    else
    {
        text_attrib = csbi.wAttributes & 0xf0;
        text_attrib |= (match_colour & 0x0f);
    }

    SetConsoleTextAttribute(std_out_handle, text_attrib);

    // If there's lots of matches, check with the user before displaying them
    // This matches readline's behaviour, which will get skipped (annoyingly)
    if ((rl_completion_query_items > 0) &&
        (match_count >= rl_completion_query_items))
    {
        DWORD written;

        _snwprintf(
            buffer,
            sizeof_array(buffer),
            L"\nDisplay all %d possibilities? (y or n)",
            match_count
        );
        WriteConsoleW(std_out_handle, buffer, wcslen(buffer), &written, NULL);

        while (show_matches > 1)
        {
            int c = rl_read_key();
            switch (c)
            {
            case 'y':
            case 'Y':
            case ' ':
                show_matches = 1;
                break;

            case 'n':
            case 'N':
            case 0x7f:
                show_matches = 0;
                break;
            }
        }
    }

    // Get readline to display the matches.
    if (show_matches > 0)
    {
        // Turn of '/' suffix for directories. RL assumes '/', which isn't the
        // case, plus clink uses colours instead.
        int j = _rl_complete_mark_directories;
        _rl_complete_mark_directories = 0;

        rl_display_match_list(new_matches, match_count, longest);

        _rl_complete_mark_directories = j;
    }
    else
    {
        rl_crlf();
    }

    // Reset console colour back to normal.
    SetConsoleTextAttribute(std_out_handle, csbi.wAttributes);
    rl_forced_update_display();
    rl_display_fixed = 1;

    // Tidy up.
    for (i = 0; i < match_count; ++i)
    {
        free(new_matches[i]);
    }
    free(new_matches);
}
예제 #8
0
/*
    Taken from msvcrt.dll's getextendedkeycode()

                          ELSE SHFT CTRL ALTS
    00000000`723d36e0  1c 000d 000d 000a a600
    00000000`723d36ea  35 002f 003f 9500 a400
    00000000`723d36f4  47 47e0 47e0 77e0 9700
    00000000`723d36fe  48 48e0 48e0 8de0 9800
    00000000`723d3708  49 49e0 49e0 86e0 9900
    00000000`723d3712  4b 4be0 4be0 73e0 9b00
    00000000`723d371c  4d 4de0 4de0 74e0 9d00
    00000000`723d3726  4f 4fe0 4fe0 75e0 9f00
    00000000`723d3730  50 50e0 50e0 91e0 a000
    00000000`723d373a  51 51e0 51e0 76e0 a100
    00000000`723d3744  52 52e0 52e0 92e0 a200
    00000000`723d374e  53 53e0 53e0 93e0 a300

    home 01 00 00 00 01 00 24 00 47 00 00 00 00 00 00 00
    end  01 00 00 00 01 00 23 00 4f 00 00 00 00 00 00 00
    pgup 01 00 00 00 01 00 21 00 49 00 00 00 00 00 00 00
    pgdn 01 00 00 00 01 00 22 00 51 00 00 00 00 00 00 00
*/
static int getc_internal(int* alt)
{
    static int       carry        = 0; // Multithreading? What's that?
    static const int CTRL_PRESSED = LEFT_CTRL_PRESSED|RIGHT_CTRL_PRESSED;

    int key_char;
    int key_vk;
    int key_sc;
    int key_flags;
    HANDLE handle;
    DWORD mode;

    // Clear all flags so the console doesn't do anything special. This prevents
    // key presses such as Ctrl-C and Ctrl-S from being swallowed.
    handle = GetStdHandle(STD_INPUT_HANDLE);
    GetConsoleMode(handle, &mode);

loop:
    key_char = 0;
    key_vk = 0;
    key_sc = 0;
    key_flags = 0;
    *alt = 0;

    // Read a key or use what was carried across from a previous call.
    if (carry)
    {
        key_flags = ENHANCED_KEY;
        key_char = carry;
        carry = 0;
    }
    else
    {
        HANDLE handle_stdout = GetStdHandle(STD_OUTPUT_HANDLE);
        CONSOLE_SCREEN_BUFFER_INFO csbi;
        DWORD i;
        INPUT_RECORD record;
        const KEY_EVENT_RECORD* key;
        int altgr_sub;

        GetConsoleScreenBufferInfo(handle_stdout, &csbi);

        // Check for a new buffer size for simulated SIGWINCH signals.
        i = (csbi.dwSize.X << 16) | csbi.dwSize.Y;
        if (!g_knownBufferSize || g_knownBufferSize != i)
        {
            simulate_sigwinch(csbi.dwCursorPosition);
            g_knownBufferSize = i;
            goto loop;
        }

        // Fresh read from the console.
        SetConsoleMode(handle, 0);
        ReadConsoleInputW(handle, &record, 1, &i);
        if (record.EventType != KEY_EVENT)
        {
            goto loop;
        }

        key = &record.Event.KeyEvent;
        key_char = key->uChar.UnicodeChar;
        key_vk = key->wVirtualKeyCode;
        key_sc = key->wVirtualScanCode;
        key_flags = key->dwControlKeyState;

#if defined(DEBUG_GETC) && defined(_DEBUG)
        {
            static int id = 0;
            int i;
            printf("\n%03d: %s ", id++, key->bKeyDown ? "+" : "-");
            for (i = 2; i < sizeof(*key) / sizeof(short); ++i)
            {
                printf("%04x ", ((unsigned short*)key)[i]);
            }
        }
#endif

        if (key->bKeyDown == FALSE)
        {
            // Some times conhost can send through ALT codes, with the resulting
            // Unicode code point in the Alt key-up event.
            if (key_vk == VK_MENU && key_char)
            {
                goto end;
            }

            goto loop;
        }

        // Windows supports an AltGr substitute which we check for here. As it
        // collides with Readline mappings Clink's support can be disabled.
        altgr_sub = !!(key_flags & LEFT_ALT_PRESSED);
        altgr_sub &= !!(key_flags & (LEFT_CTRL_PRESSED|RIGHT_CTRL_PRESSED));
        altgr_sub &= !!key_char;
        altgr_sub &= get_clink_setting_int("use_altgr_substitute");

        if (!altgr_sub)
        {
            *alt = !!(key_flags & LEFT_ALT_PRESSED);
        }
    }

    // No Unicode character? Then some post-processing is required to make the
    // output compatible with whatever standard Linux terminals adhere to and
    // that which Readline expects.
    if (key_char == 0)
    {
        int i;

        // The numpad keys such as PgUp, End, etc. don't come through with the
        // ENHANCED_KEY flag set so we'll infer it here.
        static const int enhanced_vks[] = {
            VK_UP, VK_DOWN, VK_LEFT, VK_RIGHT, VK_HOME, VK_END,
            VK_INSERT, VK_DELETE, VK_PRIOR, VK_NEXT,
        };

        for (i = 0; i < sizeof_array(enhanced_vks); ++i)
        {
            if (key_vk == enhanced_vks[i])
            {
                key_flags |= ENHANCED_KEY;
                break;
            }
        }

        // Differentiate enhanced keys depending on modifier key state. MSVC's
        // runtime does something similar. Slightly non-standard.
        if (key_flags & ENHANCED_KEY)
        {
            static const int mod_map[][4] =
            {
                //Nrml  Shft  Ctrl  CtSh
                { 0x47, 0x61, 0x77, 0x21 }, // Gaw! home
                { 0x48, 0x62, 0x54, 0x22 }, // HbT" up
                { 0x49, 0x63, 0x55, 0x23 }, // IcU# pgup
                { 0x4b, 0x64, 0x73, 0x24 }, // Kds$ left
                { 0x4d, 0x65, 0x74, 0x25 }, // Met% right
                { 0x4f, 0x66, 0x75, 0x26 }, // Ofu& end
                { 0x50, 0x67, 0x56, 0x27 }, // PgV' down
                { 0x51, 0x68, 0x76, 0x28 }, // Qhv( pgdn
                { 0x52, 0x69, 0x57, 0x29 }, // RiW) insert
                { 0x53, 0x6a, 0x58, 0x2a }, // SjX* delete
            };

            for (i = 0; i < sizeof_array(mod_map); ++i)
            {
                int j = 0;
                if (mod_map[i][j] != key_sc)
                {
                    continue;
                }

                j += !!(key_flags & SHIFT_PRESSED);
                j += !!(key_flags & CTRL_PRESSED) << 1;
                carry = mod_map[i][j];
                break;
            }

            // Blacklist.
            if (!carry)
            {
                goto loop;
            }

            key_vk = 0xe0;
        }
        else if (!(key_flags & CTRL_PRESSED))
        {
            goto loop;
        }

        // This builds Ctrl-<key> map to match that as described by Readline's
        // source for the emacs/vi keymaps.
        #define CONTAINS(l, r) (unsigned)(key_vk - l) <= (r - l)
        else if (CONTAINS('A', 'Z'))    key_vk -= 'A' - 1;
        else if (CONTAINS(0xdb, 0xdd))  key_vk -= 0xdb - 0x1b;
        else if (key_vk == 0x32)        key_vk = 0;
        else if (key_vk == 0x36)        key_vk = 0x1e;
        else if (key_vk == 0xbd)        key_vk = 0x1f;
        else                            goto loop;
        #undef CONTAINS

        key_char = key_vk;
    }
    else if (!(key_flags & ENHANCED_KEY) && key_char > 0x7f)
    {
        key_char |= 0x8000000;
    }

end:
#if defined(DEBUG_GETC) && defined(_DEBUG)
    printf("\n%08x '%c'", key_char, key_char);
#endif

    SetConsoleMode(handle, mode);
    return key_char;
}
예제 #9
0
파일: rl.c 프로젝트: Tafhim/clink
//------------------------------------------------------------------------------
static char* call_readline_impl(const char* prompt)
{
    static int initialised = 0;
    int expand_result;
    char* text;
    char* expanded;
    char* prepared_prompt;
    char cwd_cache[MAX_PATH];

    // Turn off EOL wrapping as Readline will take care of it.
    {
        HANDLE handle_stdout = GetStdHandle(STD_OUTPUT_HANDLE);
        SetConsoleMode(handle_stdout, ENABLE_PROCESSED_OUTPUT);
    }

    // Initialisation
    if (!initialised)
    {
        initialise_clink_settings();
        initialise_lua();
        initialise_fwrite();

        load_history();
        history_inhibit_expansion_function = history_expand_control;

        rl_catch_signals = 0;
        rl_startup_hook = initialise_hook;
        initialised = 1;
    }

    // If no prompt was provided assume the line is prompted already and
    // extract it. If a prompt was provided filter it through Lua.
    prepared_prompt = NULL;
    if (prompt == NULL)
    {
        prepared_prompt = extract_prompt(1);

        // Even though we're not going to display filtered result the extracted
        // prompt is run through Lua. This is a little bit of a hack, but helps
        // to keep behaviour consistent.
        if (prepared_prompt != NULL)
        {
            char buffer[1024];

            str_cpy(buffer, prepared_prompt, sizeof(buffer));
            lua_filter_prompt(buffer, sizeof_array(buffer));
        }
    }
    else
    {
        prepared_prompt = filter_prompt(prompt);
    }

    GetCurrentDirectory(sizeof_array(cwd_cache), cwd_cache);

    do
    {
        // Call readline
        rl_already_prompted = (prompt == NULL);
        text = readline(prepared_prompt ? prepared_prompt : "");
        if (!text)
        {
            goto call_readline_epilogue;
        }

        // Expand history designators in returned buffer.
        expanded = NULL;
        expand_result = expand_from_history(text, &expanded);
        if (expand_result > 0 && expanded != NULL)
        {
            free(text);
            text = expanded;

            // If there was some expansion then display the expanded result.
            if (expand_result > 0)
            {
                hooked_fprintf(NULL, "History expansion: %s\n", text);
            }
        }

        // Should we read the history from disk.
        if (get_clink_setting_int("history_io"))
        {
            load_history();
            add_to_history(text);
            save_history();
        }
        else
            add_to_history(text);
    }
    while (!text || expand_result == 2);

call_readline_epilogue:
    free_prompt(prepared_prompt);
    SetCurrentDirectory(cwd_cache);
    return text;
}