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 static char delbuf[BUFSIZ]; /* a place to store deleted characters */
446 static char *delp = delbuf;
447 static int newdelflag; /* whether delbuf should be reused yet */
450 /* Delete the char in front of the cursor, optionally saving it
451 * for later putback */
452 static void input_delete(int save)
459 #ifdef CONFIG_FEATURE_COMMAND_EDITING_VI
465 if (delp - delbuf < BUFSIZ)
466 *delp++ = command_ps[j];
470 strcpy(command_ps + j, command_ps + j + 1);
472 input_end(); /* rewrite new line */
473 cmdedit_set_out_char(0); /* destroy end char */
474 input_backward(cursor - j); /* back to old pos cursor */
477 #ifdef CONFIG_FEATURE_COMMAND_EDITING_VI
478 static void put(void)
480 int ocursor, j = delp - delbuf;
484 /* open hole and then fill it */
485 memmove(command_ps + cursor + j, command_ps + cursor, len - cursor + 1);
486 strncpy(command_ps + cursor, delbuf, j);
488 input_end(); /* rewrite new line */
489 input_backward(cursor-ocursor-j+1); /* at end of new text */
493 /* Delete the char in back of the cursor */
494 static void input_backspace(void)
503 /* Move forward one character */
504 static void input_forward(void)
507 cmdedit_set_out_char(command_ps[cursor + 1]);
510 static void cmdedit_setwidth(int w, int redraw_flg)
512 cmdedit_termw = cmdedit_prmt_len + 2;
513 if (w <= cmdedit_termw) {
514 cmdedit_termw = cmdedit_termw % w;
516 if (w > cmdedit_termw) {
520 /* new y for current cursor */
521 int new_y = (cursor + cmdedit_prmt_len) / w;
524 redraw((new_y >= cmdedit_y ? new_y : cmdedit_y), len - cursor);
530 static void cmdedit_init(void)
532 cmdedit_prmt_len = 0;
533 if ((handlers_sets & SET_WCHG_HANDLERS) == 0) {
534 /* emulate usage handler to set handler and call yours work */
535 win_changed(-SIGWINCH);
536 handlers_sets |= SET_WCHG_HANDLERS;
539 if ((handlers_sets & SET_ATEXIT) == 0) {
540 #ifdef CONFIG_FEATURE_GETUSERNAME_AND_HOMEDIR
541 struct passwd *entry;
544 entry = getpwuid(my_euid);
546 user_buf = bb_xstrdup(entry->pw_name);
547 home_pwd_buf = bb_xstrdup(entry->pw_dir);
551 #ifdef CONFIG_FEATURE_COMMAND_TAB_COMPLETION
553 #ifndef CONFIG_FEATURE_GETUSERNAME_AND_HOMEDIR
558 #endif /* CONFIG_FEATURE_COMMAND_TAB_COMPLETION */
559 handlers_sets |= SET_ATEXIT;
560 atexit(cmdedit_reset_term); /* be sure to do this only once */
564 #ifdef CONFIG_FEATURE_COMMAND_TAB_COMPLETION
566 static int is_execute(const struct stat *st)
568 if ((!my_euid && (st->st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))) ||
569 (my_uid == st->st_uid && (st->st_mode & S_IXUSR)) ||
570 (my_gid == st->st_gid && (st->st_mode & S_IXGRP)) ||
571 (st->st_mode & S_IXOTH)) return TRUE;
575 #ifdef CONFIG_FEATURE_COMMAND_USERNAME_COMPLETION
577 static char **username_tab_completion(char *ud, int *num_matches)
579 struct passwd *entry;
584 ud++; /* ~user/... to user/... */
585 userlen = strlen(ud);
587 if (num_matches == 0) { /* "~/..." or "~user/..." */
588 char *sav_ud = ud - 1;
591 if (*ud == '/') { /* "~/..." */
595 temp = strchr(ud, '/');
596 *temp = 0; /* ~user\0 */
597 entry = getpwnam(ud);
598 *temp = '/'; /* restore ~user/... */
601 home = entry->pw_dir;
604 if ((userlen + strlen(home) + 1) < BUFSIZ) {
605 char temp2[BUFSIZ]; /* argument size */
608 sprintf(temp2, "%s%s", home, ud);
609 strcpy(sav_ud, temp2);
612 return 0; /* void, result save to argument :-) */
615 char **matches = (char **) NULL;
620 while ((entry = getpwent()) != NULL) {
621 /* Null usernames should result in all users as possible completions. */
622 if ( /*!userlen || */ !strncmp(ud, entry->pw_name, userlen)) {
624 temp = bb_xasprintf("~%s/", entry->pw_name);
625 matches = xrealloc(matches, (nm + 1) * sizeof(char *));
627 matches[nm++] = temp;
636 #endif /* CONFIG_FEATURE_COMMAND_USERNAME_COMPLETION */
645 const char *cmdedit_path_lookup;
647 #define cmdedit_path_lookup getenv("PATH")
650 static int path_parse(char ***p, int flags)
656 /* if not setenv PATH variable, to search cur dir "." */
657 if (flags != FIND_EXE_ONLY || (pth = cmdedit_path_lookup) == 0 ||
658 /* PATH=<empty> or PATH=:<empty> */
659 *pth == 0 || (*pth == ':' && *(pth + 1) == 0)) {
667 npth++; /* count words is + 1 count ':' */
668 tmp = strchr(tmp, ':');
671 break; /* :<empty> */
676 *p = xmalloc(npth * sizeof(char *));
679 (*p)[0] = bb_xstrdup(tmp);
680 npth = 1; /* count words is + 1 count ':' */
683 tmp = strchr(tmp, ':');
685 (*p)[0][(tmp - pth)] = 0; /* ':' -> '\0' */
687 break; /* :<empty> */
690 (*p)[npth++] = &(*p)[0][(tmp - pth)]; /* p[next]=p[0][&'\0'+1] */
696 static char *add_quote_for_spec_chars(char *found)
699 char *s = xmalloc((strlen(found) + 1) * 2);
702 if (strchr(" `\"#$%^&*()=+{}[]:;\'|\\<>", *found))
710 static char **exe_n_cwd_tab_completion(char *command, int *num_matches,
718 int nm = *num_matches;
721 char **paths = path1;
725 char *pfind = strrchr(command, '/');
730 /* no dir, if flags==EXE_ONLY - get paths, else "." */
731 npaths = path_parse(&paths, type);
735 /* save for change */
736 strcpy(dirbuf, command);
738 dirbuf[(pfind - command) + 1] = 0;
739 #ifdef CONFIG_FEATURE_COMMAND_USERNAME_COMPLETION
740 if (dirbuf[0] == '~') /* ~/... or ~user/... */
741 username_tab_completion(dirbuf, 0);
743 /* "strip" dirname in command */
747 npaths = 1; /* only 1 dir */
750 for (i = 0; i < npaths; i++) {
752 dir = opendir(paths[i]);
753 if (!dir) /* Don't print an error */
756 while ((next = readdir(dir)) != NULL) {
757 char *str_found = next->d_name;
760 if (strncmp(str_found, pfind, strlen(pfind)))
762 /* not see .name without .match */
763 if (*str_found == '.' && *pfind == 0) {
764 if (*paths[i] == '/' && paths[i][1] == 0
765 && str_found[1] == 0) str_found = ""; /* only "/" */
769 found = concat_path_file(paths[i], str_found);
770 /* hmm, remover in progress? */
771 if (stat(found, &st) < 0)
773 /* find with dirs ? */
774 if (paths[i] != dirbuf)
775 strcpy(found, next->d_name); /* only name */
776 if (S_ISDIR(st.st_mode)) {
777 /* name is directory */
779 found = concat_path_file(found, "");
781 str_found = add_quote_for_spec_chars(found);
783 /* not put found file if search only dirs for cd */
784 if (type == FIND_DIR_ONLY)
786 str_found = add_quote_for_spec_chars(found);
787 if (type == FIND_FILE_ONLY ||
788 (type == FIND_EXE_ONLY && is_execute(&st)))
789 strcat(str_found, " ");
791 /* Add it to the list */
792 matches = xrealloc(matches, (nm + 1) * sizeof(char *));
794 matches[nm++] = str_found;
800 if (paths != path1) {
801 free(paths[0]); /* allocated memory only in first member */
808 static int match_compare(const void *a, const void *b)
810 return strcmp(*(char **) a, *(char **) b);
815 #define QUOT (UCHAR_MAX+1)
817 #define collapse_pos(is, in) { \
818 memmove(int_buf+(is), int_buf+(in), (BUFSIZ+1-(is)-(in))*sizeof(int)); \
819 memmove(pos_buf+(is), pos_buf+(in), (BUFSIZ+1-(is)-(in))*sizeof(int)); }
821 static int find_match(char *matchBuf, int *len_with_quotes)
826 int int_buf[BUFSIZ + 1];
827 int pos_buf[BUFSIZ + 1];
829 /* set to integer dimension characters and own positions */
831 int_buf[i] = (int) ((unsigned char) matchBuf[i]);
832 if (int_buf[i] == 0) {
833 pos_buf[i] = -1; /* indicator end line */
839 /* mask \+symbol and convert '\t' to ' ' */
840 for (i = j = 0; matchBuf[i]; i++, j++)
841 if (matchBuf[i] == '\\') {
842 collapse_pos(j, j + 1);
845 #ifdef CONFIG_FEATURE_NONPRINTABLE_INVERSE_PUT
846 if (matchBuf[i] == '\t') /* algorithm equivalent */
847 int_buf[j] = ' ' | QUOT;
850 #ifdef CONFIG_FEATURE_NONPRINTABLE_INVERSE_PUT
851 else if (matchBuf[i] == '\t')
855 /* mask "symbols" or 'symbols' */
857 for (i = 0; int_buf[i]; i++) {
859 if (c == '\'' || c == '"') {
868 } else if (c2 != 0 && c != '$')
872 /* skip commands with arguments if line have commands delimiters */
873 /* ';' ';;' '&' '|' '&&' '||' but `>&' `<&' `>|' */
874 for (i = 0; int_buf[i]; i++) {
877 j = i ? int_buf[i - 1] : -1;
879 if (c == ';' || c == '&' || c == '|') {
880 command_mode = 1 + (c == c2);
882 if (j == '>' || j == '<')
884 } else if (c == '|' && j == '>')
888 collapse_pos(0, i + command_mode);
889 i = -1; /* hack incremet */
892 /* collapse `command...` */
893 for (i = 0; int_buf[i]; i++)
894 if (int_buf[i] == '`') {
895 for (j = i + 1; int_buf[j]; j++)
896 if (int_buf[j] == '`') {
897 collapse_pos(i, j + 1);
902 /* not found close ` - command mode, collapse all previous */
903 collapse_pos(0, i + 1);
906 i--; /* hack incremet */
909 /* collapse (command...(command...)...) or {command...{command...}...} */
910 c = 0; /* "recursive" level */
912 for (i = 0; int_buf[i]; i++)
913 if (int_buf[i] == '(' || int_buf[i] == '{') {
914 if (int_buf[i] == '(')
918 collapse_pos(0, i + 1);
919 i = -1; /* hack incremet */
921 for (i = 0; pos_buf[i] >= 0 && (c > 0 || c2 > 0); i++)
922 if ((int_buf[i] == ')' && c > 0) || (int_buf[i] == '}' && c2 > 0)) {
923 if (int_buf[i] == ')')
927 collapse_pos(0, i + 1);
928 i = -1; /* hack incremet */
931 /* skip first not quote space */
932 for (i = 0; int_buf[i]; i++)
933 if (int_buf[i] != ' ')
938 /* set find mode for completion */
939 command_mode = FIND_EXE_ONLY;
940 for (i = 0; int_buf[i]; i++)
941 if (int_buf[i] == ' ' || int_buf[i] == '<' || int_buf[i] == '>') {
942 if (int_buf[i] == ' ' && command_mode == FIND_EXE_ONLY
943 && matchBuf[pos_buf[0]]=='c'
944 && matchBuf[pos_buf[1]]=='d' )
945 command_mode = FIND_DIR_ONLY;
947 command_mode = FIND_FILE_ONLY;
952 for (i = 0; int_buf[i]; i++);
954 for (--i; i >= 0; i--) {
956 if (c == ' ' || c == '<' || c == '>' || c == '|' || c == '&') {
957 collapse_pos(0, i + 1);
961 /* skip first not quoted '\'' or '"' */
962 for (i = 0; int_buf[i] == '\'' || int_buf[i] == '"'; i++);
963 /* collapse quote or unquote // or /~ */
964 while ((int_buf[i] & ~QUOT) == '/' &&
965 ((int_buf[i + 1] & ~QUOT) == '/'
966 || (int_buf[i + 1] & ~QUOT) == '~')) {
970 /* set only match and destroy quotes */
972 for (c = 0; pos_buf[i] >= 0; i++) {
973 matchBuf[c++] = matchBuf[pos_buf[i]];
977 /* old lenght matchBuf with quotes symbols */
978 *len_with_quotes = j ? j - pos_buf[0] : 0;
984 display by column original ideas from ls applet,
985 very optimize by my :)
987 static void showfiles(char **matches, int nfiles)
990 int column_width = 0;
993 /* find the longest file name- use that as the column width */
994 for (row = 0; row < nrows; row++) {
995 int l = strlen(matches[row]);
997 if (column_width < l)
1000 column_width += 2; /* min space for columns */
1001 ncols = cmdedit_termw / column_width;
1006 nrows++; /* round up fractionals */
1007 column_width = -column_width; /* for printf("%-Ns", ...); */
1011 for (row = 0; row < nrows; row++) {
1015 for(nc = 1; nc < ncols && n+nrows < nfiles; n += nrows, nc++)
1016 printf("%*s", column_width, matches[n]);
1017 printf("%s\n", matches[n]);
1022 static void input_tab(int *lastWasTab)
1024 /* Do TAB completion */
1025 static int num_matches;
1026 static char **matches;
1028 if (lastWasTab == 0) { /* free all memory */
1030 while (num_matches > 0)
1031 free(matches[--num_matches]);
1033 matches = (char **) NULL;
1037 if (! *lastWasTab) {
1041 char matchBuf[BUFSIZ];
1045 *lastWasTab = TRUE; /* flop trigger */
1047 /* Make a local copy of the string -- up
1048 * to the position of the cursor */
1049 tmp = strncpy(matchBuf, command_ps, cursor);
1052 find_type = find_match(matchBuf, &recalc_pos);
1054 /* Free up any memory already allocated */
1057 #ifdef CONFIG_FEATURE_COMMAND_USERNAME_COMPLETION
1058 /* If the word starts with `~' and there is no slash in the word,
1059 * then try completing this word as a username. */
1061 if (matchBuf[0] == '~' && strchr(matchBuf, '/') == 0)
1062 matches = username_tab_completion(matchBuf, &num_matches);
1064 /* Try to match any executable in our path and everything
1065 * in the current working directory that matches. */
1068 exe_n_cwd_tab_completion(matchBuf,
1069 &num_matches, find_type);
1070 /* Remove duplicate found */
1074 for(i=0; i<(num_matches-1); i++)
1075 for(j=i+1; j<num_matches; j++)
1076 if(matches[i]!=0 && matches[j]!=0 &&
1077 strcmp(matches[i], matches[j])==0) {
1085 if(!strcmp(matches[i], "./"))
1087 else if(!strcmp(matches[i], "../"))
1089 matches[num_matches++]=matches[i];
1092 /* Did we find exactly one match? */
1093 if (!matches || num_matches > 1) {
1098 return; /* not found */
1100 qsort(matches, num_matches, sizeof(char *), match_compare);
1102 /* find minimal match */
1103 tmp = bb_xstrdup(matches[0]);
1104 for (tmp1 = tmp; *tmp1; tmp1++)
1105 for (len_found = 1; len_found < num_matches; len_found++)
1106 if (matches[len_found][(tmp1 - tmp)] != *tmp1) {
1110 if (*tmp == 0) { /* have unique */
1114 } else { /* one match */
1116 /* for next completion current found */
1117 *lastWasTab = FALSE;
1120 len_found = strlen(tmp);
1121 /* have space to placed match? */
1122 if ((len_found - strlen(matchBuf) + len) < BUFSIZ) {
1124 /* before word for match */
1125 command_ps[cursor - recalc_pos] = 0;
1126 /* save tail line */
1127 strcpy(matchBuf, command_ps + cursor);
1129 strcat(command_ps, tmp);
1131 strcat(command_ps, matchBuf);
1132 /* back to begin word for match */
1133 input_backward(recalc_pos);
1135 recalc_pos = cursor + len_found;
1137 len = strlen(command_ps);
1138 /* write out the matched command */
1139 redraw(cmdedit_y, len - recalc_pos);
1141 if (tmp != matches[0])
1144 /* Ok -- the last char was a TAB. Since they
1145 * just hit TAB again, print a list of all the
1146 * available choices... */
1147 if (matches && num_matches > 0) {
1148 int sav_cursor = cursor; /* change goto_new_line() */
1150 /* Go to the next line */
1152 showfiles(matches, num_matches);
1153 redraw(0, len - sav_cursor);
1157 #endif /* CONFIG_FEATURE_COMMAND_TAB_COMPLETION */
1159 #if MAX_HISTORY >= 1
1160 static void get_previous_history(void)
1162 if(command_ps[0] != 0 || history[cur_history] == 0) {
1163 free(history[cur_history]);
1164 history[cur_history] = bb_xstrdup(command_ps);
1169 static int get_next_history(void)
1171 int ch = cur_history;
1173 if (ch < n_history) {
1174 get_previous_history(); /* save the current history line */
1175 return (cur_history = ch+1);
1182 #ifdef CONFIG_FEATURE_COMMAND_SAVEHISTORY
1183 extern void load_history ( const char *fromfile )
1190 for(hi = n_history; hi > 0; ) {
1192 free ( history [hi] );
1195 if (( fp = fopen ( fromfile, "r" ))) {
1197 for ( hi = 0; hi < MAX_HISTORY; ) {
1198 char * hl = bb_get_chomped_line_from_file(fp);
1206 if(l == 0 || hl[0] == ' ') {
1210 history [hi++] = hl;
1214 cur_history = n_history = hi;
1217 extern void save_history ( const char *tofile )
1219 FILE *fp = fopen ( tofile, "w" );
1224 for ( i = 0; i < n_history; i++ ) {
1225 fprintf(fp, "%s\n", history [i]);
1241 * This function is used to grab a character buffer
1242 * from the input file descriptor and allows you to
1243 * a string with full command editing (sort of like
1246 * The following standard commands are not implemented:
1247 * ESC-b -- Move back one word
1248 * ESC-f -- Move forward one word
1249 * ESC-d -- Delete back one word
1250 * ESC-h -- Delete forward one word
1251 * CTL-t -- Transpose two characters
1253 * Minimalist vi-style command line editing available if configured.
1254 * vi mode implemented 2005 by Paul Fox <pgf@foxharp.boston.ma.us>
1258 #ifdef CONFIG_FEATURE_COMMAND_EDITING_VI
1261 void setvimode ( int viflag )
1267 vi_Word_motion(char *command, int eat)
1269 while (cursor < len && !isspace(command[cursor]))
1271 if (eat) while (cursor < len && isspace(command[cursor]))
1276 vi_word_motion(char *command, int eat)
1278 if (isalnum(command[cursor]) || command[cursor] == '_') {
1279 while (cursor < len &&
1280 (isalnum(command[cursor+1]) ||
1281 command[cursor+1] == '_'))
1283 } else if (ispunct(command[cursor])) {
1284 while (cursor < len &&
1285 (ispunct(command[cursor+1])))
1292 if (eat && cursor < len && isspace(command[cursor]))
1293 while (cursor < len && isspace(command[cursor]))
1298 vi_End_motion(char *command)
1301 while (cursor < len && isspace(command[cursor]))
1303 while (cursor < len-1 && !isspace(command[cursor+1]))
1308 vi_end_motion(char *command)
1310 if (cursor >= len-1)
1313 while (cursor < len-1 && isspace(command[cursor]))
1315 if (cursor >= len-1)
1317 if (isalnum(command[cursor]) || command[cursor] == '_') {
1318 while (cursor < len-1 &&
1319 (isalnum(command[cursor+1]) ||
1320 command[cursor+1] == '_'))
1322 } else if (ispunct(command[cursor])) {
1323 while (cursor < len-1 &&
1324 (ispunct(command[cursor+1])))
1330 vi_Back_motion(char *command)
1332 while (cursor > 0 && isspace(command[cursor-1]))
1334 while (cursor > 0 && !isspace(command[cursor-1]))
1339 vi_back_motion(char *command)
1344 while (cursor > 0 && isspace(command[cursor]))
1348 if (isalnum(command[cursor]) || command[cursor] == '_') {
1349 while (cursor > 0 &&
1350 (isalnum(command[cursor-1]) ||
1351 command[cursor-1] == '_'))
1353 } else if (ispunct(command[cursor])) {
1354 while (cursor > 0 &&
1355 (ispunct(command[cursor-1])))
1362 * the normal emacs mode and vi's insert mode are the same.
1363 * commands entered when in vi command mode ("escape mode") get
1364 * an extra bit added to distinguish them. this lets them share
1365 * much of the code in the big switch and while loop. i
1366 * experimented with an ugly macro to make the case labels for
1367 * these cases go away entirely when vi mode isn't configured, in
1368 * hopes of letting the jump tables get smaller:
1369 * #define vcase(caselabel) caselabel
1372 * case vcase(VICMD('0'):)
1373 * but it didn't seem to make any difference in code size,
1374 * and the macro-ized code was too ugly.
1377 #define VI_cmdbit 0x100
1378 #define VICMD(somecmd) ((somecmd)|VI_cmdbit)
1380 /* convert uppercase ascii to equivalent control char, for readability */
1381 #define CNTRL(uc_char) ((uc_char) - 0x40)
1384 int cmdedit_read_input(char *prompt, char command[BUFSIZ])
1388 int lastWasTab = FALSE;
1390 #ifdef CONFIG_FEATURE_COMMAND_EDITING_VI
1391 unsigned int ic, prevc;
1394 /* prepare before init handlers */
1395 cmdedit_y = 0; /* quasireal y, not true work if line > xt*yt */
1397 command_ps = command;
1399 getTermSettings(0, (void *) &initial_settings);
1400 memcpy(&new_settings, &initial_settings, sizeof(struct termios));
1401 new_settings.c_lflag &= ~ICANON; /* unbuffered input */
1402 /* Turn off echoing and CTRL-C, so we can trap it */
1403 new_settings.c_lflag &= ~(ECHO | ECHONL | ISIG);
1404 /* Hmm, in linux c_cc[] not parsed if set ~ICANON */
1405 new_settings.c_cc[VMIN] = 1;
1406 new_settings.c_cc[VTIME] = 0;
1407 /* Turn off CTRL-C, so we can trap it */
1408 # ifndef _POSIX_VDISABLE
1409 # define _POSIX_VDISABLE '\0'
1411 new_settings.c_cc[VINTR] = _POSIX_VDISABLE;
1414 setTermSettings(0, (void *) &new_settings);
1415 handlers_sets |= SET_RESET_TERM;
1417 /* Now initialize things */
1419 /* Print out the command prompt */
1420 parse_prompt(prompt);
1424 fflush(stdout); /* buffered out to fast */
1426 if (safe_read(0, &c, 1) < 1)
1427 /* if we can't read input then exit */
1428 goto prepare_to_die;
1430 #ifdef CONFIG_FEATURE_COMMAND_EDITING_VI
1450 /* Control-a -- Beginning of line */
1451 input_backward(cursor);
1457 /* Control-b -- Move back one character */
1461 case VICMD(CNTRL('C')):
1462 /* Control-c -- stop gathering input */
1471 break_out = -1; /* to control traps */
1475 /* Control-d -- Delete one character, or exit
1476 * if the len=0 and no chars to delete */
1480 #if !defined(CONFIG_ASH)
1483 /* cmdedit_reset_term() called in atexit */
1486 /* to control stopped jobs */
1487 len = break_out = -1;
1496 /* Control-e -- End of line */
1502 /* Control-f -- Move forward one character */
1507 /* Control-h and DEL */
1511 #ifdef CONFIG_FEATURE_COMMAND_TAB_COMPLETION
1512 input_tab(&lastWasTab);
1516 /* Control-k -- clear to end of line */
1517 *(command + cursor) = 0;
1522 case VICMD(CNTRL('L')):
1523 /* Control-l -- clear screen */
1525 redraw(0, len-cursor);
1527 #if MAX_HISTORY >= 1
1529 case VICMD(CNTRL('N')):
1531 /* Control-n -- Get next command in history */
1532 if (get_next_history())
1536 case VICMD(CNTRL('P')):
1538 /* Control-p -- Get previous command from history */
1539 if (cur_history > 0) {
1540 get_previous_history();
1548 case VICMD(CNTRL('U')):
1549 /* Control-U -- Clear line before cursor */
1551 strcpy(command, command + cursor);
1552 redraw(cmdedit_y, len -= cursor);
1556 case VICMD(CNTRL('W')):
1557 /* Control-W -- Remove the last word */
1558 while (cursor > 0 && isspace(command[cursor-1]))
1560 while (cursor > 0 &&!isspace(command[cursor-1]))
1563 #if CONFIG_FEATURE_COMMAND_EDITING_VI
1568 input_backward(cursor);
1589 vi_Word_motion(command, 1);
1592 vi_word_motion(command, 1);
1595 vi_End_motion(command);
1598 vi_end_motion(command);
1601 vi_Back_motion(command);
1604 vi_back_motion(command);
1620 if (safe_read(0, &c, 1) < 1)
1621 goto prepare_to_die;
1622 if (c == (prevc & 0xff)) {
1624 input_backward(cursor);
1634 case 'w': /* "dw", "cw" */
1635 vi_word_motion(command, vi_cmdmode);
1637 case 'W': /* 'dW', 'cW' */
1638 vi_Word_motion(command, vi_cmdmode);
1640 case 'e': /* 'de', 'ce' */
1641 vi_end_motion(command);
1644 case 'E': /* 'dE', 'cE' */
1645 vi_End_motion(command);
1650 input_backward(cursor - sc);
1651 while (nc-- > cursor)
1654 case 'b': /* "db", "cb" */
1655 case 'B': /* implemented as B */
1657 vi_back_motion(command);
1659 vi_Back_motion(command);
1660 while (sc-- > cursor)
1663 case ' ': /* "d ", "c " */
1666 case '$': /* "d$", "c$" */
1668 while (cursor < len)
1681 if (safe_read(0, &c, 1) < 1)
1682 goto prepare_to_die;
1686 *(command + cursor) = c;
1691 #endif /* CONFIG_FEATURE_COMMAND_EDITING_VI */
1693 #if CONFIG_FEATURE_COMMAND_EDITING_VI
1695 /* ESC: insert mode --> command mode */
1701 /* escape sequence follows */
1702 if (safe_read(0, &c, 1) < 1)
1703 goto prepare_to_die;
1704 /* different vt100 emulations */
1705 if (c == '[' || c == 'O') {
1708 if (safe_read(0, &c, 1) < 1)
1709 goto prepare_to_die;
1711 if (c >= '1' && c <= '9') {
1712 unsigned char dummy;
1714 if (safe_read(0, &dummy, 1) < 1)
1715 goto prepare_to_die;
1720 #ifdef CONFIG_FEATURE_COMMAND_TAB_COMPLETION
1721 case '\t': /* Alt-Tab */
1723 input_tab(&lastWasTab);
1726 #if MAX_HISTORY >= 1
1728 /* Up Arrow -- Get previous command from history */
1729 if (cur_history > 0) {
1730 get_previous_history();
1737 /* Down Arrow -- Get next command in history */
1738 if (!get_next_history())
1740 /* Rewrite the line with the selected history item */
1742 /* change command */
1743 len = strlen(strcpy(command, history[cur_history]));
1744 /* redraw and go to eol (bol, in vi */
1745 #if CONFIG_FEATURE_COMMAND_EDITING_VI
1746 redraw(cmdedit_y, vi_mode ? 9999:0);
1748 redraw(cmdedit_y, 0);
1753 /* Right Arrow -- Move forward one character */
1757 /* Left Arrow -- Move back one character */
1767 input_backward(cursor);
1781 default: /* If it's regular input, do the normal thing */
1782 #ifdef CONFIG_FEATURE_NONPRINTABLE_INVERSE_PUT
1783 /* Control-V -- Add non-printable symbol */
1784 if (c == CNTRL('V')) {
1785 if (safe_read(0, &c, 1) < 1)
1786 goto prepare_to_die;
1794 #if CONFIG_FEATURE_COMMAND_EDITING_VI
1795 if (vi_cmdmode) /* don't self-insert */
1798 if (!Isprint(c)) /* Skip non-printable characters */
1802 if (len >= (BUFSIZ - 2)) /* Need to leave space for enter */
1807 if (cursor == (len - 1)) { /* Append if at the end of the line */
1808 *(command + cursor) = c;
1809 *(command + cursor + 1) = 0;
1810 cmdedit_set_out_char(0);
1811 } else { /* Insert otherwise */
1814 memmove(command + sc + 1, command + sc, len - sc);
1815 *(command + sc) = c;
1817 /* rewrite from cursor */
1819 /* to prev x pos + 1 */
1820 input_backward(cursor - sc);
1825 if (break_out) /* Enter is the command terminator, no more input. */
1832 setTermSettings(0, (void *) &initial_settings);
1833 handlers_sets &= ~SET_RESET_TERM;
1835 #if MAX_HISTORY >= 1
1836 /* Handle command history log */
1837 /* cleanup may be saved current command line */
1838 if (len> 0) { /* no put empty line */
1841 free(history[MAX_HISTORY]);
1842 history[MAX_HISTORY] = 0;
1843 /* After max history, remove the oldest command */
1844 if (i >= MAX_HISTORY) {
1846 for(i = 0; i < (MAX_HISTORY-1); i++)
1847 history[i] = history[i+1];
1849 history[i++] = bb_xstrdup(command);
1852 #if defined(CONFIG_FEATURE_SH_FANCY_PROMPT)
1856 #else /* MAX_HISTORY < 1 */
1857 #if defined(CONFIG_FEATURE_SH_FANCY_PROMPT)
1858 if (len > 0) { /* no put empty line */
1862 #endif /* MAX_HISTORY >= 1 */
1863 if (break_out > 0) {
1864 command[len++] = '\n'; /* set '\n' */
1867 #if defined(CONFIG_FEATURE_CLEAN_UP) && defined(CONFIG_FEATURE_COMMAND_TAB_COMPLETION)
1868 input_tab(0); /* strong free */
1870 #if defined(CONFIG_FEATURE_SH_FANCY_PROMPT)
1871 free(cmdedit_prompt);
1873 cmdedit_reset_term();
1879 #endif /* CONFIG_FEATURE_COMMAND_EDITING */
1884 const char *bb_applet_name = "debug stuff usage";
1886 #ifdef CONFIG_FEATURE_NONPRINTABLE_INVERSE_PUT
1890 int main(int argc, char **argv)
1894 #if defined(CONFIG_FEATURE_SH_FANCY_PROMPT)
1895 "\\[\\033[32;1m\\]\\u@\\[\\x1b[33;1m\\]\\h:\
1896 \\[\\033[34;1m\\]\\w\\[\\033[35;1m\\] \
1897 \\!\\[\\e[36;1m\\]\\$ \\[\\E[0m\\]";
1902 #ifdef CONFIG_FEATURE_NONPRINTABLE_INVERSE_PUT
1903 setlocale(LC_ALL, "");
1907 l = cmdedit_read_input(prompt, buff);
1908 if(l > 0 && buff[l-1] == '\n') {
1910 printf("*** cmdedit_read_input() returned line =%s=\n", buff);
1915 printf("*** cmdedit_read_input() detect ^D\n");