1*f05cddf9SRui Paulo /* 2*f05cddf9SRui Paulo * Command line editing and history wrapper for readline 3*f05cddf9SRui Paulo * Copyright (c) 2010, Jouni Malinen <j@w1.fi> 4*f05cddf9SRui Paulo * 5*f05cddf9SRui Paulo * This software may be distributed under the terms of the BSD license. 6*f05cddf9SRui Paulo * See README for more details. 7*f05cddf9SRui Paulo */ 8*f05cddf9SRui Paulo 9*f05cddf9SRui Paulo #include "includes.h" 10*f05cddf9SRui Paulo #include <readline/readline.h> 11*f05cddf9SRui Paulo #include <readline/history.h> 12*f05cddf9SRui Paulo 13*f05cddf9SRui Paulo #include "common.h" 14*f05cddf9SRui Paulo #include "eloop.h" 15*f05cddf9SRui Paulo #include "edit.h" 16*f05cddf9SRui Paulo 17*f05cddf9SRui Paulo 18*f05cddf9SRui Paulo static void *edit_cb_ctx; 19*f05cddf9SRui Paulo static void (*edit_cmd_cb)(void *ctx, char *cmd); 20*f05cddf9SRui Paulo static void (*edit_eof_cb)(void *ctx); 21*f05cddf9SRui Paulo static char ** (*edit_completion_cb)(void *ctx, const char *cmd, int pos) = 22*f05cddf9SRui Paulo NULL; 23*f05cddf9SRui Paulo 24*f05cddf9SRui Paulo static char **pending_completions = NULL; 25*f05cddf9SRui Paulo 26*f05cddf9SRui Paulo 27*f05cddf9SRui Paulo static void readline_free_completions(void) 28*f05cddf9SRui Paulo { 29*f05cddf9SRui Paulo int i; 30*f05cddf9SRui Paulo if (pending_completions == NULL) 31*f05cddf9SRui Paulo return; 32*f05cddf9SRui Paulo for (i = 0; pending_completions[i]; i++) 33*f05cddf9SRui Paulo os_free(pending_completions[i]); 34*f05cddf9SRui Paulo os_free(pending_completions); 35*f05cddf9SRui Paulo pending_completions = NULL; 36*f05cddf9SRui Paulo } 37*f05cddf9SRui Paulo 38*f05cddf9SRui Paulo 39*f05cddf9SRui Paulo static char * readline_completion_func(const char *text, int state) 40*f05cddf9SRui Paulo { 41*f05cddf9SRui Paulo static int pos = 0; 42*f05cddf9SRui Paulo static size_t len = 0; 43*f05cddf9SRui Paulo 44*f05cddf9SRui Paulo if (pending_completions == NULL) { 45*f05cddf9SRui Paulo rl_attempted_completion_over = 1; 46*f05cddf9SRui Paulo return NULL; 47*f05cddf9SRui Paulo } 48*f05cddf9SRui Paulo 49*f05cddf9SRui Paulo if (state == 0) { 50*f05cddf9SRui Paulo pos = 0; 51*f05cddf9SRui Paulo len = os_strlen(text); 52*f05cddf9SRui Paulo } 53*f05cddf9SRui Paulo for (; pending_completions[pos]; pos++) { 54*f05cddf9SRui Paulo if (strncmp(pending_completions[pos], text, len) == 0) 55*f05cddf9SRui Paulo return strdup(pending_completions[pos++]); 56*f05cddf9SRui Paulo } 57*f05cddf9SRui Paulo 58*f05cddf9SRui Paulo rl_attempted_completion_over = 1; 59*f05cddf9SRui Paulo return NULL; 60*f05cddf9SRui Paulo } 61*f05cddf9SRui Paulo 62*f05cddf9SRui Paulo 63*f05cddf9SRui Paulo static char ** readline_completion(const char *text, int start, int end) 64*f05cddf9SRui Paulo { 65*f05cddf9SRui Paulo readline_free_completions(); 66*f05cddf9SRui Paulo if (edit_completion_cb) 67*f05cddf9SRui Paulo pending_completions = edit_completion_cb(edit_cb_ctx, 68*f05cddf9SRui Paulo rl_line_buffer, end); 69*f05cddf9SRui Paulo return rl_completion_matches(text, readline_completion_func); 70*f05cddf9SRui Paulo } 71*f05cddf9SRui Paulo 72*f05cddf9SRui Paulo 73*f05cddf9SRui Paulo static void edit_read_char(int sock, void *eloop_ctx, void *sock_ctx) 74*f05cddf9SRui Paulo { 75*f05cddf9SRui Paulo rl_callback_read_char(); 76*f05cddf9SRui Paulo } 77*f05cddf9SRui Paulo 78*f05cddf9SRui Paulo 79*f05cddf9SRui Paulo static void trunc_nl(char *str) 80*f05cddf9SRui Paulo { 81*f05cddf9SRui Paulo char *pos = str; 82*f05cddf9SRui Paulo while (*pos != '\0') { 83*f05cddf9SRui Paulo if (*pos == '\n') { 84*f05cddf9SRui Paulo *pos = '\0'; 85*f05cddf9SRui Paulo break; 86*f05cddf9SRui Paulo } 87*f05cddf9SRui Paulo pos++; 88*f05cddf9SRui Paulo } 89*f05cddf9SRui Paulo } 90*f05cddf9SRui Paulo 91*f05cddf9SRui Paulo 92*f05cddf9SRui Paulo static void readline_cmd_handler(char *cmd) 93*f05cddf9SRui Paulo { 94*f05cddf9SRui Paulo if (cmd && *cmd) { 95*f05cddf9SRui Paulo HIST_ENTRY *h; 96*f05cddf9SRui Paulo while (next_history()) 97*f05cddf9SRui Paulo ; 98*f05cddf9SRui Paulo h = previous_history(); 99*f05cddf9SRui Paulo if (h == NULL || os_strcmp(cmd, h->line) != 0) 100*f05cddf9SRui Paulo add_history(cmd); 101*f05cddf9SRui Paulo next_history(); 102*f05cddf9SRui Paulo } 103*f05cddf9SRui Paulo if (cmd == NULL) { 104*f05cddf9SRui Paulo edit_eof_cb(edit_cb_ctx); 105*f05cddf9SRui Paulo return; 106*f05cddf9SRui Paulo } 107*f05cddf9SRui Paulo trunc_nl(cmd); 108*f05cddf9SRui Paulo edit_cmd_cb(edit_cb_ctx, cmd); 109*f05cddf9SRui Paulo } 110*f05cddf9SRui Paulo 111*f05cddf9SRui Paulo 112*f05cddf9SRui Paulo int edit_init(void (*cmd_cb)(void *ctx, char *cmd), 113*f05cddf9SRui Paulo void (*eof_cb)(void *ctx), 114*f05cddf9SRui Paulo char ** (*completion_cb)(void *ctx, const char *cmd, int pos), 115*f05cddf9SRui Paulo void *ctx, const char *history_file, const char *ps) 116*f05cddf9SRui Paulo { 117*f05cddf9SRui Paulo edit_cb_ctx = ctx; 118*f05cddf9SRui Paulo edit_cmd_cb = cmd_cb; 119*f05cddf9SRui Paulo edit_eof_cb = eof_cb; 120*f05cddf9SRui Paulo edit_completion_cb = completion_cb; 121*f05cddf9SRui Paulo 122*f05cddf9SRui Paulo rl_attempted_completion_function = readline_completion; 123*f05cddf9SRui Paulo if (history_file) { 124*f05cddf9SRui Paulo read_history(history_file); 125*f05cddf9SRui Paulo stifle_history(100); 126*f05cddf9SRui Paulo } 127*f05cddf9SRui Paulo 128*f05cddf9SRui Paulo eloop_register_read_sock(STDIN_FILENO, edit_read_char, NULL, NULL); 129*f05cddf9SRui Paulo 130*f05cddf9SRui Paulo if (ps) { 131*f05cddf9SRui Paulo size_t blen = os_strlen(ps) + 3; 132*f05cddf9SRui Paulo char *ps2 = os_malloc(blen); 133*f05cddf9SRui Paulo if (ps2) { 134*f05cddf9SRui Paulo os_snprintf(ps2, blen, "%s> ", ps); 135*f05cddf9SRui Paulo rl_callback_handler_install(ps2, readline_cmd_handler); 136*f05cddf9SRui Paulo os_free(ps2); 137*f05cddf9SRui Paulo return 0; 138*f05cddf9SRui Paulo } 139*f05cddf9SRui Paulo } 140*f05cddf9SRui Paulo 141*f05cddf9SRui Paulo rl_callback_handler_install("> ", readline_cmd_handler); 142*f05cddf9SRui Paulo 143*f05cddf9SRui Paulo return 0; 144*f05cddf9SRui Paulo } 145*f05cddf9SRui Paulo 146*f05cddf9SRui Paulo 147*f05cddf9SRui Paulo void edit_deinit(const char *history_file, 148*f05cddf9SRui Paulo int (*filter_cb)(void *ctx, const char *cmd)) 149*f05cddf9SRui Paulo { 150*f05cddf9SRui Paulo rl_set_prompt(""); 151*f05cddf9SRui Paulo rl_replace_line("", 0); 152*f05cddf9SRui Paulo rl_redisplay(); 153*f05cddf9SRui Paulo rl_callback_handler_remove(); 154*f05cddf9SRui Paulo readline_free_completions(); 155*f05cddf9SRui Paulo 156*f05cddf9SRui Paulo eloop_unregister_read_sock(STDIN_FILENO); 157*f05cddf9SRui Paulo 158*f05cddf9SRui Paulo if (history_file) { 159*f05cddf9SRui Paulo /* Save command history, excluding lines that may contain 160*f05cddf9SRui Paulo * passwords. */ 161*f05cddf9SRui Paulo HIST_ENTRY *h; 162*f05cddf9SRui Paulo history_set_pos(0); 163*f05cddf9SRui Paulo while ((h = current_history())) { 164*f05cddf9SRui Paulo char *p = h->line; 165*f05cddf9SRui Paulo while (*p == ' ' || *p == '\t') 166*f05cddf9SRui Paulo p++; 167*f05cddf9SRui Paulo if (filter_cb && filter_cb(edit_cb_ctx, p)) { 168*f05cddf9SRui Paulo h = remove_history(where_history()); 169*f05cddf9SRui Paulo if (h) { 170*f05cddf9SRui Paulo os_free(h->line); 171*f05cddf9SRui Paulo free(h->data); 172*f05cddf9SRui Paulo os_free(h); 173*f05cddf9SRui Paulo } else 174*f05cddf9SRui Paulo next_history(); 175*f05cddf9SRui Paulo } else 176*f05cddf9SRui Paulo next_history(); 177*f05cddf9SRui Paulo } 178*f05cddf9SRui Paulo write_history(history_file); 179*f05cddf9SRui Paulo } 180*f05cddf9SRui Paulo } 181*f05cddf9SRui Paulo 182*f05cddf9SRui Paulo 183*f05cddf9SRui Paulo void edit_clear_line(void) 184*f05cddf9SRui Paulo { 185*f05cddf9SRui Paulo } 186*f05cddf9SRui Paulo 187*f05cddf9SRui Paulo 188*f05cddf9SRui Paulo void edit_redraw(void) 189*f05cddf9SRui Paulo { 190*f05cddf9SRui Paulo rl_on_new_line(); 191*f05cddf9SRui Paulo rl_redisplay(); 192*f05cddf9SRui Paulo } 193