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
39 #include <sys/ioctl.h>
46 #include "../shell/cmdedit.h"
49 #ifdef CONFIG_LOCALE_SUPPORT
50 #define Isprint(c) isprint((c))
52 #define Isprint(c) ( (c) >= ' ' && (c) != ((unsigned char)'\233') )
57 /* pretect redefined for test */
58 #undef CONFIG_FEATURE_COMMAND_EDITING
59 #undef CONFIG_FEATURE_COMMAND_TAB_COMPLETION
60 #undef CONFIG_FEATURE_COMMAND_USERNAME_COMPLETION
61 #undef CONFIG_FEATURE_NONPRINTABLE_INVERSE_PUT
62 #undef CONFIG_FEATURE_CLEAN_UP
64 #define CONFIG_FEATURE_COMMAND_EDITING
65 #define CONFIG_FEATURE_COMMAND_TAB_COMPLETION
66 #define CONFIG_FEATURE_COMMAND_USERNAME_COMPLETION
67 #define CONFIG_FEATURE_NONPRINTABLE_INVERSE_PUT
68 #define CONFIG_FEATURE_CLEAN_UP
72 #ifdef CONFIG_FEATURE_COMMAND_TAB_COMPLETION
77 #ifdef CONFIG_FEATURE_COMMAND_EDITING
79 #if defined(CONFIG_FEATURE_COMMAND_USERNAME_COMPLETION) || defined(CONFIG_FEATURE_SH_FANCY_PROMPT)
80 #define CONFIG_FEATURE_GETUSERNAME_AND_HOMEDIR
83 #ifdef CONFIG_FEATURE_GETUSERNAME_AND_HOMEDIR
85 #endif /* advanced FEATURES */
88 /* Maximum length of the linked list for the command line history */
89 #ifndef CONFIG_FEATURE_COMMAND_HISTORY
90 #define MAX_HISTORY 15
92 #define MAX_HISTORY CONFIG_FEATURE_COMMAND_HISTORY
96 #warning cmdedit: You set MAX_HISTORY < 1. The history algorithm switched off.
98 static char *history[MAX_HISTORY+1]; /* history + current */
99 /* saved history lines */
100 static int n_history;
101 /* current pointer to history line */
102 static int cur_history;
106 #define setTermSettings(fd,argp) tcsetattr(fd,TCSANOW,argp)
107 #define getTermSettings(fd,argp) tcgetattr(fd, argp);
109 /* Current termio and the previous termio before starting sh */
110 static struct termios initial_settings, new_settings;
114 volatile int cmdedit_termw = 80; /* actual terminal width */
116 volatile int handlers_sets = 0; /* Set next bites: */
119 SET_ATEXIT = 1, /* when atexit() has been called
120 and get euid,uid,gid to fast compare */
121 SET_WCHG_HANDLERS = 2, /* winchg signal handler */
122 SET_RESET_TERM = 4, /* if the terminal needs to be reset upon exit */
126 static int cmdedit_x; /* real x terminal position */
127 static int cmdedit_y; /* pseudoreal y terminal position */
128 static int cmdedit_prmt_len; /* lenght prompt without colores string */
130 static int cursor; /* required global for signal handler */
131 static int len; /* --- "" - - "" - -"- --""-- --""--- */
132 static char *command_ps; /* --- "" - - "" - -"- --""-- --""--- */
134 #ifndef CONFIG_FEATURE_SH_FANCY_PROMPT
137 char *cmdedit_prompt; /* --- "" - - "" - -"- --""-- --""--- */
139 #ifdef CONFIG_FEATURE_GETUSERNAME_AND_HOMEDIR
140 static char *user_buf = "";
141 static char *home_pwd_buf = "";
145 #ifdef CONFIG_FEATURE_SH_FANCY_PROMPT
146 static char *hostname_buf;
147 static int num_ok_lines = 1;
151 #ifdef CONFIG_FEATURE_COMMAND_TAB_COMPLETION
153 #ifndef CONFIG_FEATURE_GETUSERNAME_AND_HOMEDIR
160 #endif /* CONFIG_FEATURE_COMMAND_TAB_COMPLETION */
162 static void cmdedit_setwidth(int w, int redraw_flg);
164 static void win_changed(int nsig)
166 static sighandler_t previous_SIGWINCH_handler; /* for reset */
168 /* emulate || signal call */
169 if (nsig == -SIGWINCH || nsig == SIGWINCH) {
171 get_terminal_width_height(0, &width, NULL);
172 cmdedit_setwidth(width, nsig == SIGWINCH);
174 /* Unix not all standart in recall signal */
176 if (nsig == -SIGWINCH) /* save previous handler */
177 previous_SIGWINCH_handler = signal(SIGWINCH, win_changed);
178 else if (nsig == SIGWINCH) /* signaled called handler */
179 signal(SIGWINCH, win_changed); /* set for next call */
181 /* set previous handler */
182 signal(SIGWINCH, previous_SIGWINCH_handler); /* reset */
185 static void cmdedit_reset_term(void)
187 if ((handlers_sets & SET_RESET_TERM) != 0) {
188 /* sparc and other have broken termios support: use old termio handling. */
189 setTermSettings(STDIN_FILENO, (void *) &initial_settings);
190 handlers_sets &= ~SET_RESET_TERM;
192 if ((handlers_sets & SET_WCHG_HANDLERS) != 0) {
193 /* reset SIGWINCH handler to previous (default) */
195 handlers_sets &= ~SET_WCHG_HANDLERS;
201 /* special for recount position for scroll and remove terminal margin effect */
202 static void cmdedit_set_out_char(int next_char)
205 int c = (int)((unsigned char) command_ps[cursor]);
208 c = ' '; /* destroy end char? */
209 #ifdef CONFIG_FEATURE_NONPRINTABLE_INVERSE_PUT
210 if (!Isprint(c)) { /* Inverse put non-printable characters */
217 printf("\033[7m%c\033[0m", c);
221 if (++cmdedit_x >= cmdedit_termw) {
222 /* terminal is scrolled down */
228 /* destroy "(auto)margin" */
235 /* Move to end line. Bonus: rewrite line from cursor */
236 static void input_end(void)
239 cmdedit_set_out_char(0);
242 /* Go to the next line */
243 static void goto_new_line(void)
251 static inline void out1str(const char *s)
257 static inline void beep(void)
262 /* Move back one character */
263 /* special for slow terminal */
264 static void input_backward(int num)
268 cursor -= num; /* new cursor (in command, not terminal) */
270 if (cmdedit_x >= num) { /* no to up line */
277 printf("\033[%dD", num);
282 putchar('\r'); /* back to first terminal pos. */
283 num -= cmdedit_x; /* set previous backward */
285 count_y = 1 + num / cmdedit_termw;
286 printf("\033[%dA", count_y);
287 cmdedit_y -= count_y;
288 /* require forward after uping */
289 cmdedit_x = cmdedit_termw * count_y - num;
290 printf("\033[%dC", cmdedit_x); /* set term cursor */
294 static void put_prompt(void)
296 out1str(cmdedit_prompt);
297 cmdedit_x = cmdedit_prmt_len; /* count real x terminal position */
299 cmdedit_y = 0; /* new quasireal y */
302 #ifndef CONFIG_FEATURE_SH_FANCY_PROMPT
303 static void parse_prompt(const char *prmt_ptr)
305 cmdedit_prompt = prmt_ptr;
306 cmdedit_prmt_len = strlen(prmt_ptr);
310 static void parse_prompt(const char *prmt_ptr)
313 size_t cur_prmt_len = 0;
314 char flg_not_length = '[';
315 char *prmt_mem_ptr = xcalloc(1, 1);
316 char *pwd_buf = xgetcwd(0);
317 char buf2[PATH_MAX + 1];
323 pwd_buf=(char *)bb_msg_unknown;
331 const char *cp = prmt_ptr;
334 c = bb_process_escape_sequence(&prmt_ptr);
340 #ifdef CONFIG_FEATURE_GETUSERNAME_AND_HOMEDIR
348 pbuf = xcalloc(256, 1);
349 if (gethostname(pbuf, 255) < 0) {
352 char *s = strchr(pbuf, '.');
361 c = my_euid == 0 ? '#' : '$';
363 #ifdef CONFIG_FEATURE_GETUSERNAME_AND_HOMEDIR
366 l = strlen(home_pwd_buf);
367 if (home_pwd_buf[0] != 0 &&
368 strncmp(home_pwd_buf, pbuf, l) == 0 &&
369 (pbuf[l]=='/' || pbuf[l]=='\0') &&
370 strlen(pwd_buf+l)<PATH_MAX) {
373 strcpy(pbuf+1, pwd_buf+l);
379 cp = strrchr(pbuf,'/');
380 if ( (cp != NULL) && (cp != pbuf) )
384 snprintf(pbuf = buf2, sizeof(buf2), "%d", num_ok_lines);
386 case 'e': case 'E': /* \e \E = \033 */
390 for (l = 0; l < 3;) {
392 buf2[l++] = *prmt_ptr;
394 h = strtol(buf2, &pbuf, 16);
395 if (h > UCHAR_MAX || (pbuf - buf2) < l) {
402 c = (char)strtol(buf2, 0, 16);
408 if (c == flg_not_length) {
409 flg_not_length = flg_not_length == '[' ? ']' : '[';
418 cur_prmt_len = strlen(pbuf);
419 prmt_len += cur_prmt_len;
420 if (flg_not_length != ']')
421 cmdedit_prmt_len += cur_prmt_len;
422 prmt_mem_ptr = strcat(xrealloc(prmt_mem_ptr, prmt_len+1), pbuf);
424 if(pwd_buf!=(char *)bb_msg_unknown)
426 cmdedit_prompt = prmt_mem_ptr;
432 /* draw prompt, editor line, and clear tail */
433 static void redraw(int y, int back_cursor)
435 if (y > 0) /* up to start y */
436 printf("\033[%dA", y);
439 input_end(); /* rewrite */
440 printf("\033[J"); /* destroy tail after cursor */
441 input_backward(back_cursor);
444 #ifdef CONFIG_FEATURE_COMMAND_EDITING_VI
445 #define DELBUFSIZ 128
446 static char *delbuf; /* a (malloced) place to store deleted characters */
448 static char newdelflag; /* whether delbuf should be reused yet */
451 /* Delete the char in front of the cursor, optionally saving it
452 * for later putback */
453 static void input_delete(int save)
460 #ifdef CONFIG_FEATURE_COMMAND_EDITING_VI
464 delbuf = malloc(DELBUFSIZ);
465 /* safe if malloc fails */
469 if (delbuf && (delp - delbuf < DELBUFSIZ))
470 *delp++ = command_ps[j];
474 strcpy(command_ps + j, command_ps + j + 1);
476 input_end(); /* rewrite new line */
477 cmdedit_set_out_char(0); /* destroy end char */
478 input_backward(cursor - j); /* back to old pos cursor */
481 #ifdef CONFIG_FEATURE_COMMAND_EDITING_VI
482 static void put(void)
484 int ocursor, j = delp - delbuf;
488 /* open hole and then fill it */
489 memmove(command_ps + cursor + j, command_ps + cursor, len - cursor + 1);
490 strncpy(command_ps + cursor, delbuf, j);
492 input_end(); /* rewrite new line */
493 input_backward(cursor-ocursor-j+1); /* at end of new text */
497 /* Delete the char in back of the cursor */
498 static void input_backspace(void)
507 /* Move forward one character */
508 static void input_forward(void)
511 cmdedit_set_out_char(command_ps[cursor + 1]);
514 static void cmdedit_setwidth(int w, int redraw_flg)
516 cmdedit_termw = cmdedit_prmt_len + 2;
517 if (w <= cmdedit_termw) {
518 cmdedit_termw = cmdedit_termw % w;
520 if (w > cmdedit_termw) {
524 /* new y for current cursor */
525 int new_y = (cursor + cmdedit_prmt_len) / w;
528 redraw((new_y >= cmdedit_y ? new_y : cmdedit_y), len - cursor);
534 static void cmdedit_init(void)
536 cmdedit_prmt_len = 0;
537 if ((handlers_sets & SET_WCHG_HANDLERS) == 0) {
538 /* emulate usage handler to set handler and call yours work */
539 win_changed(-SIGWINCH);
540 handlers_sets |= SET_WCHG_HANDLERS;
543 if ((handlers_sets & SET_ATEXIT) == 0) {
544 #ifdef CONFIG_FEATURE_GETUSERNAME_AND_HOMEDIR
545 struct passwd *entry;
548 entry = getpwuid(my_euid);
550 user_buf = bb_xstrdup(entry->pw_name);
551 home_pwd_buf = bb_xstrdup(entry->pw_dir);
555 #ifdef CONFIG_FEATURE_COMMAND_TAB_COMPLETION
557 #ifndef CONFIG_FEATURE_GETUSERNAME_AND_HOMEDIR
562 #endif /* CONFIG_FEATURE_COMMAND_TAB_COMPLETION */
563 handlers_sets |= SET_ATEXIT;
564 atexit(cmdedit_reset_term); /* be sure to do this only once */
568 #ifdef CONFIG_FEATURE_COMMAND_TAB_COMPLETION
570 static char **matches;
571 static int num_matches;
572 static char *add_char_to_match;
574 static void add_match(char *matched, int add_char)
576 int nm = num_matches;
579 matches = xrealloc(matches, nm1 * sizeof(char *));
580 add_char_to_match = xrealloc(add_char_to_match, nm1);
581 matches[nm] = matched;
582 add_char_to_match[nm] = (char)add_char;
586 static int is_execute(const struct stat *st)
588 if ((!my_euid && (st->st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))) ||
589 (my_uid == st->st_uid && (st->st_mode & S_IXUSR)) ||
590 (my_gid == st->st_gid && (st->st_mode & S_IXGRP)) ||
591 (st->st_mode & S_IXOTH)) return TRUE;
595 #ifdef CONFIG_FEATURE_COMMAND_USERNAME_COMPLETION
597 static void username_tab_completion(char *ud, char *with_shash_flg)
599 struct passwd *entry;
602 ud++; /* ~user/... to user/... */
603 userlen = strlen(ud);
605 if (with_shash_flg) { /* "~/..." or "~user/..." */
606 char *sav_ud = ud - 1;
610 if (*ud == '/') { /* "~/..." */
614 temp = strchr(ud, '/');
615 *temp = 0; /* ~user\0 */
616 entry = getpwnam(ud);
617 *temp = '/'; /* restore ~user/... */
620 home = entry->pw_dir;
623 if ((userlen + strlen(home) + 1) < BUFSIZ) {
624 char temp2[BUFSIZ]; /* argument size */
627 sprintf(temp2, "%s%s", home, ud);
628 strcpy(sav_ud, temp2);
635 while ((entry = getpwent()) != NULL) {
636 /* Null usernames should result in all users as possible completions. */
637 if ( /*!userlen || */ !strncmp(ud, entry->pw_name, userlen)) {
638 add_match(bb_xasprintf("~%s", entry->pw_name), '/');
645 #endif /* CONFIG_FEATURE_COMMAND_USERNAME_COMPLETION */
654 const char *cmdedit_path_lookup;
656 #define cmdedit_path_lookup getenv("PATH")
659 static int path_parse(char ***p, int flags)
665 /* if not setenv PATH variable, to search cur dir "." */
666 if (flags != FIND_EXE_ONLY || (pth = cmdedit_path_lookup) == 0 ||
667 /* PATH=<empty> or PATH=:<empty> */
668 *pth == 0 || (*pth == ':' && *(pth + 1) == 0)) {
676 npth++; /* count words is + 1 count ':' */
677 tmp = strchr(tmp, ':');
680 break; /* :<empty> */
685 *p = xmalloc(npth * sizeof(char *));
688 (*p)[0] = bb_xstrdup(tmp);
689 npth = 1; /* count words is + 1 count ':' */
692 tmp = strchr(tmp, ':');
694 (*p)[0][(tmp - pth)] = 0; /* ':' -> '\0' */
696 break; /* :<empty> */
699 (*p)[npth++] = &(*p)[0][(tmp - pth)]; /* p[next]=p[0][&'\0'+1] */
705 static char *add_quote_for_spec_chars(char *found, int add)
708 char *s = xmalloc((strlen(found) + 1) * 2);
711 if (strchr(" `\"#$%^&*()=+{}[]:;\'|\\<>", *found))
721 static void exe_n_cwd_tab_completion(char *command, int type)
728 char **paths = path1;
732 char *pfind = strrchr(command, '/');
737 /* no dir, if flags==EXE_ONLY - get paths, else "." */
738 npaths = path_parse(&paths, type);
742 /* save for change */
743 strcpy(dirbuf, command);
745 dirbuf[(pfind - command) + 1] = 0;
746 #ifdef CONFIG_FEATURE_COMMAND_USERNAME_COMPLETION
747 if (dirbuf[0] == '~') /* ~/... or ~user/... */
748 username_tab_completion(dirbuf, dirbuf);
750 /* "strip" dirname in command */
754 npaths = 1; /* only 1 dir */
757 for (i = 0; i < npaths; i++) {
759 dir = opendir(paths[i]);
760 if (!dir) /* Don't print an error */
763 while ((next = readdir(dir)) != NULL) {
764 char *str_found = next->d_name;
768 if (strncmp(str_found, pfind, strlen(pfind)))
770 /* not see .name without .match */
771 if (*str_found == '.' && *pfind == 0) {
772 if (*paths[i] == '/' && paths[i][1] == 0
773 && str_found[1] == 0) str_found = ""; /* only "/" */
777 found = concat_path_file(paths[i], str_found);
778 /* hmm, remover in progress? */
779 if (stat(found, &st) < 0)
781 /* find with dirs ? */
782 if (paths[i] != dirbuf)
783 strcpy(found, next->d_name); /* only name */
784 if (S_ISDIR(st.st_mode)) {
785 /* name is directory */
786 char *e = found + strlen(found) - 1;
792 /* not put found file if search only dirs for cd */
793 if (type == FIND_DIR_ONLY)
795 if (type == FIND_FILE_ONLY ||
796 (type == FIND_EXE_ONLY && is_execute(&st)))
799 /* Add it to the list */
800 add_match(found, add_chr);
807 if (paths != path1) {
808 free(paths[0]); /* allocated memory only in first member */
814 #define QUOT (UCHAR_MAX+1)
816 #define collapse_pos(is, in) { \
817 memmove(int_buf+(is), int_buf+(in), (BUFSIZ+1-(is)-(in))*sizeof(int)); \
818 memmove(pos_buf+(is), pos_buf+(in), (BUFSIZ+1-(is)-(in))*sizeof(int)); }
820 static int find_match(char *matchBuf, int *len_with_quotes)
825 int int_buf[BUFSIZ + 1];
826 int pos_buf[BUFSIZ + 1];
828 /* set to integer dimension characters and own positions */
830 int_buf[i] = (int) ((unsigned char) matchBuf[i]);
831 if (int_buf[i] == 0) {
832 pos_buf[i] = -1; /* indicator end line */
838 /* mask \+symbol and convert '\t' to ' ' */
839 for (i = j = 0; matchBuf[i]; i++, j++)
840 if (matchBuf[i] == '\\') {
841 collapse_pos(j, j + 1);
844 #ifdef CONFIG_FEATURE_NONPRINTABLE_INVERSE_PUT
845 if (matchBuf[i] == '\t') /* algorithm equivalent */
846 int_buf[j] = ' ' | QUOT;
849 #ifdef CONFIG_FEATURE_NONPRINTABLE_INVERSE_PUT
850 else if (matchBuf[i] == '\t')
854 /* mask "symbols" or 'symbols' */
856 for (i = 0; int_buf[i]; i++) {
858 if (c == '\'' || c == '"') {
867 } else if (c2 != 0 && c != '$')
871 /* skip commands with arguments if line have commands delimiters */
872 /* ';' ';;' '&' '|' '&&' '||' but `>&' `<&' `>|' */
873 for (i = 0; int_buf[i]; i++) {
876 j = i ? int_buf[i - 1] : -1;
878 if (c == ';' || c == '&' || c == '|') {
879 command_mode = 1 + (c == c2);
881 if (j == '>' || j == '<')
883 } else if (c == '|' && j == '>')
887 collapse_pos(0, i + command_mode);
888 i = -1; /* hack incremet */
891 /* collapse `command...` */
892 for (i = 0; int_buf[i]; i++)
893 if (int_buf[i] == '`') {
894 for (j = i + 1; int_buf[j]; j++)
895 if (int_buf[j] == '`') {
896 collapse_pos(i, j + 1);
901 /* not found close ` - command mode, collapse all previous */
902 collapse_pos(0, i + 1);
905 i--; /* hack incremet */
908 /* collapse (command...(command...)...) or {command...{command...}...} */
909 c = 0; /* "recursive" level */
911 for (i = 0; int_buf[i]; i++)
912 if (int_buf[i] == '(' || int_buf[i] == '{') {
913 if (int_buf[i] == '(')
917 collapse_pos(0, i + 1);
918 i = -1; /* hack incremet */
920 for (i = 0; pos_buf[i] >= 0 && (c > 0 || c2 > 0); i++)
921 if ((int_buf[i] == ')' && c > 0) || (int_buf[i] == '}' && c2 > 0)) {
922 if (int_buf[i] == ')')
926 collapse_pos(0, i + 1);
927 i = -1; /* hack incremet */
930 /* skip first not quote space */
931 for (i = 0; int_buf[i]; i++)
932 if (int_buf[i] != ' ')
937 /* set find mode for completion */
938 command_mode = FIND_EXE_ONLY;
939 for (i = 0; int_buf[i]; i++)
940 if (int_buf[i] == ' ' || int_buf[i] == '<' || int_buf[i] == '>') {
941 if (int_buf[i] == ' ' && command_mode == FIND_EXE_ONLY
942 && matchBuf[pos_buf[0]]=='c'
943 && matchBuf[pos_buf[1]]=='d' )
944 command_mode = FIND_DIR_ONLY;
946 command_mode = FIND_FILE_ONLY;
951 for (i = 0; int_buf[i]; i++);
953 for (--i; i >= 0; i--) {
955 if (c == ' ' || c == '<' || c == '>' || c == '|' || c == '&') {
956 collapse_pos(0, i + 1);
960 /* skip first not quoted '\'' or '"' */
961 for (i = 0; int_buf[i] == '\'' || int_buf[i] == '"'; i++);
962 /* collapse quote or unquote // or /~ */
963 while ((int_buf[i] & ~QUOT) == '/' &&
964 ((int_buf[i + 1] & ~QUOT) == '/'
965 || (int_buf[i + 1] & ~QUOT) == '~')) {
969 /* set only match and destroy quotes */
971 for (c = 0; pos_buf[i] >= 0; i++) {
972 matchBuf[c++] = matchBuf[pos_buf[i]];
976 /* old lenght matchBuf with quotes symbols */
977 *len_with_quotes = j ? j - pos_buf[0] : 0;
983 display by column original ideas from ls applet,
984 very optimize by my :)
986 static void showfiles(void)
989 int column_width = 0;
990 int nfiles = num_matches;
995 /* find the longest file name- use that as the column width */
996 for (row = 0; row < nrows; row++) {
997 l = strlen(matches[row]);
998 if(add_char_to_match[row])
1000 if (column_width < l)
1003 column_width += 2; /* min space for columns */
1004 ncols = cmdedit_termw / column_width;
1009 nrows++; /* round up fractionals */
1014 for (row = 0; row < nrows; row++) {
1019 for(nc = 1; nc < ncols && n+nrows < nfiles; n += nrows, nc++) {
1020 str_add_chr[0] = add_char_to_match[n];
1021 acol = str_add_chr[0] ? column_width - 1 : column_width;
1022 printf("%s%s", matches[n], str_add_chr);
1023 l = strlen(matches[n]);
1029 str_add_chr[0] = add_char_to_match[n];
1030 printf("%s%s\n", matches[n], str_add_chr);
1035 static void input_tab(int *lastWasTab)
1037 /* Do TAB completion */
1038 if (lastWasTab == 0) { /* free all memory */
1040 while (num_matches > 0)
1041 free(matches[--num_matches]);
1043 matches = (char **) NULL;
1044 free(add_char_to_match);
1045 add_char_to_match = NULL;
1049 if (! *lastWasTab) {
1053 char matchBuf[BUFSIZ];
1057 *lastWasTab = TRUE; /* flop trigger */
1059 /* Make a local copy of the string -- up
1060 * to the position of the cursor */
1061 tmp = strncpy(matchBuf, command_ps, cursor);
1064 find_type = find_match(matchBuf, &recalc_pos);
1066 /* Free up any memory already allocated */
1069 #ifdef CONFIG_FEATURE_COMMAND_USERNAME_COMPLETION
1070 /* If the word starts with `~' and there is no slash in the word,
1071 * then try completing this word as a username. */
1073 if (matchBuf[0] == '~' && strchr(matchBuf, '/') == 0)
1074 username_tab_completion(matchBuf, NULL);
1077 /* Try to match any executable in our path and everything
1078 * in the current working directory that matches. */
1079 exe_n_cwd_tab_completion(matchBuf, find_type);
1080 /* Remove duplicate found and sort */
1085 for(i=0; i<(n-1); i++)
1086 for(j=i+1; j<n; j++)
1087 if(matches[i]!=NULL && matches[j]!=NULL) {
1088 srt = strcmp(matches[i], matches[j]);
1092 } else if(srt > 0) {
1094 matches[i] = matches[j];
1096 srt = add_char_to_match[i];
1097 add_char_to_match[i] = add_char_to_match[j];
1098 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 = bb_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] = bb_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++] = bb_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 *bb_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");