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