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 /* Maximum length of the linked list for the command line history */
83 #ifndef CONFIG_FEATURE_COMMAND_HISTORY
84 #define MAX_HISTORY 15
86 #define MAX_HISTORY (CONFIG_FEATURE_COMMAND_HISTORY + 0)
90 static char *history[MAX_HISTORY+1]; /* history + current */
91 /* saved history lines */
93 /* current pointer to history line */
94 static int cur_history;
98 #define setTermSettings(fd,argp) tcsetattr(fd,TCSANOW,argp)
99 #define getTermSettings(fd,argp) tcgetattr(fd, argp);
101 /* Current termio and the previous termio before starting sh */
102 static struct termios initial_settings, new_settings;
106 volatile int cmdedit_termw = 80; /* actual terminal width */
108 volatile int handlers_sets = 0; /* Set next bites: */
111 SET_ATEXIT = 1, /* when atexit() has been called
112 and get euid,uid,gid to fast compare */
113 SET_WCHG_HANDLERS = 2, /* winchg signal handler */
114 SET_RESET_TERM = 4, /* if the terminal needs to be reset upon exit */
118 static int cmdedit_x; /* real x terminal position */
119 static int cmdedit_y; /* pseudoreal y terminal position */
120 static int cmdedit_prmt_len; /* lenght prompt without colores string */
122 static int cursor; /* required global for signal handler */
123 static int len; /* --- "" - - "" - -"- --""-- --""--- */
124 static char *command_ps; /* --- "" - - "" - -"- --""-- --""--- */
126 #ifndef CONFIG_FEATURE_SH_FANCY_PROMPT
129 char *cmdedit_prompt; /* --- "" - - "" - -"- --""-- --""--- */
131 #ifdef CONFIG_FEATURE_GETUSERNAME_AND_HOMEDIR
132 static char *user_buf = "";
133 static char *home_pwd_buf = "";
137 #ifdef CONFIG_FEATURE_SH_FANCY_PROMPT
138 static char *hostname_buf;
139 static int num_ok_lines = 1;
143 #ifdef CONFIG_FEATURE_COMMAND_TAB_COMPLETION
145 #ifndef CONFIG_FEATURE_GETUSERNAME_AND_HOMEDIR
152 #endif /* CONFIG_FEATURE_COMMAND_TAB_COMPLETION */
154 static void cmdedit_setwidth(int w, int redraw_flg);
156 static void win_changed(int nsig)
158 static sighandler_t previous_SIGWINCH_handler; /* for reset */
160 /* emulate || signal call */
161 if (nsig == -SIGWINCH || nsig == SIGWINCH) {
163 get_terminal_width_height(0, &width, NULL);
164 cmdedit_setwidth(width, nsig == SIGWINCH);
166 /* Unix not all standart in recall signal */
168 if (nsig == -SIGWINCH) /* save previous handler */
169 previous_SIGWINCH_handler = signal(SIGWINCH, win_changed);
170 else if (nsig == SIGWINCH) /* signaled called handler */
171 signal(SIGWINCH, win_changed); /* set for next call */
173 /* set previous handler */
174 signal(SIGWINCH, previous_SIGWINCH_handler); /* reset */
177 static void cmdedit_reset_term(void)
179 if ((handlers_sets & SET_RESET_TERM) != 0) {
180 /* sparc and other have broken termios support: use old termio handling. */
181 setTermSettings(STDIN_FILENO, (void *) &initial_settings);
182 handlers_sets &= ~SET_RESET_TERM;
184 if ((handlers_sets & SET_WCHG_HANDLERS) != 0) {
185 /* reset SIGWINCH handler to previous (default) */
187 handlers_sets &= ~SET_WCHG_HANDLERS;
193 /* special for recount position for scroll and remove terminal margin effect */
194 static void cmdedit_set_out_char(int next_char)
197 int c = (int)((unsigned char) command_ps[cursor]);
200 c = ' '; /* destroy end char? */
201 #ifdef CONFIG_FEATURE_NONPRINTABLE_INVERSE_PUT
202 if (!Isprint(c)) { /* Inverse put non-printable characters */
209 printf("\033[7m%c\033[0m", c);
212 if (initial_settings.c_lflag & ECHO) putchar(c);
213 if (++cmdedit_x >= cmdedit_termw) {
214 /* terminal is scrolled down */
220 /* destroy "(auto)margin" */
227 /* Move to end line. Bonus: rewrite line from cursor */
228 static void input_end(void)
231 cmdedit_set_out_char(0);
234 /* Go to the next line */
235 static void goto_new_line(void)
243 static void out1str(const char *s)
249 static void beep(void)
254 /* Move back one character */
255 /* special for slow terminal */
256 static void input_backward(int num)
260 cursor -= num; /* new cursor (in command, not terminal) */
262 if (cmdedit_x >= num) { /* no to up line */
268 printf("\033[%dD", num);
273 putchar('\r'); /* back to first terminal pos. */
274 num -= cmdedit_x; /* set previous backward */
276 count_y = 1 + num / cmdedit_termw;
277 printf("\033[%dA", count_y);
278 cmdedit_y -= count_y;
279 /* require forward after uping */
280 cmdedit_x = cmdedit_termw * count_y - num;
281 printf("\033[%dC", cmdedit_x); /* set term cursor */
285 static void put_prompt(void)
287 out1str(cmdedit_prompt);
288 cmdedit_x = cmdedit_prmt_len; /* count real x terminal position */
290 cmdedit_y = 0; /* new quasireal y */
293 #ifndef CONFIG_FEATURE_SH_FANCY_PROMPT
294 static void parse_prompt(const char *prmt_ptr)
296 cmdedit_prompt = prmt_ptr;
297 cmdedit_prmt_len = strlen(prmt_ptr);
301 static void parse_prompt(const char *prmt_ptr)
304 size_t cur_prmt_len = 0;
305 char flg_not_length = '[';
306 char *prmt_mem_ptr = xzalloc(1);
307 char *pwd_buf = xgetcwd(0);
308 char buf2[PATH_MAX + 1];
314 pwd_buf=(char *)bb_msg_unknown;
322 const char *cp = prmt_ptr;
325 c = bb_process_escape_sequence(&prmt_ptr);
331 #ifdef CONFIG_FEATURE_GETUSERNAME_AND_HOMEDIR
340 if (gethostname(pbuf, 255) < 0) {
343 char *s = strchr(pbuf, '.');
352 c = my_euid == 0 ? '#' : '$';
354 #ifdef CONFIG_FEATURE_GETUSERNAME_AND_HOMEDIR
357 l = strlen(home_pwd_buf);
358 if (home_pwd_buf[0] != 0 &&
359 strncmp(home_pwd_buf, pbuf, l) == 0 &&
360 (pbuf[l]=='/' || pbuf[l]=='\0') &&
361 strlen(pwd_buf+l)<PATH_MAX) {
364 strcpy(pbuf+1, pwd_buf+l);
370 cp = strrchr(pbuf,'/');
371 if ( (cp != NULL) && (cp != pbuf) )
375 snprintf(pbuf = buf2, sizeof(buf2), "%d", num_ok_lines);
377 case 'e': case 'E': /* \e \E = \033 */
381 for (l = 0; l < 3;) {
383 buf2[l++] = *prmt_ptr;
385 h = strtol(buf2, &pbuf, 16);
386 if (h > UCHAR_MAX || (pbuf - buf2) < l) {
393 c = (char)strtol(buf2, 0, 16);
399 if (c == flg_not_length) {
400 flg_not_length = flg_not_length == '[' ? ']' : '[';
409 cur_prmt_len = strlen(pbuf);
410 prmt_len += cur_prmt_len;
411 if (flg_not_length != ']')
412 cmdedit_prmt_len += cur_prmt_len;
413 prmt_mem_ptr = strcat(xrealloc(prmt_mem_ptr, prmt_len+1), pbuf);
415 if(pwd_buf!=(char *)bb_msg_unknown)
417 cmdedit_prompt = prmt_mem_ptr;
423 /* draw prompt, editor line, and clear tail */
424 static void redraw(int y, int back_cursor)
426 if (y > 0) /* up to start y */
427 printf("\033[%dA", y);
430 input_end(); /* rewrite */
431 printf("\033[J"); /* destroy tail after cursor */
432 input_backward(back_cursor);
435 #ifdef CONFIG_FEATURE_COMMAND_EDITING_VI
436 #define DELBUFSIZ 128
437 static char *delbuf; /* a (malloced) place to store deleted characters */
439 static char newdelflag; /* whether delbuf should be reused yet */
442 /* Delete the char in front of the cursor, optionally saving it
443 * for later putback */
444 static void input_delete(int save)
451 #ifdef CONFIG_FEATURE_COMMAND_EDITING_VI
455 delbuf = malloc(DELBUFSIZ);
456 /* safe if malloc fails */
460 if (delbuf && (delp - delbuf < DELBUFSIZ))
461 *delp++ = command_ps[j];
465 strcpy(command_ps + j, command_ps + j + 1);
467 input_end(); /* rewrite new line */
468 cmdedit_set_out_char(0); /* destroy end char */
469 input_backward(cursor - j); /* back to old pos cursor */
472 #ifdef CONFIG_FEATURE_COMMAND_EDITING_VI
473 static void put(void)
475 int ocursor, j = delp - delbuf;
479 /* open hole and then fill it */
480 memmove(command_ps + cursor + j, command_ps + cursor, len - cursor + 1);
481 strncpy(command_ps + cursor, delbuf, j);
483 input_end(); /* rewrite new line */
484 input_backward(cursor-ocursor-j+1); /* at end of new text */
488 /* Delete the char in back of the cursor */
489 static void input_backspace(void)
498 /* Move forward one character */
499 static void input_forward(void)
502 cmdedit_set_out_char(command_ps[cursor + 1]);
505 static void cmdedit_setwidth(int w, int redraw_flg)
507 cmdedit_termw = cmdedit_prmt_len + 2;
508 if (w <= cmdedit_termw) {
509 cmdedit_termw = cmdedit_termw % w;
511 if (w > cmdedit_termw) {
515 /* new y for current cursor */
516 int new_y = (cursor + cmdedit_prmt_len) / w;
519 redraw((new_y >= cmdedit_y ? new_y : cmdedit_y), len - cursor);
525 static void cmdedit_init(void)
527 cmdedit_prmt_len = 0;
528 if ((handlers_sets & SET_WCHG_HANDLERS) == 0) {
529 /* emulate usage handler to set handler and call yours work */
530 win_changed(-SIGWINCH);
531 handlers_sets |= SET_WCHG_HANDLERS;
534 if ((handlers_sets & SET_ATEXIT) == 0) {
535 #ifdef CONFIG_FEATURE_GETUSERNAME_AND_HOMEDIR
536 struct passwd *entry;
539 entry = getpwuid(my_euid);
541 user_buf = xstrdup(entry->pw_name);
542 home_pwd_buf = xstrdup(entry->pw_dir);
546 #ifdef CONFIG_FEATURE_COMMAND_TAB_COMPLETION
548 #ifndef CONFIG_FEATURE_GETUSERNAME_AND_HOMEDIR
553 #endif /* CONFIG_FEATURE_COMMAND_TAB_COMPLETION */
554 handlers_sets |= SET_ATEXIT;
555 atexit(cmdedit_reset_term); /* be sure to do this only once */
559 #ifdef CONFIG_FEATURE_COMMAND_TAB_COMPLETION
561 static char **matches;
562 static int num_matches;
563 static char *add_char_to_match;
565 static void add_match(char *matched, int add_char)
567 int nm = num_matches;
570 matches = xrealloc(matches, nm1 * sizeof(char *));
571 add_char_to_match = xrealloc(add_char_to_match, nm1);
572 matches[nm] = matched;
573 add_char_to_match[nm] = (char)add_char;
577 static int is_execute(const struct stat *st)
579 if ((!my_euid && (st->st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))) ||
580 (my_uid == st->st_uid && (st->st_mode & S_IXUSR)) ||
581 (my_gid == st->st_gid && (st->st_mode & S_IXGRP)) ||
582 (st->st_mode & S_IXOTH)) return TRUE;
586 #ifdef CONFIG_FEATURE_COMMAND_USERNAME_COMPLETION
588 static void username_tab_completion(char *ud, char *with_shash_flg)
590 struct passwd *entry;
593 ud++; /* ~user/... to user/... */
594 userlen = strlen(ud);
596 if (with_shash_flg) { /* "~/..." or "~user/..." */
597 char *sav_ud = ud - 1;
601 if (*ud == '/') { /* "~/..." */
605 temp = strchr(ud, '/');
606 *temp = 0; /* ~user\0 */
607 entry = getpwnam(ud);
608 *temp = '/'; /* restore ~user/... */
611 home = entry->pw_dir;
614 if ((userlen + strlen(home) + 1) < BUFSIZ) {
615 char temp2[BUFSIZ]; /* argument size */
618 sprintf(temp2, "%s%s", home, ud);
619 strcpy(sav_ud, temp2);
626 while ((entry = getpwent()) != NULL) {
627 /* Null usernames should result in all users as possible completions. */
628 if ( /*!userlen || */ !strncmp(ud, entry->pw_name, userlen)) {
629 add_match(xasprintf("~%s", entry->pw_name), '/');
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] = 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, int add)
699 char *s = xmalloc((strlen(found) + 1) * 2);
702 if (strchr(" `\"#$%^&*()=+{}[]:;\'|\\<>", *found))
712 static void exe_n_cwd_tab_completion(char *command, int type)
719 char **paths = path1;
723 char *pfind = strrchr(command, '/');
728 /* no dir, if flags==EXE_ONLY - get paths, else "." */
729 npaths = path_parse(&paths, type);
733 /* save for change */
734 strcpy(dirbuf, command);
736 dirbuf[(pfind - command) + 1] = 0;
737 #ifdef CONFIG_FEATURE_COMMAND_USERNAME_COMPLETION
738 if (dirbuf[0] == '~') /* ~/... or ~user/... */
739 username_tab_completion(dirbuf, dirbuf);
741 /* "strip" dirname in command */
745 npaths = 1; /* only 1 dir */
748 for (i = 0; i < npaths; i++) {
750 dir = opendir(paths[i]);
751 if (!dir) /* Don't print an error */
754 while ((next = readdir(dir)) != NULL) {
755 char *str_found = next->d_name;
759 if (strncmp(str_found, pfind, strlen(pfind)))
761 /* not see .name without .match */
762 if (*str_found == '.' && *pfind == 0) {
763 if (*paths[i] == '/' && paths[i][1] == 0
764 && str_found[1] == 0) str_found = ""; /* only "/" */
768 found = concat_path_file(paths[i], str_found);
769 /* hmm, remover in progress? */
770 if (stat(found, &st) < 0)
772 /* find with dirs ? */
773 if (paths[i] != dirbuf)
774 strcpy(found, next->d_name); /* only name */
775 if (S_ISDIR(st.st_mode)) {
776 /* name is directory */
777 char *e = found + strlen(found) - 1;
783 /* not put found file if search only dirs for cd */
784 if (type == FIND_DIR_ONLY)
786 if (type == FIND_FILE_ONLY ||
787 (type == FIND_EXE_ONLY && is_execute(&st)))
790 /* Add it to the list */
791 add_match(found, add_chr);
798 if (paths != path1) {
799 free(paths[0]); /* allocated memory only in first member */
805 #define QUOT (UCHAR_MAX+1)
807 #define collapse_pos(is, in) { \
808 memmove(int_buf+(is), int_buf+(in), (BUFSIZ+1-(is)-(in))*sizeof(int)); \
809 memmove(pos_buf+(is), pos_buf+(in), (BUFSIZ+1-(is)-(in))*sizeof(int)); }
811 static int find_match(char *matchBuf, int *len_with_quotes)
816 int int_buf[BUFSIZ + 1];
817 int pos_buf[BUFSIZ + 1];
819 /* set to integer dimension characters and own positions */
821 int_buf[i] = (int) ((unsigned char) matchBuf[i]);
822 if (int_buf[i] == 0) {
823 pos_buf[i] = -1; /* indicator end line */
829 /* mask \+symbol and convert '\t' to ' ' */
830 for (i = j = 0; matchBuf[i]; i++, j++)
831 if (matchBuf[i] == '\\') {
832 collapse_pos(j, j + 1);
835 #ifdef CONFIG_FEATURE_NONPRINTABLE_INVERSE_PUT
836 if (matchBuf[i] == '\t') /* algorithm equivalent */
837 int_buf[j] = ' ' | QUOT;
840 #ifdef CONFIG_FEATURE_NONPRINTABLE_INVERSE_PUT
841 else if (matchBuf[i] == '\t')
845 /* mask "symbols" or 'symbols' */
847 for (i = 0; int_buf[i]; i++) {
849 if (c == '\'' || c == '"') {
858 } else if (c2 != 0 && c != '$')
862 /* skip commands with arguments if line have commands delimiters */
863 /* ';' ';;' '&' '|' '&&' '||' but `>&' `<&' `>|' */
864 for (i = 0; int_buf[i]; i++) {
867 j = i ? int_buf[i - 1] : -1;
869 if (c == ';' || c == '&' || c == '|') {
870 command_mode = 1 + (c == c2);
872 if (j == '>' || j == '<')
874 } else if (c == '|' && j == '>')
878 collapse_pos(0, i + command_mode);
879 i = -1; /* hack incremet */
882 /* collapse `command...` */
883 for (i = 0; int_buf[i]; i++)
884 if (int_buf[i] == '`') {
885 for (j = i + 1; int_buf[j]; j++)
886 if (int_buf[j] == '`') {
887 collapse_pos(i, j + 1);
892 /* not found close ` - command mode, collapse all previous */
893 collapse_pos(0, i + 1);
896 i--; /* hack incremet */
899 /* collapse (command...(command...)...) or {command...{command...}...} */
900 c = 0; /* "recursive" level */
902 for (i = 0; int_buf[i]; i++)
903 if (int_buf[i] == '(' || int_buf[i] == '{') {
904 if (int_buf[i] == '(')
908 collapse_pos(0, i + 1);
909 i = -1; /* hack incremet */
911 for (i = 0; pos_buf[i] >= 0 && (c > 0 || c2 > 0); i++)
912 if ((int_buf[i] == ')' && c > 0) || (int_buf[i] == '}' && c2 > 0)) {
913 if (int_buf[i] == ')')
917 collapse_pos(0, i + 1);
918 i = -1; /* hack incremet */
921 /* skip first not quote space */
922 for (i = 0; int_buf[i]; i++)
923 if (int_buf[i] != ' ')
928 /* set find mode for completion */
929 command_mode = FIND_EXE_ONLY;
930 for (i = 0; int_buf[i]; i++)
931 if (int_buf[i] == ' ' || int_buf[i] == '<' || int_buf[i] == '>') {
932 if (int_buf[i] == ' ' && command_mode == FIND_EXE_ONLY
933 && matchBuf[pos_buf[0]]=='c'
934 && matchBuf[pos_buf[1]]=='d' )
935 command_mode = FIND_DIR_ONLY;
937 command_mode = FIND_FILE_ONLY;
942 for (i = 0; int_buf[i]; i++);
944 for (--i; i >= 0; i--) {
946 if (c == ' ' || c == '<' || c == '>' || c == '|' || c == '&') {
947 collapse_pos(0, i + 1);
951 /* skip first not quoted '\'' or '"' */
952 for (i = 0; int_buf[i] == '\'' || int_buf[i] == '"'; i++);
953 /* collapse quote or unquote // or /~ */
954 while ((int_buf[i] & ~QUOT) == '/' &&
955 ((int_buf[i + 1] & ~QUOT) == '/'
956 || (int_buf[i + 1] & ~QUOT) == '~')) {
960 /* set only match and destroy quotes */
962 for (c = 0; pos_buf[i] >= 0; i++) {
963 matchBuf[c++] = matchBuf[pos_buf[i]];
967 /* old lenght matchBuf with quotes symbols */
968 *len_with_quotes = j ? j - pos_buf[0] : 0;
974 display by column original ideas from ls applet,
975 very optimize by my :)
977 static void showfiles(void)
980 int column_width = 0;
981 int nfiles = num_matches;
986 /* find the longest file name- use that as the column width */
987 for (row = 0; row < nrows; row++) {
988 l = strlen(matches[row]);
989 if(add_char_to_match[row])
991 if (column_width < l)
994 column_width += 2; /* min space for columns */
995 ncols = cmdedit_termw / column_width;
1000 nrows++; /* round up fractionals */
1005 for (row = 0; row < nrows; row++) {
1010 for(nc = 1; nc < ncols && n+nrows < nfiles; n += nrows, nc++) {
1011 str_add_chr[0] = add_char_to_match[n];
1012 acol = str_add_chr[0] ? column_width - 1 : column_width;
1013 printf("%s%s", matches[n], str_add_chr);
1014 l = strlen(matches[n]);
1020 str_add_chr[0] = add_char_to_match[n];
1021 printf("%s%s\n", matches[n], str_add_chr);
1026 static void input_tab(int *lastWasTab)
1028 /* Do TAB completion */
1029 if (lastWasTab == 0) { /* free all memory */
1031 while (num_matches > 0)
1032 free(matches[--num_matches]);
1034 matches = (char **) NULL;
1035 free(add_char_to_match);
1036 add_char_to_match = NULL;
1040 if (! *lastWasTab) {
1044 char matchBuf[BUFSIZ];
1048 *lastWasTab = TRUE; /* flop trigger */
1050 /* Make a local copy of the string -- up
1051 * to the position of the cursor */
1052 tmp = strncpy(matchBuf, command_ps, cursor);
1055 find_type = find_match(matchBuf, &recalc_pos);
1057 /* Free up any memory already allocated */
1060 #ifdef CONFIG_FEATURE_COMMAND_USERNAME_COMPLETION
1061 /* If the word starts with `~' and there is no slash in the word,
1062 * then try completing this word as a username. */
1064 if (matchBuf[0] == '~' && strchr(matchBuf, '/') == 0)
1065 username_tab_completion(matchBuf, NULL);
1068 /* Try to match any executable in our path and everything
1069 * in the current working directory that matches. */
1070 exe_n_cwd_tab_completion(matchBuf, find_type);
1071 /* Remove duplicate found and sort */
1076 for(i=0; i<(n-1); i++) {
1077 for(j=i+1; j<n; j++) {
1078 if(matches[i]!=NULL && matches[j]!=NULL) {
1079 srt = strcmp(matches[i], matches[j]);
1083 } else if(srt > 0) {
1085 matches[i] = matches[j];
1087 srt = add_char_to_match[i];
1088 add_char_to_match[i] = add_char_to_match[j];
1089 add_char_to_match[j] = srt;
1098 matches[n]=matches[i];
1099 add_char_to_match[n]=add_char_to_match[i];
1104 /* Did we find exactly one match? */
1105 if (!matches || num_matches > 1) {
1109 return; /* not found */
1110 /* find minimal match */
1111 tmp1 = xstrdup(matches[0]);
1112 for (tmp = tmp1; *tmp; tmp++)
1113 for (len_found = 1; len_found < num_matches; len_found++)
1114 if (matches[len_found][(tmp - tmp1)] != *tmp) {
1118 if (*tmp1 == 0) { /* have unique */
1122 tmp = add_quote_for_spec_chars(tmp1, 0);
1124 } else { /* one match */
1125 tmp = add_quote_for_spec_chars(matches[0], add_char_to_match[0]);
1126 /* for next completion current found */
1127 *lastWasTab = FALSE;
1129 len_found = strlen(tmp);
1130 /* have space to placed match? */
1131 if ((len_found - strlen(matchBuf) + len) < BUFSIZ) {
1133 /* before word for match */
1134 command_ps[cursor - recalc_pos] = 0;
1135 /* save tail line */
1136 strcpy(matchBuf, command_ps + cursor);
1138 strcat(command_ps, tmp);
1140 strcat(command_ps, matchBuf);
1141 /* back to begin word for match */
1142 input_backward(recalc_pos);
1144 recalc_pos = cursor + len_found;
1146 len = strlen(command_ps);
1147 /* write out the matched command */
1148 redraw(cmdedit_y, len - recalc_pos);
1152 /* Ok -- the last char was a TAB. Since they
1153 * just hit TAB again, print a list of all the
1154 * available choices... */
1155 if (matches && num_matches > 0) {
1156 int sav_cursor = cursor; /* change goto_new_line() */
1158 /* Go to the next line */
1161 redraw(0, len - sav_cursor);
1165 #endif /* CONFIG_FEATURE_COMMAND_TAB_COMPLETION */
1168 static void get_previous_history(void)
1170 if(command_ps[0] != 0 || history[cur_history] == 0) {
1171 free(history[cur_history]);
1172 history[cur_history] = xstrdup(command_ps);
1177 static int get_next_history(void)
1179 int ch = cur_history;
1181 if (ch < n_history) {
1182 get_previous_history(); /* save the current history line */
1183 cur_history = ch + 1;
1191 #ifdef CONFIG_FEATURE_COMMAND_SAVEHISTORY
1192 void load_history ( const char *fromfile )
1199 for(hi = n_history; hi > 0; ) {
1201 free ( history [hi] );
1204 if (( fp = fopen ( fromfile, "r" ))) {
1206 for ( hi = 0; hi < MAX_HISTORY; ) {
1207 char * hl = xmalloc_getline(fp);
1215 if(l == 0 || hl[0] == ' ') {
1219 history [hi++] = hl;
1223 cur_history = n_history = hi;
1226 void save_history ( const char *tofile )
1228 FILE *fp = fopen ( tofile, "w" );
1233 for ( i = 0; i < n_history; i++ ) {
1234 fprintf(fp, "%s\n", history [i]);
1250 * This function is used to grab a character buffer
1251 * from the input file descriptor and allows you to
1252 * a string with full command editing (sort of like
1255 * The following standard commands are not implemented:
1256 * ESC-b -- Move back one word
1257 * ESC-f -- Move forward one word
1258 * ESC-d -- Delete back one word
1259 * ESC-h -- Delete forward one word
1260 * CTL-t -- Transpose two characters
1262 * Minimalist vi-style command line editing available if configured.
1263 * vi mode implemented 2005 by Paul Fox <pgf@foxharp.boston.ma.us>
1267 #ifdef CONFIG_FEATURE_COMMAND_EDITING_VI
1270 void setvimode ( int viflag )
1276 vi_Word_motion(char *command, int eat)
1278 while (cursor < len && !isspace(command[cursor]))
1280 if (eat) while (cursor < len && isspace(command[cursor]))
1285 vi_word_motion(char *command, int eat)
1287 if (isalnum(command[cursor]) || command[cursor] == '_') {
1288 while (cursor < len &&
1289 (isalnum(command[cursor+1]) ||
1290 command[cursor+1] == '_'))
1292 } else if (ispunct(command[cursor])) {
1293 while (cursor < len &&
1294 (ispunct(command[cursor+1])))
1301 if (eat && cursor < len && isspace(command[cursor]))
1302 while (cursor < len && isspace(command[cursor]))
1307 vi_End_motion(char *command)
1310 while (cursor < len && isspace(command[cursor]))
1312 while (cursor < len-1 && !isspace(command[cursor+1]))
1317 vi_end_motion(char *command)
1319 if (cursor >= len-1)
1322 while (cursor < len-1 && isspace(command[cursor]))
1324 if (cursor >= len-1)
1326 if (isalnum(command[cursor]) || command[cursor] == '_') {
1327 while (cursor < len-1 &&
1328 (isalnum(command[cursor+1]) ||
1329 command[cursor+1] == '_'))
1331 } else if (ispunct(command[cursor])) {
1332 while (cursor < len-1 &&
1333 (ispunct(command[cursor+1])))
1339 vi_Back_motion(char *command)
1341 while (cursor > 0 && isspace(command[cursor-1]))
1343 while (cursor > 0 && !isspace(command[cursor-1]))
1348 vi_back_motion(char *command)
1353 while (cursor > 0 && isspace(command[cursor]))
1357 if (isalnum(command[cursor]) || command[cursor] == '_') {
1358 while (cursor > 0 &&
1359 (isalnum(command[cursor-1]) ||
1360 command[cursor-1] == '_'))
1362 } else if (ispunct(command[cursor])) {
1363 while (cursor > 0 &&
1364 (ispunct(command[cursor-1])))
1371 * the emacs and vi modes share much of the code in the big
1372 * command loop. commands entered when in vi's command mode (aka
1373 * "escape mode") get an extra bit added to distinguish them --
1374 * this keeps them from being self-inserted. this clutters the
1375 * big switch a bit, but keeps all the code in one place.
1380 /* leave out the "vi-mode"-only case labels if vi editing isn't
1382 #define vi_case(caselabel) USE_FEATURE_COMMAND_EDITING(caselabel)
1384 /* convert uppercase ascii to equivalent control char, for readability */
1385 #define CNTRL(uc_char) ((uc_char) - 0x40)
1388 int cmdedit_read_input(char *prompt, char command[BUFSIZ])
1392 int lastWasTab = FALSE;
1395 #ifdef CONFIG_FEATURE_COMMAND_EDITING_VI
1399 /* prepare before init handlers */
1400 cmdedit_y = 0; /* quasireal y, not true work if line > xt*yt */
1402 command_ps = command;
1404 getTermSettings(0, (void *) &initial_settings);
1405 memcpy(&new_settings, &initial_settings, sizeof(struct termios));
1406 new_settings.c_lflag &= ~ICANON; /* unbuffered input */
1407 /* Turn off echoing and CTRL-C, so we can trap it */
1408 new_settings.c_lflag &= ~(ECHO | ECHONL | ISIG);
1409 /* Hmm, in linux c_cc[] not parsed if set ~ICANON */
1410 new_settings.c_cc[VMIN] = 1;
1411 new_settings.c_cc[VTIME] = 0;
1412 /* Turn off CTRL-C, so we can trap it */
1413 # ifndef _POSIX_VDISABLE
1414 # define _POSIX_VDISABLE '\0'
1416 new_settings.c_cc[VINTR] = _POSIX_VDISABLE;
1419 setTermSettings(0, (void *) &new_settings);
1420 handlers_sets |= SET_RESET_TERM;
1422 /* Now initialize things */
1424 /* Print out the command prompt */
1425 parse_prompt(prompt);
1429 fflush(stdout); /* buffered out to fast */
1431 if (safe_read(0, &c, 1) < 1)
1432 /* if we can't read input then exit */
1433 goto prepare_to_die;
1437 #ifdef CONFIG_FEATURE_COMMAND_EDITING_VI
1446 vi_case( case '\n'|vbit: )
1447 vi_case( case '\r'|vbit: )
1453 vi_case( case '0'|vbit: )
1454 /* Control-a -- Beginning of line */
1455 input_backward(cursor);
1458 vi_case( case 'h'|vbit: )
1459 vi_case( case '\b'|vbit: )
1460 vi_case( case DEL|vbit: )
1461 /* Control-b -- Move back one character */
1465 vi_case( case CNTRL('C')|vbit: )
1466 /* Control-c -- stop gathering input */
1475 break_out = -1; /* to control traps */
1479 /* Control-d -- Delete one character, or exit
1480 * if the len=0 and no chars to delete */
1484 #if !defined(CONFIG_ASH)
1487 /* cmdedit_reset_term() called in atexit */
1490 /* to control stopped jobs */
1491 len = break_out = -1;
1499 vi_case( case '$'|vbit: )
1500 /* Control-e -- End of line */
1504 vi_case( case 'l'|vbit: )
1505 vi_case( case ' '|vbit: )
1506 /* Control-f -- Move forward one character */
1511 /* Control-h and DEL */
1515 #ifdef CONFIG_FEATURE_COMMAND_TAB_COMPLETION
1516 input_tab(&lastWasTab);
1520 /* Control-k -- clear to end of line */
1521 *(command + cursor) = 0;
1526 vi_case( case CNTRL('L')|vbit: )
1527 /* Control-l -- clear screen */
1529 redraw(0, len-cursor);
1533 vi_case( case CNTRL('N')|vbit: )
1534 vi_case( case 'j'|vbit: )
1535 /* Control-n -- Get next command in history */
1536 if (get_next_history())
1540 vi_case( case CNTRL('P')|vbit: )
1541 vi_case( case 'k'|vbit: )
1542 /* Control-p -- Get previous command from history */
1543 if (cur_history > 0) {
1544 get_previous_history();
1552 vi_case( case CNTRL('U')|vbit: )
1553 /* Control-U -- Clear line before cursor */
1555 strcpy(command, command + cursor);
1556 redraw(cmdedit_y, len -= cursor);
1560 vi_case( case CNTRL('W')|vbit: )
1561 /* Control-W -- Remove the last word */
1562 while (cursor > 0 && isspace(command[cursor-1]))
1564 while (cursor > 0 &&!isspace(command[cursor-1]))
1567 #if ENABLE_FEATURE_COMMAND_EDITING_VI
1572 input_backward(cursor);
1593 vi_Word_motion(command, 1);
1596 vi_word_motion(command, 1);
1599 vi_End_motion(command);
1602 vi_end_motion(command);
1605 vi_Back_motion(command);
1608 vi_back_motion(command);
1624 if (safe_read(0, &c, 1) < 1)
1625 goto prepare_to_die;
1626 if (c == (prevc & 0xff)) {
1628 input_backward(cursor);
1638 case 'w': /* "dw", "cw" */
1639 vi_word_motion(command, vi_cmdmode);
1641 case 'W': /* 'dW', 'cW' */
1642 vi_Word_motion(command, vi_cmdmode);
1644 case 'e': /* 'de', 'ce' */
1645 vi_end_motion(command);
1648 case 'E': /* 'dE', 'cE' */
1649 vi_End_motion(command);
1654 input_backward(cursor - sc);
1655 while (nc-- > cursor)
1658 case 'b': /* "db", "cb" */
1659 case 'B': /* implemented as B */
1661 vi_back_motion(command);
1663 vi_Back_motion(command);
1664 while (sc-- > cursor)
1667 case ' ': /* "d ", "c " */
1670 case '$': /* "d$", "c$" */
1672 while (cursor < len)
1685 if (safe_read(0, &c, 1) < 1)
1686 goto prepare_to_die;
1690 *(command + cursor) = c;
1695 #endif /* CONFIG_FEATURE_COMMAND_EDITING_VI */
1699 #if ENABLE_FEATURE_COMMAND_EDITING_VI
1701 /* ESC: insert mode --> command mode */
1707 /* escape sequence follows */
1708 if (safe_read(0, &c, 1) < 1)
1709 goto prepare_to_die;
1710 /* different vt100 emulations */
1711 if (c == '[' || c == 'O') {
1712 vi_case( case '['|vbit: )
1713 vi_case( case 'O'|vbit: )
1714 if (safe_read(0, &c, 1) < 1)
1715 goto prepare_to_die;
1717 if (c >= '1' && c <= '9') {
1718 unsigned char dummy;
1720 if (safe_read(0, &dummy, 1) < 1)
1721 goto prepare_to_die;
1726 #ifdef CONFIG_FEATURE_COMMAND_TAB_COMPLETION
1727 case '\t': /* Alt-Tab */
1729 input_tab(&lastWasTab);
1734 /* Up Arrow -- Get previous command from history */
1735 if (cur_history > 0) {
1736 get_previous_history();
1743 /* Down Arrow -- Get next command in history */
1744 if (!get_next_history())
1746 /* Rewrite the line with the selected history item */
1748 /* change command */
1749 len = strlen(strcpy(command, history[cur_history]));
1750 /* redraw and go to eol (bol, in vi */
1751 #if ENABLE_FEATURE_COMMAND_EDITING_VI
1752 redraw(cmdedit_y, vi_mode ? 9999:0);
1754 redraw(cmdedit_y, 0);
1759 /* Right Arrow -- Move forward one character */
1763 /* Left Arrow -- Move back one character */
1773 input_backward(cursor);
1786 default: /* If it's regular input, do the normal thing */
1787 #ifdef CONFIG_FEATURE_NONPRINTABLE_INVERSE_PUT
1788 /* Control-V -- Add non-printable symbol */
1789 if (c == CNTRL('V')) {
1790 if (safe_read(0, &c, 1) < 1)
1791 goto prepare_to_die;
1799 #if ENABLE_FEATURE_COMMAND_EDITING_VI
1800 if (vi_cmdmode) /* don't self-insert */
1803 if (!Isprint(c)) /* Skip non-printable characters */
1807 if (len >= (BUFSIZ - 2)) /* Need to leave space for enter */
1812 if (cursor == (len - 1)) { /* Append if at the end of the line */
1813 *(command + cursor) = c;
1814 *(command + cursor + 1) = 0;
1815 cmdedit_set_out_char(0);
1816 } else { /* Insert otherwise */
1819 memmove(command + sc + 1, command + sc, len - sc);
1820 *(command + sc) = c;
1822 /* rewrite from cursor */
1824 /* to prev x pos + 1 */
1825 input_backward(cursor - sc);
1830 if (break_out) /* Enter is the command terminator, no more input. */
1837 setTermSettings(0, (void *) &initial_settings);
1838 handlers_sets &= ~SET_RESET_TERM;
1841 /* Handle command history log */
1842 /* cleanup may be saved current command line */
1843 if (len> 0) { /* no put empty line */
1846 free(history[MAX_HISTORY]);
1847 history[MAX_HISTORY] = 0;
1848 /* After max history, remove the oldest command */
1849 if (i >= MAX_HISTORY) {
1851 for(i = 0; i < (MAX_HISTORY-1); i++)
1852 history[i] = history[i+1];
1854 history[i++] = xstrdup(command);
1857 #if defined(CONFIG_FEATURE_SH_FANCY_PROMPT)
1861 #else /* MAX_HISTORY == 0 */
1862 #if defined(CONFIG_FEATURE_SH_FANCY_PROMPT)
1863 if (len > 0) { /* no put empty line */
1867 #endif /* MAX_HISTORY > 0 */
1868 if (break_out > 0) {
1869 command[len++] = '\n'; /* set '\n' */
1872 #if defined(CONFIG_FEATURE_CLEAN_UP) && defined(CONFIG_FEATURE_COMMAND_TAB_COMPLETION)
1873 input_tab(0); /* strong free */
1875 #if defined(CONFIG_FEATURE_SH_FANCY_PROMPT)
1876 free(cmdedit_prompt);
1878 cmdedit_reset_term();
1884 #endif /* CONFIG_FEATURE_COMMAND_EDITING */
1889 const char *applet_name = "debug stuff usage";
1891 #ifdef CONFIG_FEATURE_NONPRINTABLE_INVERSE_PUT
1895 int main(int argc, char **argv)
1899 #if defined(CONFIG_FEATURE_SH_FANCY_PROMPT)
1900 "\\[\\033[32;1m\\]\\u@\\[\\x1b[33;1m\\]\\h:\
1901 \\[\\033[34;1m\\]\\w\\[\\033[35;1m\\] \
1902 \\!\\[\\e[36;1m\\]\\$ \\[\\E[0m\\]";
1907 #ifdef CONFIG_FEATURE_NONPRINTABLE_INVERSE_PUT
1908 setlocale(LC_ALL, "");
1912 l = cmdedit_read_input(prompt, buff);
1913 if(l > 0 && buff[l-1] == '\n') {
1915 printf("*** cmdedit_read_input() returned line =%s=\n", buff);
1920 printf("*** cmdedit_read_input() detect ^D\n");