1 /* vi: set sw=4 ts=4: */
3 * Termios command line History and Editing.
5 * Copyright (c) 1986-2003 may safely be consumed by a BSD or GPL license.
6 * Written by: Vladimir Oleynik <dzo@simtreas.ru>
9 * Adam Rogoyski <rogoyski@cs.utexas.edu>
10 * Dave Cinege <dcinege@psychosis.com>
11 * Jakub Jelinek (c) 1995
12 * Erik Andersen <andersen@codepoet.org> (Majorly adjusted for busybox)
14 * This code is 'as is' with no warranty.
21 Terminal key codes are not extensive, and more will probably
22 need to be added. This version was created on Debian GNU/Linux 2.x.
23 Delete, Backspace, Home, End, and the arrow keys were tested
24 to work in an Xterm and console. Ctrl-A also works as Home.
25 Ctrl-E also works as End.
27 Small bugs (simple effect):
28 - not true viewing if terminal size (x*y symbols) less
29 size (prompt + editor`s line + 2 symbols)
30 - not true viewing if length prompt less terminal width
40 #include <sys/ioctl.h>
48 #ifdef CONFIG_LOCALE_SUPPORT
49 #define Isprint(c) isprint((c))
51 #define Isprint(c) ( (c) >= ' ' && (c) != ((unsigned char)'\233') )
56 /* pretect redefined for test */
57 #undef CONFIG_FEATURE_COMMAND_EDITING
58 #undef CONFIG_FEATURE_COMMAND_TAB_COMPLETION
59 #undef CONFIG_FEATURE_COMMAND_USERNAME_COMPLETION
60 #undef CONFIG_FEATURE_NONPRINTABLE_INVERSE_PUT
61 #undef CONFIG_FEATURE_CLEAN_UP
63 #define CONFIG_FEATURE_COMMAND_EDITING
64 #define CONFIG_FEATURE_COMMAND_TAB_COMPLETION
65 #define CONFIG_FEATURE_COMMAND_USERNAME_COMPLETION
66 #define CONFIG_FEATURE_NONPRINTABLE_INVERSE_PUT
67 #define CONFIG_FEATURE_CLEAN_UP
71 #ifdef CONFIG_FEATURE_COMMAND_TAB_COMPLETION
76 #ifdef CONFIG_FEATURE_COMMAND_EDITING
78 #if defined(CONFIG_FEATURE_COMMAND_USERNAME_COMPLETION) || defined(CONFIG_FEATURE_SH_FANCY_PROMPT)
79 #define CONFIG_FEATURE_GETUSERNAME_AND_HOMEDIR
82 #ifdef CONFIG_FEATURE_GETUSERNAME_AND_HOMEDIR
84 #endif /* advanced FEATURES */
87 /* Maximum length of the linked list for the command line history */
88 #ifndef CONFIG_FEATURE_COMMAND_HISTORY
89 #define MAX_HISTORY 15
91 #define MAX_HISTORY CONFIG_FEATURE_COMMAND_HISTORY
95 #warning cmdedit: You set MAX_HISTORY < 1. The history algorithm switched off.
97 static char *history[MAX_HISTORY+1]; /* history + current */
98 /* saved history lines */
100 /* current pointer to history line */
101 static int cur_history;
105 #define setTermSettings(fd,argp) tcsetattr(fd,TCSANOW,argp)
106 #define getTermSettings(fd,argp) tcgetattr(fd, argp);
108 /* Current termio and the previous termio before starting sh */
109 static struct termios initial_settings, new_settings;
113 volatile int cmdedit_termw = 80; /* actual terminal width */
115 volatile int handlers_sets = 0; /* Set next bites: */
118 SET_ATEXIT = 1, /* when atexit() has been called
119 and get euid,uid,gid to fast compare */
120 SET_WCHG_HANDLERS = 2, /* winchg signal handler */
121 SET_RESET_TERM = 4, /* if the terminal needs to be reset upon exit */
125 static int cmdedit_x; /* real x terminal position */
126 static int cmdedit_y; /* pseudoreal y terminal position */
127 static int cmdedit_prmt_len; /* lenght prompt without colores string */
129 static int cursor; /* required global for signal handler */
130 static int len; /* --- "" - - "" - -"- --""-- --""--- */
131 static char *command_ps; /* --- "" - - "" - -"- --""-- --""--- */
133 #ifndef CONFIG_FEATURE_SH_FANCY_PROMPT
136 char *cmdedit_prompt; /* --- "" - - "" - -"- --""-- --""--- */
138 #ifdef CONFIG_FEATURE_GETUSERNAME_AND_HOMEDIR
139 static char *user_buf = "";
140 static char *home_pwd_buf = "";
144 #ifdef CONFIG_FEATURE_SH_FANCY_PROMPT
145 static char *hostname_buf;
146 static int num_ok_lines = 1;
150 #ifdef CONFIG_FEATURE_COMMAND_TAB_COMPLETION
152 #ifndef CONFIG_FEATURE_GETUSERNAME_AND_HOMEDIR
159 #endif /* CONFIG_FEATURE_COMMAND_TAB_COMPLETION */
161 static void cmdedit_setwidth(int w, int redraw_flg);
163 static void win_changed(int nsig)
165 static sighandler_t previous_SIGWINCH_handler; /* for reset */
167 /* emulate || signal call */
168 if (nsig == -SIGWINCH || nsig == SIGWINCH) {
170 get_terminal_width_height(0, &width, NULL);
171 cmdedit_setwidth(width, nsig == SIGWINCH);
173 /* Unix not all standart in recall signal */
175 if (nsig == -SIGWINCH) /* save previous handler */
176 previous_SIGWINCH_handler = signal(SIGWINCH, win_changed);
177 else if (nsig == SIGWINCH) /* signaled called handler */
178 signal(SIGWINCH, win_changed); /* set for next call */
180 /* set previous handler */
181 signal(SIGWINCH, previous_SIGWINCH_handler); /* reset */
184 static void cmdedit_reset_term(void)
186 if ((handlers_sets & SET_RESET_TERM) != 0) {
187 /* sparc and other have broken termios support: use old termio handling. */
188 setTermSettings(STDIN_FILENO, (void *) &initial_settings);
189 handlers_sets &= ~SET_RESET_TERM;
191 if ((handlers_sets & SET_WCHG_HANDLERS) != 0) {
192 /* reset SIGWINCH handler to previous (default) */
194 handlers_sets &= ~SET_WCHG_HANDLERS;
200 /* special for recount position for scroll and remove terminal margin effect */
201 static void cmdedit_set_out_char(int next_char)
204 int c = (int)((unsigned char) command_ps[cursor]);
207 c = ' '; /* destroy end char? */
208 #ifdef CONFIG_FEATURE_NONPRINTABLE_INVERSE_PUT
209 if (!Isprint(c)) { /* Inverse put non-printable characters */
216 printf("\033[7m%c\033[0m", c);
219 if (initial_settings.c_lflag & ECHO) putchar(c);
220 if (++cmdedit_x >= cmdedit_termw) {
221 /* terminal is scrolled down */
227 /* destroy "(auto)margin" */
234 /* Move to end line. Bonus: rewrite line from cursor */
235 static void input_end(void)
238 cmdedit_set_out_char(0);
241 /* Go to the next line */
242 static void goto_new_line(void)
250 static void out1str(const char *s)
256 static void beep(void)
261 /* Move back one character */
262 /* special for slow terminal */
263 static void input_backward(int num)
267 cursor -= num; /* new cursor (in command, not terminal) */
269 if (cmdedit_x >= num) { /* no to up line */
275 printf("\033[%dD", num);
280 putchar('\r'); /* back to first terminal pos. */
281 num -= cmdedit_x; /* set previous backward */
283 count_y = 1 + num / cmdedit_termw;
284 printf("\033[%dA", count_y);
285 cmdedit_y -= count_y;
286 /* require forward after uping */
287 cmdedit_x = cmdedit_termw * count_y - num;
288 printf("\033[%dC", cmdedit_x); /* set term cursor */
292 static void put_prompt(void)
294 out1str(cmdedit_prompt);
295 cmdedit_x = cmdedit_prmt_len; /* count real x terminal position */
297 cmdedit_y = 0; /* new quasireal y */
300 #ifndef CONFIG_FEATURE_SH_FANCY_PROMPT
301 static void parse_prompt(const char *prmt_ptr)
303 cmdedit_prompt = prmt_ptr;
304 cmdedit_prmt_len = strlen(prmt_ptr);
308 static void parse_prompt(const char *prmt_ptr)
311 size_t cur_prmt_len = 0;
312 char flg_not_length = '[';
313 char *prmt_mem_ptr = xzalloc(1);
314 char *pwd_buf = xgetcwd(0);
315 char buf2[PATH_MAX + 1];
321 pwd_buf=(char *)bb_msg_unknown;
329 const char *cp = prmt_ptr;
332 c = bb_process_escape_sequence(&prmt_ptr);
338 #ifdef CONFIG_FEATURE_GETUSERNAME_AND_HOMEDIR
347 if (gethostname(pbuf, 255) < 0) {
350 char *s = strchr(pbuf, '.');
359 c = my_euid == 0 ? '#' : '$';
361 #ifdef CONFIG_FEATURE_GETUSERNAME_AND_HOMEDIR
364 l = strlen(home_pwd_buf);
365 if (home_pwd_buf[0] != 0 &&
366 strncmp(home_pwd_buf, pbuf, l) == 0 &&
367 (pbuf[l]=='/' || pbuf[l]=='\0') &&
368 strlen(pwd_buf+l)<PATH_MAX) {
371 strcpy(pbuf+1, pwd_buf+l);
377 cp = strrchr(pbuf,'/');
378 if ( (cp != NULL) && (cp != pbuf) )
382 snprintf(pbuf = buf2, sizeof(buf2), "%d", num_ok_lines);
384 case 'e': case 'E': /* \e \E = \033 */
388 for (l = 0; l < 3;) {
390 buf2[l++] = *prmt_ptr;
392 h = strtol(buf2, &pbuf, 16);
393 if (h > UCHAR_MAX || (pbuf - buf2) < l) {
400 c = (char)strtol(buf2, 0, 16);
406 if (c == flg_not_length) {
407 flg_not_length = flg_not_length == '[' ? ']' : '[';
416 cur_prmt_len = strlen(pbuf);
417 prmt_len += cur_prmt_len;
418 if (flg_not_length != ']')
419 cmdedit_prmt_len += cur_prmt_len;
420 prmt_mem_ptr = strcat(xrealloc(prmt_mem_ptr, prmt_len+1), pbuf);
422 if(pwd_buf!=(char *)bb_msg_unknown)
424 cmdedit_prompt = prmt_mem_ptr;
430 /* draw prompt, editor line, and clear tail */
431 static void redraw(int y, int back_cursor)
433 if (y > 0) /* up to start y */
434 printf("\033[%dA", y);
437 input_end(); /* rewrite */
438 printf("\033[J"); /* destroy tail after cursor */
439 input_backward(back_cursor);
442 #ifdef CONFIG_FEATURE_COMMAND_EDITING_VI
443 #define DELBUFSIZ 128
444 static char *delbuf; /* a (malloced) place to store deleted characters */
446 static char newdelflag; /* whether delbuf should be reused yet */
449 /* Delete the char in front of the cursor, optionally saving it
450 * for later putback */
451 static void input_delete(int save)
458 #ifdef CONFIG_FEATURE_COMMAND_EDITING_VI
462 delbuf = malloc(DELBUFSIZ);
463 /* safe if malloc fails */
467 if (delbuf && (delp - delbuf < DELBUFSIZ))
468 *delp++ = command_ps[j];
472 strcpy(command_ps + j, command_ps + j + 1);
474 input_end(); /* rewrite new line */
475 cmdedit_set_out_char(0); /* destroy end char */
476 input_backward(cursor - j); /* back to old pos cursor */
479 #ifdef CONFIG_FEATURE_COMMAND_EDITING_VI
480 static void put(void)
482 int ocursor, j = delp - delbuf;
486 /* open hole and then fill it */
487 memmove(command_ps + cursor + j, command_ps + cursor, len - cursor + 1);
488 strncpy(command_ps + cursor, delbuf, j);
490 input_end(); /* rewrite new line */
491 input_backward(cursor-ocursor-j+1); /* at end of new text */
495 /* Delete the char in back of the cursor */
496 static void input_backspace(void)
505 /* Move forward one character */
506 static void input_forward(void)
509 cmdedit_set_out_char(command_ps[cursor + 1]);
512 static void cmdedit_setwidth(int w, int redraw_flg)
514 cmdedit_termw = cmdedit_prmt_len + 2;
515 if (w <= cmdedit_termw) {
516 cmdedit_termw = cmdedit_termw % w;
518 if (w > cmdedit_termw) {
522 /* new y for current cursor */
523 int new_y = (cursor + cmdedit_prmt_len) / w;
526 redraw((new_y >= cmdedit_y ? new_y : cmdedit_y), len - cursor);
532 static void cmdedit_init(void)
534 cmdedit_prmt_len = 0;
535 if ((handlers_sets & SET_WCHG_HANDLERS) == 0) {
536 /* emulate usage handler to set handler and call yours work */
537 win_changed(-SIGWINCH);
538 handlers_sets |= SET_WCHG_HANDLERS;
541 if ((handlers_sets & SET_ATEXIT) == 0) {
542 #ifdef CONFIG_FEATURE_GETUSERNAME_AND_HOMEDIR
543 struct passwd *entry;
546 entry = getpwuid(my_euid);
548 user_buf = xstrdup(entry->pw_name);
549 home_pwd_buf = xstrdup(entry->pw_dir);
553 #ifdef CONFIG_FEATURE_COMMAND_TAB_COMPLETION
555 #ifndef CONFIG_FEATURE_GETUSERNAME_AND_HOMEDIR
560 #endif /* CONFIG_FEATURE_COMMAND_TAB_COMPLETION */
561 handlers_sets |= SET_ATEXIT;
562 atexit(cmdedit_reset_term); /* be sure to do this only once */
566 #ifdef CONFIG_FEATURE_COMMAND_TAB_COMPLETION
568 static char **matches;
569 static int num_matches;
570 static char *add_char_to_match;
572 static void add_match(char *matched, int add_char)
574 int nm = num_matches;
577 matches = xrealloc(matches, nm1 * sizeof(char *));
578 add_char_to_match = xrealloc(add_char_to_match, nm1);
579 matches[nm] = matched;
580 add_char_to_match[nm] = (char)add_char;
584 static int is_execute(const struct stat *st)
586 if ((!my_euid && (st->st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))) ||
587 (my_uid == st->st_uid && (st->st_mode & S_IXUSR)) ||
588 (my_gid == st->st_gid && (st->st_mode & S_IXGRP)) ||
589 (st->st_mode & S_IXOTH)) return TRUE;
593 #ifdef CONFIG_FEATURE_COMMAND_USERNAME_COMPLETION
595 static void username_tab_completion(char *ud, char *with_shash_flg)
597 struct passwd *entry;
600 ud++; /* ~user/... to user/... */
601 userlen = strlen(ud);
603 if (with_shash_flg) { /* "~/..." or "~user/..." */
604 char *sav_ud = ud - 1;
608 if (*ud == '/') { /* "~/..." */
612 temp = strchr(ud, '/');
613 *temp = 0; /* ~user\0 */
614 entry = getpwnam(ud);
615 *temp = '/'; /* restore ~user/... */
618 home = entry->pw_dir;
621 if ((userlen + strlen(home) + 1) < BUFSIZ) {
622 char temp2[BUFSIZ]; /* argument size */
625 sprintf(temp2, "%s%s", home, ud);
626 strcpy(sav_ud, temp2);
633 while ((entry = getpwent()) != NULL) {
634 /* Null usernames should result in all users as possible completions. */
635 if ( /*!userlen || */ !strncmp(ud, entry->pw_name, userlen)) {
636 add_match(xasprintf("~%s", entry->pw_name), '/');
643 #endif /* CONFIG_FEATURE_COMMAND_USERNAME_COMPLETION */
652 const char *cmdedit_path_lookup;
654 #define cmdedit_path_lookup getenv("PATH")
657 static int path_parse(char ***p, int flags)
663 /* if not setenv PATH variable, to search cur dir "." */
664 if (flags != FIND_EXE_ONLY || (pth = cmdedit_path_lookup) == 0 ||
665 /* PATH=<empty> or PATH=:<empty> */
666 *pth == 0 || (*pth == ':' && *(pth + 1) == 0)) {
674 npth++; /* count words is + 1 count ':' */
675 tmp = strchr(tmp, ':');
678 break; /* :<empty> */
683 *p = xmalloc(npth * sizeof(char *));
686 (*p)[0] = xstrdup(tmp);
687 npth = 1; /* count words is + 1 count ':' */
690 tmp = strchr(tmp, ':');
692 (*p)[0][(tmp - pth)] = 0; /* ':' -> '\0' */
694 break; /* :<empty> */
697 (*p)[npth++] = &(*p)[0][(tmp - pth)]; /* p[next]=p[0][&'\0'+1] */
703 static char *add_quote_for_spec_chars(char *found, int add)
706 char *s = xmalloc((strlen(found) + 1) * 2);
709 if (strchr(" `\"#$%^&*()=+{}[]:;\'|\\<>", *found))
719 static void exe_n_cwd_tab_completion(char *command, int type)
726 char **paths = path1;
730 char *pfind = strrchr(command, '/');
735 /* no dir, if flags==EXE_ONLY - get paths, else "." */
736 npaths = path_parse(&paths, type);
740 /* save for change */
741 strcpy(dirbuf, command);
743 dirbuf[(pfind - command) + 1] = 0;
744 #ifdef CONFIG_FEATURE_COMMAND_USERNAME_COMPLETION
745 if (dirbuf[0] == '~') /* ~/... or ~user/... */
746 username_tab_completion(dirbuf, dirbuf);
748 /* "strip" dirname in command */
752 npaths = 1; /* only 1 dir */
755 for (i = 0; i < npaths; i++) {
757 dir = opendir(paths[i]);
758 if (!dir) /* Don't print an error */
761 while ((next = readdir(dir)) != NULL) {
762 char *str_found = next->d_name;
766 if (strncmp(str_found, pfind, strlen(pfind)))
768 /* not see .name without .match */
769 if (*str_found == '.' && *pfind == 0) {
770 if (*paths[i] == '/' && paths[i][1] == 0
771 && str_found[1] == 0) str_found = ""; /* only "/" */
775 found = concat_path_file(paths[i], str_found);
776 /* hmm, remover in progress? */
777 if (stat(found, &st) < 0)
779 /* find with dirs ? */
780 if (paths[i] != dirbuf)
781 strcpy(found, next->d_name); /* only name */
782 if (S_ISDIR(st.st_mode)) {
783 /* name is directory */
784 char *e = found + strlen(found) - 1;
790 /* not put found file if search only dirs for cd */
791 if (type == FIND_DIR_ONLY)
793 if (type == FIND_FILE_ONLY ||
794 (type == FIND_EXE_ONLY && is_execute(&st)))
797 /* Add it to the list */
798 add_match(found, add_chr);
805 if (paths != path1) {
806 free(paths[0]); /* allocated memory only in first member */
812 #define QUOT (UCHAR_MAX+1)
814 #define collapse_pos(is, in) { \
815 memmove(int_buf+(is), int_buf+(in), (BUFSIZ+1-(is)-(in))*sizeof(int)); \
816 memmove(pos_buf+(is), pos_buf+(in), (BUFSIZ+1-(is)-(in))*sizeof(int)); }
818 static int find_match(char *matchBuf, int *len_with_quotes)
823 int int_buf[BUFSIZ + 1];
824 int pos_buf[BUFSIZ + 1];
826 /* set to integer dimension characters and own positions */
828 int_buf[i] = (int) ((unsigned char) matchBuf[i]);
829 if (int_buf[i] == 0) {
830 pos_buf[i] = -1; /* indicator end line */
836 /* mask \+symbol and convert '\t' to ' ' */
837 for (i = j = 0; matchBuf[i]; i++, j++)
838 if (matchBuf[i] == '\\') {
839 collapse_pos(j, j + 1);
842 #ifdef CONFIG_FEATURE_NONPRINTABLE_INVERSE_PUT
843 if (matchBuf[i] == '\t') /* algorithm equivalent */
844 int_buf[j] = ' ' | QUOT;
847 #ifdef CONFIG_FEATURE_NONPRINTABLE_INVERSE_PUT
848 else if (matchBuf[i] == '\t')
852 /* mask "symbols" or 'symbols' */
854 for (i = 0; int_buf[i]; i++) {
856 if (c == '\'' || c == '"') {
865 } else if (c2 != 0 && c != '$')
869 /* skip commands with arguments if line have commands delimiters */
870 /* ';' ';;' '&' '|' '&&' '||' but `>&' `<&' `>|' */
871 for (i = 0; int_buf[i]; i++) {
874 j = i ? int_buf[i - 1] : -1;
876 if (c == ';' || c == '&' || c == '|') {
877 command_mode = 1 + (c == c2);
879 if (j == '>' || j == '<')
881 } else if (c == '|' && j == '>')
885 collapse_pos(0, i + command_mode);
886 i = -1; /* hack incremet */
889 /* collapse `command...` */
890 for (i = 0; int_buf[i]; i++)
891 if (int_buf[i] == '`') {
892 for (j = i + 1; int_buf[j]; j++)
893 if (int_buf[j] == '`') {
894 collapse_pos(i, j + 1);
899 /* not found close ` - command mode, collapse all previous */
900 collapse_pos(0, i + 1);
903 i--; /* hack incremet */
906 /* collapse (command...(command...)...) or {command...{command...}...} */
907 c = 0; /* "recursive" level */
909 for (i = 0; int_buf[i]; i++)
910 if (int_buf[i] == '(' || int_buf[i] == '{') {
911 if (int_buf[i] == '(')
915 collapse_pos(0, i + 1);
916 i = -1; /* hack incremet */
918 for (i = 0; pos_buf[i] >= 0 && (c > 0 || c2 > 0); i++)
919 if ((int_buf[i] == ')' && c > 0) || (int_buf[i] == '}' && c2 > 0)) {
920 if (int_buf[i] == ')')
924 collapse_pos(0, i + 1);
925 i = -1; /* hack incremet */
928 /* skip first not quote space */
929 for (i = 0; int_buf[i]; i++)
930 if (int_buf[i] != ' ')
935 /* set find mode for completion */
936 command_mode = FIND_EXE_ONLY;
937 for (i = 0; int_buf[i]; i++)
938 if (int_buf[i] == ' ' || int_buf[i] == '<' || int_buf[i] == '>') {
939 if (int_buf[i] == ' ' && command_mode == FIND_EXE_ONLY
940 && matchBuf[pos_buf[0]]=='c'
941 && matchBuf[pos_buf[1]]=='d' )
942 command_mode = FIND_DIR_ONLY;
944 command_mode = FIND_FILE_ONLY;
949 for (i = 0; int_buf[i]; i++);
951 for (--i; i >= 0; i--) {
953 if (c == ' ' || c == '<' || c == '>' || c == '|' || c == '&') {
954 collapse_pos(0, i + 1);
958 /* skip first not quoted '\'' or '"' */
959 for (i = 0; int_buf[i] == '\'' || int_buf[i] == '"'; i++);
960 /* collapse quote or unquote // or /~ */
961 while ((int_buf[i] & ~QUOT) == '/' &&
962 ((int_buf[i + 1] & ~QUOT) == '/'
963 || (int_buf[i + 1] & ~QUOT) == '~')) {
967 /* set only match and destroy quotes */
969 for (c = 0; pos_buf[i] >= 0; i++) {
970 matchBuf[c++] = matchBuf[pos_buf[i]];
974 /* old lenght matchBuf with quotes symbols */
975 *len_with_quotes = j ? j - pos_buf[0] : 0;
981 display by column original ideas from ls applet,
982 very optimize by my :)
984 static void showfiles(void)
987 int column_width = 0;
988 int nfiles = num_matches;
993 /* find the longest file name- use that as the column width */
994 for (row = 0; row < nrows; row++) {
995 l = strlen(matches[row]);
996 if(add_char_to_match[row])
998 if (column_width < l)
1001 column_width += 2; /* min space for columns */
1002 ncols = cmdedit_termw / column_width;
1007 nrows++; /* round up fractionals */
1012 for (row = 0; row < nrows; row++) {
1017 for(nc = 1; nc < ncols && n+nrows < nfiles; n += nrows, nc++) {
1018 str_add_chr[0] = add_char_to_match[n];
1019 acol = str_add_chr[0] ? column_width - 1 : column_width;
1020 printf("%s%s", matches[n], str_add_chr);
1021 l = strlen(matches[n]);
1027 str_add_chr[0] = add_char_to_match[n];
1028 printf("%s%s\n", matches[n], str_add_chr);
1033 static void input_tab(int *lastWasTab)
1035 /* Do TAB completion */
1036 if (lastWasTab == 0) { /* free all memory */
1038 while (num_matches > 0)
1039 free(matches[--num_matches]);
1041 matches = (char **) NULL;
1042 free(add_char_to_match);
1043 add_char_to_match = NULL;
1047 if (! *lastWasTab) {
1051 char matchBuf[BUFSIZ];
1055 *lastWasTab = TRUE; /* flop trigger */
1057 /* Make a local copy of the string -- up
1058 * to the position of the cursor */
1059 tmp = strncpy(matchBuf, command_ps, cursor);
1062 find_type = find_match(matchBuf, &recalc_pos);
1064 /* Free up any memory already allocated */
1067 #ifdef CONFIG_FEATURE_COMMAND_USERNAME_COMPLETION
1068 /* If the word starts with `~' and there is no slash in the word,
1069 * then try completing this word as a username. */
1071 if (matchBuf[0] == '~' && strchr(matchBuf, '/') == 0)
1072 username_tab_completion(matchBuf, NULL);
1075 /* Try to match any executable in our path and everything
1076 * in the current working directory that matches. */
1077 exe_n_cwd_tab_completion(matchBuf, find_type);
1078 /* Remove duplicate found and sort */
1083 for(i=0; i<(n-1); i++) {
1084 for(j=i+1; j<n; j++) {
1085 if(matches[i]!=NULL && matches[j]!=NULL) {
1086 srt = strcmp(matches[i], matches[j]);
1090 } else if(srt > 0) {
1092 matches[i] = matches[j];
1094 srt = add_char_to_match[i];
1095 add_char_to_match[i] = add_char_to_match[j];
1096 add_char_to_match[j] = srt;
1105 matches[n]=matches[i];
1106 add_char_to_match[n]=add_char_to_match[i];
1111 /* Did we find exactly one match? */
1112 if (!matches || num_matches > 1) {
1116 return; /* not found */
1117 /* find minimal match */
1118 tmp1 = xstrdup(matches[0]);
1119 for (tmp = tmp1; *tmp; tmp++)
1120 for (len_found = 1; len_found < num_matches; len_found++)
1121 if (matches[len_found][(tmp - tmp1)] != *tmp) {
1125 if (*tmp1 == 0) { /* have unique */
1129 tmp = add_quote_for_spec_chars(tmp1, 0);
1131 } else { /* one match */
1132 tmp = add_quote_for_spec_chars(matches[0], add_char_to_match[0]);
1133 /* for next completion current found */
1134 *lastWasTab = FALSE;
1136 len_found = strlen(tmp);
1137 /* have space to placed match? */
1138 if ((len_found - strlen(matchBuf) + len) < BUFSIZ) {
1140 /* before word for match */
1141 command_ps[cursor - recalc_pos] = 0;
1142 /* save tail line */
1143 strcpy(matchBuf, command_ps + cursor);
1145 strcat(command_ps, tmp);
1147 strcat(command_ps, matchBuf);
1148 /* back to begin word for match */
1149 input_backward(recalc_pos);
1151 recalc_pos = cursor + len_found;
1153 len = strlen(command_ps);
1154 /* write out the matched command */
1155 redraw(cmdedit_y, len - recalc_pos);
1159 /* Ok -- the last char was a TAB. Since they
1160 * just hit TAB again, print a list of all the
1161 * available choices... */
1162 if (matches && num_matches > 0) {
1163 int sav_cursor = cursor; /* change goto_new_line() */
1165 /* Go to the next line */
1168 redraw(0, len - sav_cursor);
1172 #endif /* CONFIG_FEATURE_COMMAND_TAB_COMPLETION */
1174 #if MAX_HISTORY >= 1
1175 static void get_previous_history(void)
1177 if(command_ps[0] != 0 || history[cur_history] == 0) {
1178 free(history[cur_history]);
1179 history[cur_history] = xstrdup(command_ps);
1184 static int get_next_history(void)
1186 int ch = cur_history;
1188 if (ch < n_history) {
1189 get_previous_history(); /* save the current history line */
1190 return (cur_history = ch+1);
1197 #ifdef CONFIG_FEATURE_COMMAND_SAVEHISTORY
1198 void load_history ( const char *fromfile )
1205 for(hi = n_history; hi > 0; ) {
1207 free ( history [hi] );
1210 if (( fp = fopen ( fromfile, "r" ))) {
1212 for ( hi = 0; hi < MAX_HISTORY; ) {
1213 char * hl = bb_get_chomped_line_from_file(fp);
1221 if(l == 0 || hl[0] == ' ') {
1225 history [hi++] = hl;
1229 cur_history = n_history = hi;
1232 void save_history ( const char *tofile )
1234 FILE *fp = fopen ( tofile, "w" );
1239 for ( i = 0; i < n_history; i++ ) {
1240 fprintf(fp, "%s\n", history [i]);
1256 * This function is used to grab a character buffer
1257 * from the input file descriptor and allows you to
1258 * a string with full command editing (sort of like
1261 * The following standard commands are not implemented:
1262 * ESC-b -- Move back one word
1263 * ESC-f -- Move forward one word
1264 * ESC-d -- Delete back one word
1265 * ESC-h -- Delete forward one word
1266 * CTL-t -- Transpose two characters
1268 * Minimalist vi-style command line editing available if configured.
1269 * vi mode implemented 2005 by Paul Fox <pgf@foxharp.boston.ma.us>
1273 #ifdef CONFIG_FEATURE_COMMAND_EDITING_VI
1276 void setvimode ( int viflag )
1282 vi_Word_motion(char *command, int eat)
1284 while (cursor < len && !isspace(command[cursor]))
1286 if (eat) while (cursor < len && isspace(command[cursor]))
1291 vi_word_motion(char *command, int eat)
1293 if (isalnum(command[cursor]) || command[cursor] == '_') {
1294 while (cursor < len &&
1295 (isalnum(command[cursor+1]) ||
1296 command[cursor+1] == '_'))
1298 } else if (ispunct(command[cursor])) {
1299 while (cursor < len &&
1300 (ispunct(command[cursor+1])))
1307 if (eat && cursor < len && isspace(command[cursor]))
1308 while (cursor < len && isspace(command[cursor]))
1313 vi_End_motion(char *command)
1316 while (cursor < len && isspace(command[cursor]))
1318 while (cursor < len-1 && !isspace(command[cursor+1]))
1323 vi_end_motion(char *command)
1325 if (cursor >= len-1)
1328 while (cursor < len-1 && isspace(command[cursor]))
1330 if (cursor >= len-1)
1332 if (isalnum(command[cursor]) || command[cursor] == '_') {
1333 while (cursor < len-1 &&
1334 (isalnum(command[cursor+1]) ||
1335 command[cursor+1] == '_'))
1337 } else if (ispunct(command[cursor])) {
1338 while (cursor < len-1 &&
1339 (ispunct(command[cursor+1])))
1345 vi_Back_motion(char *command)
1347 while (cursor > 0 && isspace(command[cursor-1]))
1349 while (cursor > 0 && !isspace(command[cursor-1]))
1354 vi_back_motion(char *command)
1359 while (cursor > 0 && isspace(command[cursor]))
1363 if (isalnum(command[cursor]) || command[cursor] == '_') {
1364 while (cursor > 0 &&
1365 (isalnum(command[cursor-1]) ||
1366 command[cursor-1] == '_'))
1368 } else if (ispunct(command[cursor])) {
1369 while (cursor > 0 &&
1370 (ispunct(command[cursor-1])))
1377 * the emacs and vi modes share much of the code in the big
1378 * command loop. commands entered when in vi's command mode (aka
1379 * "escape mode") get an extra bit added to distinguish them --
1380 * this keeps them from being self-inserted. this clutters the
1381 * big switch a bit, but keeps all the code in one place.
1386 /* leave out the "vi-mode"-only case labels if vi editing isn't
1388 #define vi_case(caselabel) USE_FEATURE_COMMAND_EDITING(caselabel)
1390 /* convert uppercase ascii to equivalent control char, for readability */
1391 #define CNTRL(uc_char) ((uc_char) - 0x40)
1394 int cmdedit_read_input(char *prompt, char command[BUFSIZ])
1398 int lastWasTab = FALSE;
1401 #ifdef CONFIG_FEATURE_COMMAND_EDITING_VI
1405 /* prepare before init handlers */
1406 cmdedit_y = 0; /* quasireal y, not true work if line > xt*yt */
1408 command_ps = command;
1410 getTermSettings(0, (void *) &initial_settings);
1411 memcpy(&new_settings, &initial_settings, sizeof(struct termios));
1412 new_settings.c_lflag &= ~ICANON; /* unbuffered input */
1413 /* Turn off echoing and CTRL-C, so we can trap it */
1414 new_settings.c_lflag &= ~(ECHO | ECHONL | ISIG);
1415 /* Hmm, in linux c_cc[] not parsed if set ~ICANON */
1416 new_settings.c_cc[VMIN] = 1;
1417 new_settings.c_cc[VTIME] = 0;
1418 /* Turn off CTRL-C, so we can trap it */
1419 # ifndef _POSIX_VDISABLE
1420 # define _POSIX_VDISABLE '\0'
1422 new_settings.c_cc[VINTR] = _POSIX_VDISABLE;
1425 setTermSettings(0, (void *) &new_settings);
1426 handlers_sets |= SET_RESET_TERM;
1428 /* Now initialize things */
1430 /* Print out the command prompt */
1431 parse_prompt(prompt);
1435 fflush(stdout); /* buffered out to fast */
1437 if (safe_read(0, &c, 1) < 1)
1438 /* if we can't read input then exit */
1439 goto prepare_to_die;
1443 #ifdef CONFIG_FEATURE_COMMAND_EDITING_VI
1452 vi_case( case '\n'|vbit: )
1453 vi_case( case '\r'|vbit: )
1459 vi_case( case '0'|vbit: )
1460 /* Control-a -- Beginning of line */
1461 input_backward(cursor);
1464 vi_case( case 'h'|vbit: )
1465 vi_case( case '\b'|vbit: )
1466 vi_case( case DEL|vbit: )
1467 /* Control-b -- Move back one character */
1471 vi_case( case CNTRL('C')|vbit: )
1472 /* Control-c -- stop gathering input */
1481 break_out = -1; /* to control traps */
1485 /* Control-d -- Delete one character, or exit
1486 * if the len=0 and no chars to delete */
1490 #if !defined(CONFIG_ASH)
1493 /* cmdedit_reset_term() called in atexit */
1496 /* to control stopped jobs */
1497 len = break_out = -1;
1505 vi_case( case '$'|vbit: )
1506 /* Control-e -- End of line */
1510 vi_case( case 'l'|vbit: )
1511 vi_case( case ' '|vbit: )
1512 /* Control-f -- Move forward one character */
1517 /* Control-h and DEL */
1521 #ifdef CONFIG_FEATURE_COMMAND_TAB_COMPLETION
1522 input_tab(&lastWasTab);
1526 /* Control-k -- clear to end of line */
1527 *(command + cursor) = 0;
1532 vi_case( case CNTRL('L')|vbit: )
1533 /* Control-l -- clear screen */
1535 redraw(0, len-cursor);
1537 #if MAX_HISTORY >= 1
1539 vi_case( case CNTRL('N')|vbit: )
1540 vi_case( case 'j'|vbit: )
1541 /* Control-n -- Get next command in history */
1542 if (get_next_history())
1546 vi_case( case CNTRL('P')|vbit: )
1547 vi_case( case 'k'|vbit: )
1548 /* Control-p -- Get previous command from history */
1549 if (cur_history > 0) {
1550 get_previous_history();
1558 vi_case( case CNTRL('U')|vbit: )
1559 /* Control-U -- Clear line before cursor */
1561 strcpy(command, command + cursor);
1562 redraw(cmdedit_y, len -= cursor);
1566 vi_case( case CNTRL('W')|vbit: )
1567 /* Control-W -- Remove the last word */
1568 while (cursor > 0 && isspace(command[cursor-1]))
1570 while (cursor > 0 &&!isspace(command[cursor-1]))
1573 #if CONFIG_FEATURE_COMMAND_EDITING_VI
1578 input_backward(cursor);
1599 vi_Word_motion(command, 1);
1602 vi_word_motion(command, 1);
1605 vi_End_motion(command);
1608 vi_end_motion(command);
1611 vi_Back_motion(command);
1614 vi_back_motion(command);
1630 if (safe_read(0, &c, 1) < 1)
1631 goto prepare_to_die;
1632 if (c == (prevc & 0xff)) {
1634 input_backward(cursor);
1644 case 'w': /* "dw", "cw" */
1645 vi_word_motion(command, vi_cmdmode);
1647 case 'W': /* 'dW', 'cW' */
1648 vi_Word_motion(command, vi_cmdmode);
1650 case 'e': /* 'de', 'ce' */
1651 vi_end_motion(command);
1654 case 'E': /* 'dE', 'cE' */
1655 vi_End_motion(command);
1660 input_backward(cursor - sc);
1661 while (nc-- > cursor)
1664 case 'b': /* "db", "cb" */
1665 case 'B': /* implemented as B */
1667 vi_back_motion(command);
1669 vi_Back_motion(command);
1670 while (sc-- > cursor)
1673 case ' ': /* "d ", "c " */
1676 case '$': /* "d$", "c$" */
1678 while (cursor < len)
1691 if (safe_read(0, &c, 1) < 1)
1692 goto prepare_to_die;
1696 *(command + cursor) = c;
1701 #endif /* CONFIG_FEATURE_COMMAND_EDITING_VI */
1705 #if CONFIG_FEATURE_COMMAND_EDITING_VI
1707 /* ESC: insert mode --> command mode */
1713 /* escape sequence follows */
1714 if (safe_read(0, &c, 1) < 1)
1715 goto prepare_to_die;
1716 /* different vt100 emulations */
1717 if (c == '[' || c == 'O') {
1718 vi_case( case '['|vbit: )
1719 vi_case( case 'O'|vbit: )
1720 if (safe_read(0, &c, 1) < 1)
1721 goto prepare_to_die;
1723 if (c >= '1' && c <= '9') {
1724 unsigned char dummy;
1726 if (safe_read(0, &dummy, 1) < 1)
1727 goto prepare_to_die;
1732 #ifdef CONFIG_FEATURE_COMMAND_TAB_COMPLETION
1733 case '\t': /* Alt-Tab */
1735 input_tab(&lastWasTab);
1738 #if MAX_HISTORY >= 1
1740 /* Up Arrow -- Get previous command from history */
1741 if (cur_history > 0) {
1742 get_previous_history();
1749 /* Down Arrow -- Get next command in history */
1750 if (!get_next_history())
1752 /* Rewrite the line with the selected history item */
1754 /* change command */
1755 len = strlen(strcpy(command, history[cur_history]));
1756 /* redraw and go to eol (bol, in vi */
1757 #if CONFIG_FEATURE_COMMAND_EDITING_VI
1758 redraw(cmdedit_y, vi_mode ? 9999:0);
1760 redraw(cmdedit_y, 0);
1765 /* Right Arrow -- Move forward one character */
1769 /* Left Arrow -- Move back one character */
1779 input_backward(cursor);
1792 default: /* If it's regular input, do the normal thing */
1793 #ifdef CONFIG_FEATURE_NONPRINTABLE_INVERSE_PUT
1794 /* Control-V -- Add non-printable symbol */
1795 if (c == CNTRL('V')) {
1796 if (safe_read(0, &c, 1) < 1)
1797 goto prepare_to_die;
1805 #if CONFIG_FEATURE_COMMAND_EDITING_VI
1806 if (vi_cmdmode) /* don't self-insert */
1809 if (!Isprint(c)) /* Skip non-printable characters */
1813 if (len >= (BUFSIZ - 2)) /* Need to leave space for enter */
1818 if (cursor == (len - 1)) { /* Append if at the end of the line */
1819 *(command + cursor) = c;
1820 *(command + cursor + 1) = 0;
1821 cmdedit_set_out_char(0);
1822 } else { /* Insert otherwise */
1825 memmove(command + sc + 1, command + sc, len - sc);
1826 *(command + sc) = c;
1828 /* rewrite from cursor */
1830 /* to prev x pos + 1 */
1831 input_backward(cursor - sc);
1836 if (break_out) /* Enter is the command terminator, no more input. */
1843 setTermSettings(0, (void *) &initial_settings);
1844 handlers_sets &= ~SET_RESET_TERM;
1846 #if MAX_HISTORY >= 1
1847 /* Handle command history log */
1848 /* cleanup may be saved current command line */
1849 if (len> 0) { /* no put empty line */
1852 free(history[MAX_HISTORY]);
1853 history[MAX_HISTORY] = 0;
1854 /* After max history, remove the oldest command */
1855 if (i >= MAX_HISTORY) {
1857 for(i = 0; i < (MAX_HISTORY-1); i++)
1858 history[i] = history[i+1];
1860 history[i++] = xstrdup(command);
1863 #if defined(CONFIG_FEATURE_SH_FANCY_PROMPT)
1867 #else /* MAX_HISTORY < 1 */
1868 #if defined(CONFIG_FEATURE_SH_FANCY_PROMPT)
1869 if (len > 0) { /* no put empty line */
1873 #endif /* MAX_HISTORY >= 1 */
1874 if (break_out > 0) {
1875 command[len++] = '\n'; /* set '\n' */
1878 #if defined(CONFIG_FEATURE_CLEAN_UP) && defined(CONFIG_FEATURE_COMMAND_TAB_COMPLETION)
1879 input_tab(0); /* strong free */
1881 #if defined(CONFIG_FEATURE_SH_FANCY_PROMPT)
1882 free(cmdedit_prompt);
1884 cmdedit_reset_term();
1890 #endif /* CONFIG_FEATURE_COMMAND_EDITING */
1895 const char *applet_name = "debug stuff usage";
1897 #ifdef CONFIG_FEATURE_NONPRINTABLE_INVERSE_PUT
1901 int main(int argc, char **argv)
1905 #if defined(CONFIG_FEATURE_SH_FANCY_PROMPT)
1906 "\\[\\033[32;1m\\]\\u@\\[\\x1b[33;1m\\]\\h:\
1907 \\[\\033[34;1m\\]\\w\\[\\033[35;1m\\] \
1908 \\!\\[\\e[36;1m\\]\\$ \\[\\E[0m\\]";
1913 #ifdef CONFIG_FEATURE_NONPRINTABLE_INVERSE_PUT
1914 setlocale(LC_ALL, "");
1918 l = cmdedit_read_input(prompt, buff);
1919 if(l > 0 && buff[l-1] == '\n') {
1921 printf("*** cmdedit_read_input() returned line =%s=\n", buff);
1926 printf("*** cmdedit_read_input() detect ^D\n");