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
42 #include <sys/ioctl.h>
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 */
57 /* ED: sparc termios is broken: revert back to old termio handling. */
58 #ifdef BB_FEATURE_USE_TERMIOS
62 # define termios termio
63 # define setTermSettings(fd,argp) ioctl(fd,TCSETAF,argp)
64 # define getTermSettings(fd,argp) ioctl(fd,TCGETA,argp)
67 # define setTermSettings(fd,argp) tcsetattr(fd,TCSANOW,argp)
68 # define getTermSettings(fd,argp) tcgetattr(fd, argp);
71 /* Current termio and the previous termio before starting sh */
72 static struct termios initial_settings, new_settings;
75 #ifndef _POSIX_VDISABLE
76 #define _POSIX_VDISABLE '\0'
83 static int cmdedit_termw = 80; /* actual terminal width */
84 static int cmdedit_scroll = 27; /* width of EOL scrolling region */
85 static int history_counter = 0; /* Number of commands in history list */
86 static int reset_term = 0; /* Set to true if the terminal needs to be reset upon exit */
87 static int exithandler_set = 0; /* Set to true when atexit() has been called */
90 /* Link into lash to reset context to 0
92 extern unsigned int shell_context;
104 * TODO: Someday we want to implement 'horizontal scrolling' of the
105 * command-line when the user has typed more than the current width. This
106 * would allow the user to see a 'window' of what he has typed.
108 static void cmdedit_setwidth(int w)
112 cmdedit_scroll = w / 3;
114 error_msg("\n*** Error: minimum screen width is 21\n");
118 static void win_changed(int junk)
120 struct winsize win = { 0, 0, 0, 0 };
121 ioctl(0, TIOCGWINSZ, &win);
122 if (win.ws_col > 0) {
123 cmdedit_setwidth( win.ws_col - 1);
128 static void cmdedit_reset_term(void)
131 /* sparc and other have broken termios support: use old termio handling. */
132 setTermSettings(fileno(stdin), (void*) &initial_settings);
133 #ifdef BB_FEATURE_CLEAN_UP
136 //while(his_front!=his_end) {
137 while(his_front!=his_end) {
147 static void clean_up_and_die(int sig)
149 cmdedit_reset_term();
150 fprintf(stdout, "\n");
155 /* Go to HOME position */
156 static void input_home(int outputFd, int *cursor)
158 while (*cursor > 0) {
159 xwrite(outputFd, "\b", 1);
164 /* Go to END position */
165 static void input_end(int outputFd, int *cursor, int len)
167 while (*cursor < len) {
168 xwrite(outputFd, "\033[C", 3);
173 /* Delete the char in back of the cursor */
174 static void input_backspace(char* command, int outputFd, int *cursor, int *len)
179 //fprintf(stderr, "\nerik: len=%d, cursor=%d, strlen(command)='%d'\n", *len, *cursor, strlen(command));
180 //xwrite(outputFd, command, *len);
185 xwrite(outputFd, "\b \b", 3);
187 memmove(command + *cursor, command + *cursor + 1,
188 BUFSIZ - *cursor + 1);
190 for (j = *cursor; j < (BUFSIZ - 1); j++) {
194 xwrite(outputFd, (command + j), 1);
197 xwrite(outputFd, " \b", 2);
199 while (j-- > *cursor)
200 xwrite(outputFd, "\b", 1);
206 /* Delete the char in front of the cursor */
207 static void input_delete(char* command, int outputFd, int cursor, int *len)
214 memmove(command + cursor, command + cursor + 1,
215 BUFSIZ - cursor - 1);
216 for (j = cursor; j < (BUFSIZ - 1); j++) {
220 xwrite(outputFd, (command + j), 1);
223 xwrite(outputFd, " \b", 2);
226 xwrite(outputFd, "\b", 1);
230 /* Move forward one charactor */
231 static void input_forward(int outputFd, int *cursor, int len)
234 xwrite(outputFd, "\033[C", 3);
239 /* Move back one charactor */
240 static void input_backward(int outputFd, int *cursor)
243 xwrite(outputFd, "\033[D", 3);
250 #ifdef BB_FEATURE_SH_TAB_COMPLETION
251 static char** username_tab_completion(char* command, int *num_matches)
253 char **matches = (char **) NULL;
255 fprintf(stderr, "\nin username_tab_completion\n");
260 static char** exe_n_cwd_tab_completion(char* command, int *num_matches)
267 matches = xmalloc( sizeof(char*)*50);
269 /* Stick a wildcard onto the command, for later use */
270 strcat( command, "*");
272 /* Now wall the current directory */
273 dirName = get_current_dir_name();
274 dir = opendir(dirName);
276 /* Don't print an error, just shut up and return */
280 while ((next = readdir(dir)) != NULL) {
282 /* Some quick sanity checks */
283 if ((strcmp(next->d_name, "..") == 0)
284 || (strcmp(next->d_name, ".") == 0)) {
287 /* See if this matches */
288 if (check_wildcard_match(next->d_name, command) == TRUE) {
289 /* Cool, found a match. Add it to the list */
290 matches[*num_matches] = xmalloc(strlen(next->d_name)+1);
291 strcpy( matches[*num_matches], next->d_name);
293 //matches = realloc( matches, sizeof(char*)*(*num_matches));
300 static void input_tab(char* command, char* prompt, int outputFd, int *cursor, int *len, int lastWasTab)
302 /* Do TAB completion */
303 static int num_matches=0;
304 static char **matches = (char **) NULL;
308 if (lastWasTab == FALSE) {
309 char *tmp, *tmp1, *matchBuf;
311 /* For now, we will not bother with trying to distinguish
312 * whether the cursor is in/at a command extression -- we
313 * will always try all possible matches. If you don't like
314 * that then feel free to fix it.
317 /* Make a local copy of the string -- up
318 * to the position of the cursor */
319 matchBuf = (char *) xcalloc(BUFSIZ, sizeof(char));
320 strncpy(matchBuf, command, *cursor);
323 /* skip past any command seperator tokens */
324 while (*tmp && (tmp1=strpbrk(tmp, ";|&{(`")) != NULL) {
326 /* skip any leading white space */
327 while (*tmp && isspace(*tmp))
331 /* skip any leading white space */
332 while (*tmp && isspace(*tmp))
335 /* Free up any memory already allocated */
338 matches = (char **) NULL;
341 /* If the word starts with `~' and there is no slash in the word,
342 * then try completing this word as a username. */
344 /* FIXME -- this check is broken! */
345 if (*tmp == '~' && !strchr(tmp, '/'))
346 matches = username_tab_completion(tmp, &num_matches);
348 /* Try to match any executable in our path and everything
349 * in the current working directory that matches. */
351 matches = exe_n_cwd_tab_completion(tmp, &num_matches);
353 /* Don't leak memory */
356 /* Did we find exactly one match? */
357 if (matches && num_matches==1) {
358 /* write out the matched command */
359 strncpy(command+pos, matches[0]+pos, strlen(matches[0])-pos);
360 *len=strlen(command);
362 xwrite(outputFd, matches[0]+pos, strlen(matches[0])-pos);
366 /* Ok -- the last char was a TAB. Since they
367 * just hit TAB again, print a list of all the
368 * available choices... */
369 if ( matches && num_matches>0 ) {
372 /* Go to the next line */
373 xwrite(outputFd, "\n", 1);
374 /* Print the list of matches */
375 for (i=0,col=0; i<num_matches; i++) {
377 sprintf(foo, "%-14s ", matches[i]);
378 col += xwrite(outputFd, foo, strlen(foo));
379 if (col > 60 && matches[i+1] != NULL) {
380 xwrite(outputFd, "\n", 1);
384 /* Go to the next line */
385 xwrite(outputFd, "\n", 1);
386 /* Rewrite the prompt */
387 xwrite(outputFd, prompt, strlen(prompt));
388 /* Rewrite the command */
389 xwrite(outputFd, command, *len);
390 /* Put the cursor back to where it used to be */
391 for (cursor=len; *cursor > pos; cursor--)
392 xwrite(outputFd, "\b", 1);
398 static void get_previous_history(struct history **hp, char* command)
402 (*hp)->s = strdup(command);
406 static void get_next_history(struct history **hp, char* command)
410 (*hp)->s = strdup(command);
415 * This function is used to grab a character buffer
416 * from the input file descriptor and allows you to
417 * a string with full command editing (sortof like
420 * The following standard commands are not implemented:
421 * ESC-b -- Move back one word
422 * ESC-f -- Move forward one word
423 * ESC-d -- Delete back one word
424 * ESC-h -- Delete forward one word
425 * CTL-t -- Transpose two characters
427 * Furthermore, the "vi" command editing keys are not implemented.
429 * TODO: implement TAB command completion. :)
431 extern void cmdedit_read_input(char* prompt, char command[BUFSIZ])
434 int inputFd=fileno(stdin);
435 int outputFd=fileno(stdout);
442 int lastWasTab = FALSE;
444 struct history *hp = his_end;
448 getTermSettings(inputFd, (void*) &initial_settings);
449 memcpy(&new_settings, &initial_settings, sizeof(struct termios));
450 new_settings.c_cc[VMIN] = 1;
451 new_settings.c_cc[VTIME] = 0;
452 new_settings.c_cc[VINTR] = _POSIX_VDISABLE; /* Turn off CTRL-C, so we can trap it */
453 new_settings.c_lflag &= ~ICANON; /* unbuffered input */
454 new_settings.c_lflag &= ~(ECHO|ECHOCTL|ECHONL); /* Turn off echoing */
457 setTermSettings(inputFd, (void*) &new_settings);
459 memset(command, 0, BUFSIZ);
461 /* Print out the command prompt */
462 xwrite(outputFd, prompt, strlen(prompt));
466 if ((ret = read(inputFd, &c, 1)) < 1)
468 //fprintf(stderr, "got a '%c' (%d)\n", c, c);
474 *(command + len++ + 1) = c;
475 xwrite(outputFd, &c, 1);
479 /* Control-a -- Beginning of line */
480 input_home(outputFd, &cursor);
482 /* Control-b -- Move back one character */
483 input_backward(outputFd, &cursor);
486 /* Control-c -- stop gathering input */
488 /* Link into lash to reset context to 0 on ^C and such */
491 /* Go to the next line */
492 xwrite(outputFd, "\n", 1);
495 /* Rewrite the prompt */
496 xwrite(outputFd, prompt, strlen(prompt));
498 /* Reset the command string */
499 memset(command, 0, BUFSIZ);
505 /* Control-d -- Delete one character, or exit
506 * if the len=0 and no chars to delete */
508 xwrite(outputFd, "exit", 4);
511 input_delete(command, outputFd, cursor, &len);
515 /* Control-e -- End of line */
516 input_end(outputFd, &cursor, len);
519 /* Control-f -- Move forward one character */
520 input_forward(outputFd, &cursor, len);
524 /* Control-h and DEL */
525 input_backspace(command, outputFd, &cursor, &len);
528 #ifdef BB_FEATURE_SH_TAB_COMPLETION
529 input_tab(command, prompt, outputFd, &cursor,
534 /* Control-n -- Get next command in history */
535 if (hp && hp->n && hp->n->s) {
536 get_next_history(&hp, command);
539 xwrite(outputFd, "\007", 1);
543 /* Control-p -- Get previous command from history */
545 get_previous_history(&hp, command);
548 xwrite(outputFd, "\007", 1);
552 /* escape sequence follows */
553 if ((ret = read(inputFd, &c, 1)) < 1)
556 if (c == '[') { /* 91 */
557 if ((ret = read(inputFd, &c, 1)) < 1)
562 /* Up Arrow -- Get previous command from history */
564 get_previous_history(&hp, command);
567 xwrite(outputFd, "\007", 1);
571 /* Down Arrow -- Get next command in history */
572 if (hp && hp->n && hp->n->s) {
573 get_next_history(&hp, command);
576 xwrite(outputFd, "\007", 1);
580 /* Rewrite the line with the selected history item */
582 /* erase old command from command line */
583 len = strlen(command)-strlen(hp->s);
586 input_delete(command, outputFd, cursor, &len);
588 input_backspace(command, outputFd, &cursor, &len);
589 input_home(outputFd, &cursor);
591 /* write new command */
592 strcpy(command, hp->s);
594 xwrite(outputFd, command, len);
598 /* Right Arrow -- Move forward one character */
599 input_forward(outputFd, &cursor, len);
602 /* Left Arrow -- Move back one character */
603 input_backward(outputFd, &cursor);
607 input_delete(command, outputFd, cursor, &len);
611 input_home(outputFd, &cursor);
615 input_end(outputFd, &cursor, len);
618 xwrite(outputFd, "\007", 1);
620 if (c == '1' || c == '3' || c == '4')
621 if ((ret = read(inputFd, &c, 1)) < 1)
622 return; /* read 126 (~) */
626 if ((ret = read(inputFd, &c, 1)) < 1)
631 input_home(outputFd, &cursor);
635 input_end(outputFd, &cursor, len);
638 xwrite(outputFd, "\007", 1);
645 default: /* If it's regular input, do the normal thing */
647 if (!isprint(c)) { /* Skip non-printable characters */
651 if (len >= (BUFSIZ - 2)) /* Need to leave space for enter */
656 if (cursor == (len - 1)) { /* Append if at the end of the line */
657 *(command + cursor) = c;
658 } else { /* Insert otherwise */
659 memmove(command + cursor + 1, command + cursor,
662 *(command + cursor) = c;
664 for (j = cursor; j < len; j++)
665 xwrite(outputFd, command + j, 1);
666 for (; j > cursor; j--)
667 xwrite(outputFd, "\033[D", 3);
671 xwrite(outputFd, &c, 1);
679 if (break_out) /* Enter is the command terminator, no more input. */
684 setTermSettings(inputFd, (void *) &initial_settings);
688 /* Handle command history log */
691 struct history *h = his_end;
694 /* No previous history -- this memory is never freed */
695 h = his_front = xmalloc(sizeof(struct history));
696 h->n = xmalloc(sizeof(struct history));
699 h->s = strdup(command);
706 /* Add a new history command -- this memory is never freed */
707 h->n = xmalloc(sizeof(struct history));
712 h->s = strdup(command);
715 /* After max history, remove the oldest command */
716 if (history_counter >= MAX_HISTORY) {
718 struct history *p = his_front->n;
733 extern void cmdedit_init(void)
736 signal(SIGWINCH, win_changed);
738 if(exithandler_set == 0) {
739 atexit(cmdedit_reset_term); /* be sure to do this only once */
742 signal(SIGKILL, clean_up_and_die);
743 signal(SIGINT, clean_up_and_die);
744 signal(SIGQUIT, clean_up_and_die);
745 signal(SIGTERM, clean_up_and_die);
749 ** Undo the effects of cmdedit_init() as good as we can:
750 ** I am not aware of a way to revoke an atexit() handler,
751 ** but, fortunately, our particular handler can be made
752 ** a no-op by setting reset_term = 0.
754 extern void cmdedit_terminate(void)
756 cmdedit_reset_term();
758 signal(SIGKILL, SIG_DFL);
759 signal(SIGINT, SIG_DFL);
760 signal(SIGQUIT, SIG_DFL);
761 signal(SIGTERM, SIG_DFL);
762 signal(SIGWINCH, SIG_DFL);
767 #endif /* BB_FEATURE_SH_COMMAND_EDITING */