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 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