1252190Srpaulo/* 2252190Srpaulo * Command line editing and history wrapper for readline 3252190Srpaulo * Copyright (c) 2010, Jouni Malinen <j@w1.fi> 4252190Srpaulo * 5252190Srpaulo * This software may be distributed under the terms of the BSD license. 6252190Srpaulo * See README for more details. 7252190Srpaulo */ 8252190Srpaulo 9252190Srpaulo#include "includes.h" 10252190Srpaulo#include <readline/readline.h> 11252190Srpaulo#include <readline/history.h> 12252190Srpaulo 13252190Srpaulo#include "common.h" 14252190Srpaulo#include "eloop.h" 15252190Srpaulo#include "edit.h" 16252190Srpaulo 17252190Srpaulo 18252190Srpaulostatic void *edit_cb_ctx; 19252190Srpaulostatic void (*edit_cmd_cb)(void *ctx, char *cmd); 20252190Srpaulostatic void (*edit_eof_cb)(void *ctx); 21252190Srpaulostatic char ** (*edit_completion_cb)(void *ctx, const char *cmd, int pos) = 22252190Srpaulo NULL; 23252190Srpaulo 24252190Srpaulostatic char **pending_completions = NULL; 25252190Srpaulo 26252190Srpaulo 27252190Srpaulostatic void readline_free_completions(void) 28252190Srpaulo{ 29252190Srpaulo int i; 30252190Srpaulo if (pending_completions == NULL) 31252190Srpaulo return; 32252190Srpaulo for (i = 0; pending_completions[i]; i++) 33252190Srpaulo os_free(pending_completions[i]); 34252190Srpaulo os_free(pending_completions); 35252190Srpaulo pending_completions = NULL; 36252190Srpaulo} 37252190Srpaulo 38252190Srpaulo 39252190Srpaulostatic char * readline_completion_func(const char *text, int state) 40252190Srpaulo{ 41252190Srpaulo static int pos = 0; 42252190Srpaulo static size_t len = 0; 43252190Srpaulo 44252190Srpaulo if (pending_completions == NULL) { 45252190Srpaulo rl_attempted_completion_over = 1; 46252190Srpaulo return NULL; 47252190Srpaulo } 48252190Srpaulo 49252190Srpaulo if (state == 0) { 50252190Srpaulo pos = 0; 51252190Srpaulo len = os_strlen(text); 52252190Srpaulo } 53252190Srpaulo for (; pending_completions[pos]; pos++) { 54252190Srpaulo if (strncmp(pending_completions[pos], text, len) == 0) 55252190Srpaulo return strdup(pending_completions[pos++]); 56252190Srpaulo } 57252190Srpaulo 58252190Srpaulo rl_attempted_completion_over = 1; 59252190Srpaulo return NULL; 60252190Srpaulo} 61252190Srpaulo 62252190Srpaulo 63252190Srpaulostatic char ** readline_completion(const char *text, int start, int end) 64252190Srpaulo{ 65252190Srpaulo readline_free_completions(); 66252190Srpaulo if (edit_completion_cb) 67252190Srpaulo pending_completions = edit_completion_cb(edit_cb_ctx, 68252190Srpaulo rl_line_buffer, end); 69252190Srpaulo return rl_completion_matches(text, readline_completion_func); 70252190Srpaulo} 71252190Srpaulo 72252190Srpaulo 73252190Srpaulostatic void edit_read_char(int sock, void *eloop_ctx, void *sock_ctx) 74252190Srpaulo{ 75252190Srpaulo rl_callback_read_char(); 76252190Srpaulo} 77252190Srpaulo 78252190Srpaulo 79252190Srpaulostatic void trunc_nl(char *str) 80252190Srpaulo{ 81252190Srpaulo char *pos = str; 82252190Srpaulo while (*pos != '\0') { 83252190Srpaulo if (*pos == '\n') { 84252190Srpaulo *pos = '\0'; 85252190Srpaulo break; 86252190Srpaulo } 87252190Srpaulo pos++; 88252190Srpaulo } 89252190Srpaulo} 90252190Srpaulo 91252190Srpaulo 92252190Srpaulostatic void readline_cmd_handler(char *cmd) 93252190Srpaulo{ 94252190Srpaulo if (cmd && *cmd) { 95252190Srpaulo HIST_ENTRY *h; 96252190Srpaulo while (next_history()) 97252190Srpaulo ; 98252190Srpaulo h = previous_history(); 99252190Srpaulo if (h == NULL || os_strcmp(cmd, h->line) != 0) 100252190Srpaulo add_history(cmd); 101252190Srpaulo next_history(); 102252190Srpaulo } 103252190Srpaulo if (cmd == NULL) { 104252190Srpaulo edit_eof_cb(edit_cb_ctx); 105252190Srpaulo return; 106252190Srpaulo } 107252190Srpaulo trunc_nl(cmd); 108252190Srpaulo edit_cmd_cb(edit_cb_ctx, cmd); 109252190Srpaulo} 110252190Srpaulo 111252190Srpaulo 112252190Srpauloint edit_init(void (*cmd_cb)(void *ctx, char *cmd), 113252190Srpaulo void (*eof_cb)(void *ctx), 114252190Srpaulo char ** (*completion_cb)(void *ctx, const char *cmd, int pos), 115252190Srpaulo void *ctx, const char *history_file, const char *ps) 116252190Srpaulo{ 117252190Srpaulo edit_cb_ctx = ctx; 118252190Srpaulo edit_cmd_cb = cmd_cb; 119252190Srpaulo edit_eof_cb = eof_cb; 120252190Srpaulo edit_completion_cb = completion_cb; 121252190Srpaulo 122252190Srpaulo rl_attempted_completion_function = readline_completion; 123252190Srpaulo if (history_file) { 124252190Srpaulo read_history(history_file); 125252190Srpaulo stifle_history(100); 126252190Srpaulo } 127252190Srpaulo 128252190Srpaulo eloop_register_read_sock(STDIN_FILENO, edit_read_char, NULL, NULL); 129252190Srpaulo 130252190Srpaulo if (ps) { 131252190Srpaulo size_t blen = os_strlen(ps) + 3; 132252190Srpaulo char *ps2 = os_malloc(blen); 133252190Srpaulo if (ps2) { 134252190Srpaulo os_snprintf(ps2, blen, "%s> ", ps); 135252190Srpaulo rl_callback_handler_install(ps2, readline_cmd_handler); 136252190Srpaulo os_free(ps2); 137252190Srpaulo return 0; 138252190Srpaulo } 139252190Srpaulo } 140252190Srpaulo 141252190Srpaulo rl_callback_handler_install("> ", readline_cmd_handler); 142252190Srpaulo 143252190Srpaulo return 0; 144252190Srpaulo} 145252190Srpaulo 146252190Srpaulo 147252190Srpaulovoid edit_deinit(const char *history_file, 148252190Srpaulo int (*filter_cb)(void *ctx, const char *cmd)) 149252190Srpaulo{ 150252190Srpaulo rl_set_prompt(""); 151252190Srpaulo rl_replace_line("", 0); 152252190Srpaulo rl_redisplay(); 153252190Srpaulo rl_callback_handler_remove(); 154252190Srpaulo readline_free_completions(); 155252190Srpaulo 156252190Srpaulo eloop_unregister_read_sock(STDIN_FILENO); 157252190Srpaulo 158252190Srpaulo if (history_file) { 159252190Srpaulo /* Save command history, excluding lines that may contain 160252190Srpaulo * passwords. */ 161252190Srpaulo HIST_ENTRY *h; 162252190Srpaulo history_set_pos(0); 163252190Srpaulo while ((h = current_history())) { 164252190Srpaulo char *p = h->line; 165252190Srpaulo while (*p == ' ' || *p == '\t') 166252190Srpaulo p++; 167252190Srpaulo if (filter_cb && filter_cb(edit_cb_ctx, p)) { 168252190Srpaulo h = remove_history(where_history()); 169252190Srpaulo if (h) { 170252190Srpaulo os_free(h->line); 171252190Srpaulo free(h->data); 172252190Srpaulo os_free(h); 173252190Srpaulo } else 174252190Srpaulo next_history(); 175252190Srpaulo } else 176252190Srpaulo next_history(); 177252190Srpaulo } 178252190Srpaulo write_history(history_file); 179252190Srpaulo } 180252190Srpaulo} 181252190Srpaulo 182252190Srpaulo 183252190Srpaulovoid edit_clear_line(void) 184252190Srpaulo{ 185252190Srpaulo} 186252190Srpaulo 187252190Srpaulo 188252190Srpaulovoid edit_redraw(void) 189252190Srpaulo{ 190252190Srpaulo rl_on_new_line(); 191252190Srpaulo rl_redisplay(); 192252190Srpaulo} 193