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 /* sparc and other have broken termios support: use old termio handling. */
87 ioctl(fileno(stdin), TCSETA, (void *) &old_term);
90 void clean_up_and_die(int sig)
93 fprintf(stdout, "\n");
97 /* Go to HOME position */
98 void input_home(int outputFd, int *cursor)
100 while (*cursor > 0) {
101 xwrite(outputFd, "\b", 1);
106 /* Go to END position */
107 void input_end(int outputFd, int *cursor, int len)
109 while (*cursor < len) {
110 xwrite(outputFd, "\033[C", 3);
115 /* Delete the char in back of the cursor */
116 void input_backspace(char* command, int outputFd, int *cursor, int *len)
121 xwrite(outputFd, "\b \b", 3);
123 memmove(command + *cursor, command + *cursor + 1,
124 BUFSIZ - *cursor + 1);
126 for (j = *cursor; j < (BUFSIZ - 1); j++) {
130 xwrite(outputFd, (command + j), 1);
133 xwrite(outputFd, " \b", 2);
135 while (j-- > *cursor)
136 xwrite(outputFd, "\b", 1);
142 /* Delete the char in front of the cursor */
143 void input_delete(char* command, int outputFd, int cursor, int *len)
150 memmove(command + cursor, command + cursor + 1,
151 BUFSIZ - cursor - 1);
152 for (j = cursor; j < (BUFSIZ - 1); j++) {
156 xwrite(outputFd, (command + j), 1);
159 xwrite(outputFd, " \b", 2);
162 xwrite(outputFd, "\b", 1);
166 /* Move forward one charactor */
167 void input_forward(int outputFd, int *cursor, int len)
170 xwrite(outputFd, "\033[C", 3);
175 /* Move back one charactor */
176 void input_backward(int outputFd, int *cursor)
179 xwrite(outputFd, "\033[D", 3);
186 #ifdef BB_FEATURE_SH_TAB_COMPLETION
187 char** username_tab_completion(char* command, int *num_matches)
189 char **matches = (char **) NULL;
191 fprintf(stderr, "\nin username_tab_completion\n");
196 char** exe_n_cwd_tab_completion(char* command, int *num_matches)
199 char **matches = (char **) NULL;
203 matches = malloc( sizeof(char*)*50);
205 /* Stick a wildcard onto the command, for later use */
206 strcat( command, "*");
208 /* Now wall the current directory */
209 dirName = get_current_dir_name();
210 dir = opendir(dirName);
212 /* Don't print an error, just shut up and return */
216 while ((next = readdir(dir)) != NULL) {
218 /* Some quick sanity checks */
219 if ((strcmp(next->d_name, "..") == 0)
220 || (strcmp(next->d_name, ".") == 0)) {
223 /* See if this matches */
224 if (check_wildcard_match(next->d_name, command) == TRUE) {
225 /* Cool, found a match. Add it to the list */
226 matches[*num_matches] = malloc(strlen(next->d_name)+1);
227 strcpy( matches[*num_matches], next->d_name);
229 //matches = realloc( matches, sizeof(char*)*(*num_matches));
236 void input_tab(char* command, int outputFd, int *cursor, int *len)
238 /* Do TAB completion */
239 static int num_matches=0;
240 static char **matches = (char **) NULL;
244 if (lastWasTab == FALSE) {
245 char *tmp, *tmp1, *matchBuf;
247 /* For now, we will not bother with trying to distinguish
248 * whether the cursor is in/at a command extression -- we
249 * will always try all possable matches. If you don't like
250 * that then feel free to fix it.
253 /* Make a local copy of the string -- up
254 * to the position of the cursor */
255 matchBuf = (char *) calloc(BUFSIZ, sizeof(char));
256 strncpy(matchBuf, command, cursor);
259 /* skip past any command seperator tokens */
260 while (*tmp && (tmp1=strpbrk(tmp, ";|&{(`")) != NULL) {
262 /* skip any leading white space */
263 while (*tmp && isspace(*tmp))
267 /* skip any leading white space */
268 while (*tmp && isspace(*tmp))
271 /* Free up any memory already allocated */
274 matches = (char **) NULL;
277 /* If the word starts with `~' and there is no slash in the word,
278 * then try completing this word as a username. */
280 /* FIXME -- this check is broken! */
281 if (*tmp == '~' && !strchr(tmp, '/'))
282 matches = username_tab_completion(tmp, &num_matches);
284 /* Try to match any executable in our path and everything
285 * in the current working directory that matches. */
287 matches = exe_n_cwd_tab_completion(tmp, &num_matches);
289 /* Don't leak memory */
292 /* Did we find exactly one match? */
293 if (matches && num_matches==1) {
294 /* write out the matched command */
295 strncpy(command+pos, matches[0]+pos, strlen(matches[0])-pos);
298 xwrite(outputFd, matches[0]+pos, strlen(matches[0])-pos);
302 /* Ok -- the last char was a TAB. Since they
303 * just hit TAB again, print a list of all the
304 * available choices... */
305 if ( matches && num_matches>0 ) {
308 /* Go to the next line */
309 xwrite(outputFd, "\n", 1);
310 /* Print the list of matches */
311 for (i=0,col=0; i<num_matches; i++) {
313 sprintf(foo, "%-14s ", matches[i]);
314 col += xwrite(outputFd, foo, strlen(foo));
315 if (col > 60 && matches[i+1] != NULL) {
316 xwrite(outputFd, "\n", 1);
320 /* Go to the next line */
321 xwrite(outputFd, "\n", 1);
322 /* Rewrite the prompt */
323 xwrite(outputFd, prompt, strlen(prompt));
324 /* Rewrite the command */
325 xwrite(outputFd, command, len);
326 /* Put the cursor back to where it used to be */
327 for (cursor=len; cursor > pos; cursor--)
328 xwrite(outputFd, "\b", 1);
334 void get_previous_history(struct history **hp, char* command)
337 (*hp)->s = strdup(command);
341 void get_next_history(struct history **hp, char* command)
344 (*hp)->s = strdup(command);
349 * This function is used to grab a character buffer
350 * from the input file descriptor and allows you to
351 * a string with full command editing (sortof like
354 * The following standard commands are not implemented:
355 * ESC-b -- Move back one word
356 * ESC-f -- Move forward one word
357 * ESC-d -- Delete back one word
358 * ESC-h -- Delete forward one word
359 * CTL-t -- Transpose two characters
361 * Furthermore, the "vi" command editing keys are not implemented.
363 * TODO: implement TAB command completion. :)
365 extern void cmdedit_read_input(char* prompt, char command[BUFSIZ])
368 int inputFd=fileno(stdin);
369 int outputFd=fileno(stdout);
376 int lastWasTab = FALSE;
378 struct history *hp = his_end;
380 memset(command, 0, sizeof(command));
382 /* sparc and other have broken termios support: use old termio handling. */
383 ioctl(inputFd, TCGETA, (void *) &old_term);
384 memcpy(&new_term, &old_term, sizeof(struct termio));
386 new_term.c_cc[VMIN] = 1;
387 new_term.c_cc[VTIME] = 0;
388 new_term.c_lflag &= ~ICANON; /* unbuffered input */
389 new_term.c_lflag &= ~ECHO;
391 ioctl(inputFd, TCSETA, (void *) &new_term);
393 ioctl(inputFd, TCSETA, (void *) &new_term);
396 memset(command, 0, BUFSIZ);
400 if ((ret = read(inputFd, &c, 1)) < 1)
407 *(command + len++ + 1) = c;
408 xwrite(outputFd, &c, 1);
412 /* Control-a -- Beginning of line */
413 input_home(outputFd, &cursor);
415 /* Control-b -- Move back one character */
416 input_backward(outputFd, &cursor);
419 /* Control-d -- Delete one character, or exit
420 * if the len=0 and no chars to delete */
422 xwrite(outputFd, "exit", 4);
425 input_delete(command, outputFd, cursor, &len);
429 /* Control-e -- End of line */
430 input_end(outputFd, &cursor, len);
433 /* Control-f -- Move forward one character */
434 input_forward(outputFd, &cursor, len);
438 /* control-h and DEL */
439 input_backspace(command, outputFd, &cursor, &len);
442 #ifdef BB_FEATURE_SH_TAB_COMPLETION
443 input_tab(command, outputFd, &cursor, &len);
447 /* Control-n -- Get next command in history */
448 if (hp && hp->n && hp->n->s) {
449 get_next_history(&hp, command);
452 xwrite(outputFd, "\007", 1);
456 /* Control-p -- Get previous command from history */
458 get_previous_history(&hp, command);
461 xwrite(outputFd, "\007", 1);
465 /* escape sequence follows */
466 if ((ret = read(inputFd, &c, 1)) < 1)
469 if (c == '[') { /* 91 */
470 if ((ret = read(inputFd, &c, 1)) < 1)
475 /* Up Arrow -- Get previous command from history */
477 get_previous_history(&hp, command);
480 xwrite(outputFd, "\007", 1);
484 /* Down Arrow -- Get next command in history */
485 if (hp && hp->n && hp->n->s) {
486 get_next_history(&hp, command);
489 xwrite(outputFd, "\007", 1);
493 /* Rewrite the line with the selected history item */
495 /* erase old command from command line */
496 len = strlen(command)-strlen(hp->s);
498 input_backspace(command, outputFd, &cursor, &len);
499 input_home(outputFd, &cursor);
501 /* write new command */
502 strcpy(command, hp->s);
504 xwrite(outputFd, command, len);
508 /* Right Arrow -- Move forward one character */
509 input_forward(outputFd, &cursor, len);
512 /* Left Arrow -- Move back one character */
513 input_backward(outputFd, &cursor);
517 input_delete(command, outputFd, cursor, &len);
521 input_home(outputFd, &cursor);
525 input_end(outputFd, &cursor, len);
528 xwrite(outputFd, "\007", 1);
530 if (c == '1' || c == '3' || c == '4')
531 if ((ret = read(inputFd, &c, 1)) < 1)
532 return; /* read 126 (~) */
536 if ((ret = read(inputFd, &c, 1)) < 1)
541 input_home(outputFd, &cursor);
545 input_end(outputFd, &cursor, len);
548 xwrite(outputFd, "\007", 1);
555 default: /* If it's regular input, do the normal thing */
557 if (!isprint(c)) { /* Skip non-printable characters */
561 if (len >= (BUFSIZ - 2)) /* Need to leave space for enter */
566 if (cursor == (len - 1)) { /* Append if at the end of the line */
567 *(command + cursor) = c;
568 } else { /* Insert otherwise */
569 memmove(command + cursor + 1, command + cursor,
572 *(command + cursor) = c;
574 for (j = cursor; j < len; j++)
575 xwrite(outputFd, command + j, 1);
576 for (; j > cursor; j--)
577 xwrite(outputFd, "\033[D", 3);
581 xwrite(outputFd, &c, 1);
589 if (break_out) /* Enter is the command terminator, no more input. */
594 /* sparc and other have broken termios support: use old termio handling. */
595 ioctl(inputFd, TCSETA, (void *) &old_term);
599 /* Handle command history log */
602 struct history *h = his_end;
605 /* No previous history */
606 h = his_front = malloc(sizeof(struct history));
607 h->n = malloc(sizeof(struct history));
610 h->s = strdup(command);
617 /* Add a new history command */
618 h->n = malloc(sizeof(struct history));
623 h->s = strdup(command);
626 /* After max history, remove the oldest command */
627 if (history_counter >= MAX_HISTORY) {
629 struct history *p = his_front->n;
644 extern void cmdedit_init(void)
646 atexit(cmdedit_reset_term);
647 signal(SIGINT, clean_up_and_die);
648 signal(SIGQUIT, clean_up_and_die);
649 signal(SIGTERM, clean_up_and_die);
651 #endif /* BB_FEATURE_SH_COMMAND_EDITING */