1 /* vi: set sw=4 ts=4: */
3 * Termios command line History and Editting, originally
4 * intended for NetBSD sh (ash)
6 * Main code: Adam Rogoyski <rogoyski@cs.utexas.edu>
7 * Etc: Dave Cinege <dcinege@psychosis.com>
8 * Majorly adjusted/re-written for busybox:
9 * Erik Andersen <andersee@debian.org>
11 * You may use this code as you wish, so long as the original author(s)
12 * are attributed in any redistributions of the source code.
13 * This code is 'as is' with no warranty.
14 * This code may safely be consumed by a BSD or GPL license.
16 * v 0.5 19990328 Initial release
18 * Future plans: Simple file and path name completion. (like BASH)
24 Terminal key codes are not extensive, and more will probably
25 need to be added. This version was created on Debian GNU/Linux 2.x.
26 Delete, Backspace, Home, End, and the arrow keys were tested
27 to work in an Xterm and console. Ctrl-A also works as Home.
28 Ctrl-E also works as End. The binary size increase is <3K.
30 Editting will not display correctly for lines greater then the
31 terminal width. (more then one line.) However, history will.
35 #ifdef BB_FEATURE_SH_COMMAND_EDITING
47 #define MAX_HISTORY 15 /* Maximum length of the linked list for the command line history */
51 #define member(c, s) ((c) ? ((char *)strchr ((s), (c)) != (char *)NULL) : 0)
52 #define whitespace(c) (((c) == ' ') || ((c) == '\t'))
54 static struct history *his_front = NULL; /* First element in command line list */
55 static struct history *his_end = NULL; /* Last element in command line list */
56 static struct termio old_term, new_term; /* Current termio and the previous termio before starting ash */
58 static int cmdedit_termw = 80; /* actual terminal width */
59 static int cmdedit_scroll = 27; /* width of EOL scrolling region */
60 static int history_counter = 0; /* Number of commands in history list */
61 static int reset_term = 0; /* Set to true if the terminal needs to be reset upon exit */
72 cmdedit_setwidth(int w)
76 cmdedit_scroll = w / 3;
78 errorMsg("\n*** Error: minimum screen width is 21\n");
83 void cmdedit_reset_term(void)
86 ioctl(fileno(stdin), TCSETA, (void *) &old_term);
89 void clean_up_and_die(int sig)
92 fprintf(stdout, "\n");
96 /* Go to HOME position */
97 void input_home(int outputFd, int *cursor)
100 xwrite(outputFd, "\b", 1);
105 /* Go to END position */
106 void input_end(int outputFd, int *cursor, int len)
108 while (*cursor < len) {
109 xwrite(outputFd, "\033[C", 3);
114 /* Delete the char in back of the cursor */
115 void input_backspace(char* command, int outputFd, int *cursor, int *len)
120 xwrite(outputFd, "\b \b", 3);
122 memmove(command + *cursor, command + *cursor + 1,
123 BUFSIZ - *cursor + 1);
125 for (j = *cursor; j < (BUFSIZ - 1); j++) {
129 xwrite(outputFd, (command + j), 1);
132 xwrite(outputFd, " \b", 2);
134 while (j-- > *cursor)
135 xwrite(outputFd, "\b", 1);
141 /* Delete the char in front of the cursor */
142 void input_delete(char* command, int outputFd, int cursor, int *len)
149 memmove(command + cursor, command + cursor + 1,
150 BUFSIZ - cursor - 1);
151 for (j = cursor; j < (BUFSIZ - 1); j++) {
155 xwrite(outputFd, (command + j), 1);
158 xwrite(outputFd, " \b", 2);
161 xwrite(outputFd, "\b", 1);
165 /* Move forward one charactor */
166 void input_forward(int outputFd, int *cursor, int len)
169 xwrite(outputFd, "\033[C", 3);
174 /* Move back one charactor */
175 void input_backward(int outputFd, int *cursor)
178 xwrite(outputFd, "\033[D", 3);
185 #ifdef BB_FEATURE_SH_TAB_COMPLETION
186 char** username_tab_completion(char* command, int *num_matches)
188 char **matches = (char **) NULL;
190 fprintf(stderr, "\nin username_tab_completion\n");
195 char** exe_n_cwd_tab_completion(char* command, int *num_matches)
198 char **matches = (char **) NULL;
202 matches = malloc( sizeof(char*)*50);
204 /* Stick a wildcard onto the command, for later use */
205 strcat( command, "*");
207 /* Now wall the current directory */
208 dirName = get_current_dir_name();
209 dir = opendir(dirName);
211 /* Don't print an error, just shut up and return */
215 while ((next = readdir(dir)) != NULL) {
217 /* Some quick sanity checks */
218 if ((strcmp(next->d_name, "..") == 0)
219 || (strcmp(next->d_name, ".") == 0)) {
222 /* See if this matches */
223 if (check_wildcard_match(next->d_name, command) == TRUE) {
224 /* Cool, found a match. Add it to the list */
225 matches[*num_matches] = malloc(strlen(next->d_name)+1);
226 strcpy( matches[*num_matches], next->d_name);
228 //matches = realloc( matches, sizeof(char*)*(*num_matches));
235 void input_tab(char* command, int outputFd, int *cursor, int *len)
237 /* Do TAB completion */
238 static int num_matches=0;
239 static char **matches = (char **) NULL;
243 if (lastWasTab == FALSE) {
244 char *tmp, *tmp1, *matchBuf;
246 /* For now, we will not bother with trying to distinguish
247 * whether the cursor is in/at a command extression -- we
248 * will always try all possable matches. If you don't like
249 * that then feel free to fix it.
252 /* Make a local copy of the string -- up
253 * to the position of the cursor */
254 matchBuf = (char *) calloc(BUFSIZ, sizeof(char));
255 strncpy(matchBuf, command, cursor);
258 /* skip past any command seperator tokens */
259 while (*tmp && (tmp1=strpbrk(tmp, ";|&{(`")) != NULL) {
261 /* skip any leading white space */
262 while (*tmp && isspace(*tmp))
266 /* skip any leading white space */
267 while (*tmp && isspace(*tmp))
270 /* Free up any memory already allocated */
273 matches = (char **) NULL;
276 /* If the word starts with `~' and there is no slash in the word,
277 * then try completing this word as a username. */
279 /* FIXME -- this check is broken! */
280 if (*tmp == '~' && !strchr(tmp, '/'))
281 matches = username_tab_completion(tmp, &num_matches);
283 /* Try to match any executable in our path and everything
284 * in the current working directory that matches. */
286 matches = exe_n_cwd_tab_completion(tmp, &num_matches);
288 /* Don't leak memory */
291 /* Did we find exactly one match? */
292 if (matches && num_matches==1) {
293 /* write out the matched command */
294 strncpy(command+pos, matches[0]+pos, strlen(matches[0])-pos);
297 xwrite(outputFd, matches[0]+pos, strlen(matches[0])-pos);
301 /* Ok -- the last char was a TAB. Since they
302 * just hit TAB again, print a list of all the
303 * available choices... */
304 if ( matches && num_matches>0 ) {
307 /* Go to the next line */
308 xwrite(outputFd, "\n", 1);
309 /* Print the list of matches */
310 for (i=0,col=0; i<num_matches; i++) {
312 sprintf(foo, "%-14s ", matches[i]);
313 col += xwrite(outputFd, foo, strlen(foo));
314 if (col > 60 && matches[i+1] != NULL) {
315 xwrite(outputFd, "\n", 1);
319 /* Go to the next line */
320 xwrite(outputFd, "\n", 1);
321 /* Rewrite the prompt */
322 xwrite(outputFd, prompt, strlen(prompt));
323 /* Rewrite the command */
324 xwrite(outputFd, command, len);
325 /* Put the cursor back to where it used to be */
326 for (cursor=len; cursor > pos; cursor--)
327 xwrite(outputFd, "\b", 1);
333 void get_previous_history(struct history **hp, char* command)
336 (*hp)->s = strdup(command);
340 void get_next_history(struct history **hp, char* command)
343 (*hp)->s = strdup(command);
348 * This function is used to grab a character buffer
349 * from the input file descriptor and allows you to
350 * a string with full command editing (sortof like
353 * The following standard commands are not implemented:
354 * ESC-b -- Move back one word
355 * ESC-f -- Move forward one word
356 * ESC-d -- Delete back one word
357 * ESC-h -- Delete forward one word
358 * CTL-t -- Transpose two characters
360 * Furthermore, the "vi" command editing keys are not implemented.
362 * TODO: implement TAB command completion. :)
364 extern void cmdedit_read_input(char* prompt, char command[BUFSIZ])
367 int inputFd=fileno(stdin);
368 int outputFd=fileno(stdout);
375 int lastWasTab = FALSE;
377 struct history *hp = his_end;
379 memset(command, 0, sizeof(command));
381 ioctl(inputFd, TCGETA, (void *) &old_term);
382 memcpy(&new_term, &old_term, sizeof(struct termio));
384 new_term.c_cc[VMIN] = 1;
385 new_term.c_cc[VTIME] = 0;
386 new_term.c_lflag &= ~ICANON; /* unbuffered input */
387 new_term.c_lflag &= ~ECHO;
389 ioctl(inputFd, TCSETA, (void *) &new_term);
391 ioctl(inputFd, TCSETA, (void *) &new_term);
394 memset(command, 0, BUFSIZ);
398 if ((ret = read(inputFd, &c, 1)) < 1)
405 *(command + len++ + 1) = c;
406 xwrite(outputFd, &c, 1);
410 /* Control-a -- Beginning of line */
411 input_home(outputFd, &cursor);
413 /* Control-b -- Move back one character */
414 input_backward(outputFd, &cursor);
417 /* Control-d -- Delete one character, or exit
418 * if the len=0 and no chars to delete */
420 xwrite(outputFd, "exit", 4);
423 input_delete(command, outputFd, cursor, &len);
427 /* Control-e -- End of line */
428 input_end(outputFd, &cursor, len);
431 /* Control-f -- Move forward one character */
432 input_forward(outputFd, &cursor, len);
436 /* control-h and DEL */
437 input_backspace(command, outputFd, &cursor, &len);
440 #ifdef BB_FEATURE_SH_TAB_COMPLETION
441 input_tab(command, outputFd, &cursor, &len);
445 /* Control-n -- Get next command in history */
446 if (hp && hp->n && hp->n->s) {
447 get_next_history(&hp, command);
450 xwrite(outputFd, "\007", 1);
454 /* Control-p -- Get previous command from history */
456 get_previous_history(&hp, command);
459 xwrite(outputFd, "\007", 1);
463 /* escape sequence follows */
464 if ((ret = read(inputFd, &c, 1)) < 1)
467 if (c == '[') { /* 91 */
468 if ((ret = read(inputFd, &c, 1)) < 1)
473 /* Up Arrow -- Get previous command from history */
475 get_previous_history(&hp, command);
478 xwrite(outputFd, "\007", 1);
482 /* Down Arrow -- Get next command in history */
483 if (hp && hp->n && hp->n->s) {
484 get_next_history(&hp, command);
487 xwrite(outputFd, "\007", 1);
491 /* Rewrite the line with the selected history item */
493 /* erase old command from command line */
494 len = strlen(command)-strlen(hp->s);
496 input_backspace(command, outputFd, &cursor, &len);
497 input_home(outputFd, &cursor);
499 /* write new command */
500 strcpy(command, hp->s);
502 xwrite(outputFd, command, len);
506 /* Right Arrow -- Move forward one character */
507 input_forward(outputFd, &cursor, len);
510 /* Left Arrow -- Move back one character */
511 input_backward(outputFd, &cursor);
515 input_delete(command, outputFd, cursor, &len);
519 input_home(outputFd, &cursor);
523 input_end(outputFd, &cursor, len);
526 xwrite(outputFd, "\007", 1);
528 if (c == '1' || c == '3' || c == '4')
529 if ((ret = read(inputFd, &c, 1)) < 1)
530 return; /* read 126 (~) */
534 if ((ret = read(inputFd, &c, 1)) < 1)
539 input_home(outputFd, &cursor);
543 input_end(outputFd, &cursor, len);
546 xwrite(outputFd, "\007", 1);
553 default: /* If it's regular input, do the normal thing */
555 if (!isprint(c)) { /* Skip non-printable characters */
559 if (len >= (BUFSIZ - 2)) /* Need to leave space for enter */
564 if (cursor == (len - 1)) { /* Append if at the end of the line */
565 *(command + cursor) = c;
566 } else { /* Insert otherwise */
567 memmove(command + cursor + 1, command + cursor,
570 *(command + cursor) = c;
572 for (j = cursor; j < len; j++)
573 xwrite(outputFd, command + j, 1);
574 for (; j > cursor; j--)
575 xwrite(outputFd, "\033[D", 3);
579 xwrite(outputFd, &c, 1);
587 if (break_out) /* Enter is the command terminator, no more input. */
592 ioctl(inputFd, TCSETA, (void *) &old_term);
596 /* Handle command history log */
599 struct history *h = his_end;
602 /* No previous history */
603 h = his_front = malloc(sizeof(struct history));
604 h->n = malloc(sizeof(struct history));
607 h->s = strdup(command);
614 /* Add a new history command */
615 h->n = malloc(sizeof(struct history));
620 h->s = strdup(command);
623 /* After max history, remove the oldest command */
624 if (history_counter >= MAX_HISTORY) {
626 struct history *p = his_front->n;
641 extern void cmdedit_init(void)
643 atexit(cmdedit_reset_term);
644 signal(SIGINT, clean_up_and_die);
645 signal(SIGQUIT, clean_up_and_die);
646 signal(SIGTERM, clean_up_and_die);
648 #endif /* BB_FEATURE_SH_COMMAND_EDITING */