Home | History | Annotate | Download | only in utils
      1 /*
      2  * Command line editing and history wrapper for readline
      3  * Copyright (c) 2010, Jouni Malinen <j (at) 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 
     18 static void *edit_cb_ctx;
     19 static void (*edit_cmd_cb)(void *ctx, char *cmd);
     20 static void (*edit_eof_cb)(void *ctx);
     21 static char ** (*edit_completion_cb)(void *ctx, const char *cmd, int pos) =
     22 	NULL;
     23 
     24 static char **pending_completions = NULL;
     25 
     26 
     27 static 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 
     39 static 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 
     63 static 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 
     73 static void edit_read_char(int sock, void *eloop_ctx, void *sock_ctx)
     74 {
     75 	rl_callback_read_char();
     76 }
     77 
     78 
     79 static 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 
     92 static 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 
    112 int 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 
    147 void 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 
    183 void edit_clear_line(void)
    184 {
    185 }
    186 
    187 
    188 void edit_redraw(void)
    189 {
    190 	rl_on_new_line();
    191 	rl_redisplay();
    192 }
    193