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