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
90 #warning cmdedit: You set MAX_HISTORY < 1. The history algorithm switched off.
92 static char *history[MAX_HISTORY+1]; /* history + current */
93 /* saved history lines */
95 /* current pointer to history line */
96 static int cur_history;
100 #define setTermSettings(fd,argp) tcsetattr(fd,TCSANOW,argp)
101 #define getTermSettings(fd,argp) tcgetattr(fd, argp);
103 /* Current termio and the previous termio before starting sh */
104 static struct termios initial_settings, new_settings;
108 volatile int cmdedit_termw = 80; /* actual terminal width */
110 volatile int handlers_sets = 0; /* Set next bites: */
113 SET_ATEXIT = 1, /* when atexit() has been called
114 and get euid,uid,gid to fast compare */
115 SET_WCHG_HANDLERS = 2, /* winchg signal handler */
116 SET_RESET_TERM = 4, /* if the terminal needs to be reset upon exit */
120 static int cmdedit_x; /* real x terminal position */
121 static int cmdedit_y; /* pseudoreal y terminal position */
122 static int cmdedit_prmt_len; /* lenght prompt without colores string */
124 static int cursor; /* required global for signal handler */
125 static int len; /* --- "" - - "" - -"- --""-- --""--- */
126 static char *command_ps; /* --- "" - - "" - -"- --""-- --""--- */
128 #ifndef CONFIG_FEATURE_SH_FANCY_PROMPT
131 char *cmdedit_prompt; /* --- "" - - "" - -"- --""-- --""--- */
133 #ifdef CONFIG_FEATURE_GETUSERNAME_AND_HOMEDIR
134 static char *user_buf = "";
135 static char *home_pwd_buf = "";
139 #ifdef CONFIG_FEATURE_SH_FANCY_PROMPT
140 static char *hostname_buf;
141 static int num_ok_lines = 1;
145 #ifdef CONFIG_FEATURE_COMMAND_TAB_COMPLETION
147 #ifndef CONFIG_FEATURE_GETUSERNAME_AND_HOMEDIR
154 #endif /* CONFIG_FEATURE_COMMAND_TAB_COMPLETION */
156 static void cmdedit_setwidth(int w, int redraw_flg);
158 static void win_changed(int nsig)
160 static sighandler_t previous_SIGWINCH_handler; /* for reset */
162 /* emulate || signal call */
163 if (nsig == -SIGWINCH || nsig == SIGWINCH) {
165 get_terminal_width_height(0, &width, NULL);
166 cmdedit_setwidth(width, nsig == SIGWINCH);
168 /* Unix not all standart in recall signal */
170 if (nsig == -SIGWINCH) /* save previous handler */
171 previous_SIGWINCH_handler = signal(SIGWINCH, win_changed);
172 else if (nsig == SIGWINCH) /* signaled called handler */
173 signal(SIGWINCH, win_changed); /* set for next call */
175 /* set previous handler */
176 signal(SIGWINCH, previous_SIGWINCH_handler); /* reset */
179 static void cmdedit_reset_term(void)
181 if ((handlers_sets & SET_RESET_TERM) != 0) {
182 /* sparc and other have broken termios support: use old termio handling. */
183 setTermSettings(STDIN_FILENO, (void *) &initial_settings);
184 handlers_sets &= ~SET_RESET_TERM;
186 if ((handlers_sets & SET_WCHG_HANDLERS) != 0) {
187 /* reset SIGWINCH handler to previous (default) */
189 handlers_sets &= ~SET_WCHG_HANDLERS;
195 /* special for recount position for scroll and remove terminal margin effect */
196 static void cmdedit_set_out_char(int next_char)
199 int c = (int)((unsigned char) command_ps[cursor]);
202 c = ' '; /* destroy end char? */
203 #ifdef CONFIG_FEATURE_NONPRINTABLE_INVERSE_PUT
204 if (!Isprint(c)) { /* Inverse put non-printable characters */
211 printf("\033[7m%c\033[0m", c);
214 if (initial_settings.c_lflag & ECHO) putchar(c);
215 if (++cmdedit_x >= cmdedit_termw) {
216 /* terminal is scrolled down */
222 /* destroy "(auto)margin" */
229 /* Move to end line. Bonus: rewrite line from cursor */
230 static void input_end(void)
233 cmdedit_set_out_char(0);
236 /* Go to the next line */
237 static void goto_new_line(void)
245 static void out1str(const char *s)
251 static void beep(void)
256 /* Move back one character */
257 /* special for slow terminal */
258 static void input_backward(int num)
262 cursor -= num; /* new cursor (in command, not terminal) */
264 if (cmdedit_x >= num) { /* no to up line */
270 printf("\033[%dD", num);
275 putchar('\r'); /* back to first terminal pos. */
276 num -= cmdedit_x; /* set previous backward */
278 count_y = 1 + num / cmdedit_termw;
279 printf("\033[%dA", count_y);
280 cmdedit_y -= count_y;
281 /* require forward after uping */
282 cmdedit_x = cmdedit_termw * count_y - num;
283 printf("\033[%dC", cmdedit_x); /* set term cursor */
287 static void put_prompt(void)
289 out1str(cmdedit_prompt);
290 cmdedit_x = cmdedit_prmt_len; /* count real x terminal position */
292 cmdedit_y = 0; /* new quasireal y */
295 #ifndef CONFIG_FEATURE_SH_FANCY_PROMPT
296 static void parse_prompt(const char *prmt_ptr)
298 cmdedit_prompt = prmt_ptr;
299 cmdedit_prmt_len = strlen(prmt_ptr);
303 static void parse_prompt(const char *prmt_ptr)
306 size_t cur_prmt_len = 0;
307 char flg_not_length = '[';
308 char *prmt_mem_ptr = xzalloc(1);
309 char *pwd_buf = xgetcwd(0);
310 char buf2[PATH_MAX + 1];
316 pwd_buf=(char *)bb_msg_unknown;
324 const char *cp = prmt_ptr;
327 c = bb_process_escape_sequence(&prmt_ptr);
333 #ifdef CONFIG_FEATURE_GETUSERNAME_AND_HOMEDIR
342 if (gethostname(pbuf, 255) < 0) {
345 char *s = strchr(pbuf, '.');
354 c = my_euid == 0 ? '#' : '$';
356 #ifdef CONFIG_FEATURE_GETUSERNAME_AND_HOMEDIR
359 l = strlen(home_pwd_buf);
360 if (home_pwd_buf[0] != 0 &&
361 strncmp(home_pwd_buf, pbuf, l) == 0 &&
362 (pbuf[l]=='/' || pbuf[l]=='\0') &&
363 strlen(pwd_buf+l)<PATH_MAX) {
366 strcpy(pbuf+1, pwd_buf+l);
372 cp = strrchr(pbuf,'/');
373 if ( (cp != NULL) && (cp != pbuf) )
377 snprintf(pbuf = buf2, sizeof(buf2), "%d", num_ok_lines);
379 case 'e': case 'E': /* \e \E = \033 */
383 for (l = 0; l < 3;) {
385 buf2[l++] = *prmt_ptr;
387 h = strtol(buf2, &pbuf, 16);
388 if (h > UCHAR_MAX || (pbuf - buf2) < l) {
395 c = (char)strtol(buf2, 0, 16);
401 if (c == flg_not_length) {
402 flg_not_length = flg_not_length == '[' ? ']' : '[';
411 cur_prmt_len = strlen(pbuf);
412 prmt_len += cur_prmt_len;
413 if (flg_not_length != ']')
414 cmdedit_prmt_len += cur_prmt_len;
415 prmt_mem_ptr = strcat(xrealloc(prmt_mem_ptr, prmt_len+1), pbuf);
417 if(pwd_buf!=(char *)bb_msg_unknown)
419 cmdedit_prompt = prmt_mem_ptr;
425 /* draw prompt, editor line, and clear tail */
426 static void redraw(int y, int back_cursor)
428 if (y > 0) /* up to start y */
429 printf("\033[%dA", y);
432 input_end(); /* rewrite */
433 printf("\033[J"); /* destroy tail after cursor */
434 input_backward(back_cursor);
437 #ifdef CONFIG_FEATURE_COMMAND_EDITING_VI
438 #define DELBUFSIZ 128
439 static char *delbuf; /* a (malloced) place to store deleted characters */
441 static char newdelflag; /* whether delbuf should be reused yet */
444 /* Delete the char in front of the cursor, optionally saving it
445 * for later putback */
446 static void input_delete(int save)
453 #ifdef CONFIG_FEATURE_COMMAND_EDITING_VI
457 delbuf = malloc(DELBUFSIZ);
458 /* safe if malloc fails */
462 if (delbuf && (delp - delbuf < DELBUFSIZ))
463 *delp++ = command_ps[j];
467 strcpy(command_ps + j, command_ps + j + 1);
469 input_end(); /* rewrite new line */
470 cmdedit_set_out_char(0); /* destroy end char */
471 input_backward(cursor - j); /* back to old pos cursor */
474 #ifdef CONFIG_FEATURE_COMMAND_EDITING_VI
475 static void put(void)
477 int ocursor, j = delp - delbuf;
481 /* open hole and then fill it */
482 memmove(command_ps + cursor + j, command_ps + cursor, len - cursor + 1);
483 strncpy(command_ps + cursor, delbuf, j);
485 input_end(); /* rewrite new line */
486 input_backward(cursor-ocursor-j+1); /* at end of new text */
490 /* Delete the char in back of the cursor */
491 static void input_backspace(void)
500 /* Move forward one character */
501 static void input_forward(void)
504 cmdedit_set_out_char(command_ps[cursor + 1]);
507 static void cmdedit_setwidth(int w, int redraw_flg)
509 cmdedit_termw = cmdedit_prmt_len + 2;
510 if (w <= cmdedit_termw) {
511 cmdedit_termw = cmdedit_termw % w;
513 if (w > cmdedit_termw) {
517 /* new y for current cursor */
518 int new_y = (cursor + cmdedit_prmt_len) / w;
521 redraw((new_y >= cmdedit_y ? new_y : cmdedit_y), len - cursor);
527 static void cmdedit_init(void)
529 cmdedit_prmt_len = 0;
530 if ((handlers_sets & SET_WCHG_HANDLERS) == 0) {
531 /* emulate usage handler to set handler and call yours work */
532 win_changed(-SIGWINCH);
533 handlers_sets |= SET_WCHG_HANDLERS;
536 if ((handlers_sets & SET_ATEXIT) == 0) {
537 #ifdef CONFIG_FEATURE_GETUSERNAME_AND_HOMEDIR
538 struct passwd *entry;
541 entry = getpwuid(my_euid);
543 user_buf = xstrdup(entry->pw_name);
544 home_pwd_buf = xstrdup(entry->pw_dir);
548 #ifdef CONFIG_FEATURE_COMMAND_TAB_COMPLETION
550 #ifndef CONFIG_FEATURE_GETUSERNAME_AND_HOMEDIR
555 #endif /* CONFIG_FEATURE_COMMAND_TAB_COMPLETION */
556 handlers_sets |= SET_ATEXIT;
557 atexit(cmdedit_reset_term); /* be sure to do this only once */
561 #ifdef CONFIG_FEATURE_COMMAND_TAB_COMPLETION
563 static char **matches;
564 static int num_matches;
565 static char *add_char_to_match;
567 static void add_match(char *matched, int add_char)
569 int nm = num_matches;
572 matches = xrealloc(matches, nm1 * sizeof(char *));
573 add_char_to_match = xrealloc(add_char_to_match, nm1);
574 matches[nm] = matched;
575 add_char_to_match[nm] = (char)add_char;
579 static int is_execute(const struct stat *st)
581 if ((!my_euid && (st->st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))) ||
582 (my_uid == st->st_uid && (st->st_mode & S_IXUSR)) ||
583 (my_gid == st->st_gid && (st->st_mode & S_IXGRP)) ||
584 (st->st_mode & S_IXOTH)) return TRUE;
588 #ifdef CONFIG_FEATURE_COMMAND_USERNAME_COMPLETION
590 static void username_tab_completion(char *ud, char *with_shash_flg)
592 struct passwd *entry;
595 ud++; /* ~user/... to user/... */
596 userlen = strlen(ud);
598 if (with_shash_flg) { /* "~/..." or "~user/..." */
599 char *sav_ud = ud - 1;
603 if (*ud == '/') { /* "~/..." */
607 temp = strchr(ud, '/');
608 *temp = 0; /* ~user\0 */
609 entry = getpwnam(ud);
610 *temp = '/'; /* restore ~user/... */
613 home = entry->pw_dir;
616 if ((userlen + strlen(home) + 1) < BUFSIZ) {
617 char temp2[BUFSIZ]; /* argument size */
620 sprintf(temp2, "%s%s", home, ud);
621 strcpy(sav_ud, temp2);
628 while ((entry = getpwent()) != NULL) {
629 /* Null usernames should result in all users as possible completions. */
630 if ( /*!userlen || */ !strncmp(ud, entry->pw_name, userlen)) {
631 add_match(xasprintf("~%s", entry->pw_name), '/');
638 #endif /* CONFIG_FEATURE_COMMAND_USERNAME_COMPLETION */
647 const char *cmdedit_path_lookup;
649 #define cmdedit_path_lookup getenv("PATH")
652 static int path_parse(char ***p, int flags)
658 /* if not setenv PATH variable, to search cur dir "." */
659 if (flags != FIND_EXE_ONLY || (pth = cmdedit_path_lookup) == 0 ||
660 /* PATH=<empty> or PATH=:<empty> */
661 *pth == 0 || (*pth == ':' && *(pth + 1) == 0)) {
669 npth++; /* count words is + 1 count ':' */
670 tmp = strchr(tmp, ':');
673 break; /* :<empty> */
678 *p = xmalloc(npth * sizeof(char *));
681 (*p)[0] = xstrdup(tmp);
682 npth = 1; /* count words is + 1 count ':' */
685 tmp = strchr(tmp, ':');
687 (*p)[0][(tmp - pth)] = 0; /* ':' -> '\0' */
689 break; /* :<empty> */
692 (*p)[npth++] = &(*p)[0][(tmp - pth)]; /* p[next]=p[0][&'\0'+1] */
698 static char *add_quote_for_spec_chars(char *found, int add)
701 char *s = xmalloc((strlen(found) + 1) * 2);
704 if (strchr(" `\"#$%^&*()=+{}[]:;\'|\\<>", *found))
714 static void exe_n_cwd_tab_completion(char *command, int type)
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, dirbuf);
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;
761 if (strncmp(str_found, pfind, strlen(pfind)))
763 /* not see .name without .match */
764 if (*str_found == '.' && *pfind == 0) {
765 if (*paths[i] == '/' && paths[i][1] == 0
766 && str_found[1] == 0) str_found = ""; /* only "/" */
770 found = concat_path_file(paths[i], str_found);
771 /* hmm, remover in progress? */
772 if (stat(found, &st) < 0)
774 /* find with dirs ? */
775 if (paths[i] != dirbuf)
776 strcpy(found, next->d_name); /* only name */
777 if (S_ISDIR(st.st_mode)) {
778 /* name is directory */
779 char *e = found + strlen(found) - 1;
785 /* not put found file if search only dirs for cd */
786 if (type == FIND_DIR_ONLY)
788 if (type == FIND_FILE_ONLY ||
789 (type == FIND_EXE_ONLY && is_execute(&st)))
792 /* Add it to the list */
793 add_match(found, add_chr);
800 if (paths != path1) {
801 free(paths[0]); /* allocated memory only in first member */
807 #define QUOT (UCHAR_MAX+1)
809 #define collapse_pos(is, in) { \
810 memmove(int_buf+(is), int_buf+(in), (BUFSIZ+1-(is)-(in))*sizeof(int)); \
811 memmove(pos_buf+(is), pos_buf+(in), (BUFSIZ+1-(is)-(in))*sizeof(int)); }
813 static int find_match(char *matchBuf, int *len_with_quotes)
818 int int_buf[BUFSIZ + 1];
819 int pos_buf[BUFSIZ + 1];
821 /* set to integer dimension characters and own positions */
823 int_buf[i] = (int) ((unsigned char) matchBuf[i]);
824 if (int_buf[i] == 0) {
825 pos_buf[i] = -1; /* indicator end line */
831 /* mask \+symbol and convert '\t' to ' ' */
832 for (i = j = 0; matchBuf[i]; i++, j++)
833 if (matchBuf[i] == '\\') {
834 collapse_pos(j, j + 1);
837 #ifdef CONFIG_FEATURE_NONPRINTABLE_INVERSE_PUT
838 if (matchBuf[i] == '\t') /* algorithm equivalent */
839 int_buf[j] = ' ' | QUOT;
842 #ifdef CONFIG_FEATURE_NONPRINTABLE_INVERSE_PUT
843 else if (matchBuf[i] == '\t')
847 /* mask "symbols" or 'symbols' */
849 for (i = 0; int_buf[i]; i++) {
851 if (c == '\'' || c == '"') {
860 } else if (c2 != 0 && c != '$')
864 /* skip commands with arguments if line have commands delimiters */
865 /* ';' ';;' '&' '|' '&&' '||' but `>&' `<&' `>|' */
866 for (i = 0; int_buf[i]; i++) {
869 j = i ? int_buf[i - 1] : -1;
871 if (c == ';' || c == '&' || c == '|') {
872 command_mode = 1 + (c == c2);
874 if (j == '>' || j == '<')
876 } else if (c == '|' && j == '>')
880 collapse_pos(0, i + command_mode);
881 i = -1; /* hack incremet */
884 /* collapse `command...` */
885 for (i = 0; int_buf[i]; i++)
886 if (int_buf[i] == '`') {
887 for (j = i + 1; int_buf[j]; j++)
888 if (int_buf[j] == '`') {
889 collapse_pos(i, j + 1);
894 /* not found close ` - command mode, collapse all previous */
895 collapse_pos(0, i + 1);
898 i--; /* hack incremet */
901 /* collapse (command...(command...)...) or {command...{command...}...} */
902 c = 0; /* "recursive" level */
904 for (i = 0; int_buf[i]; i++)
905 if (int_buf[i] == '(' || int_buf[i] == '{') {
906 if (int_buf[i] == '(')
910 collapse_pos(0, i + 1);
911 i = -1; /* hack incremet */
913 for (i = 0; pos_buf[i] >= 0 && (c > 0 || c2 > 0); i++)
914 if ((int_buf[i] == ')' && c > 0) || (int_buf[i] == '}' && c2 > 0)) {
915 if (int_buf[i] == ')')
919 collapse_pos(0, i + 1);
920 i = -1; /* hack incremet */
923 /* skip first not quote space */
924 for (i = 0; int_buf[i]; i++)
925 if (int_buf[i] != ' ')
930 /* set find mode for completion */
931 command_mode = FIND_EXE_ONLY;
932 for (i = 0; int_buf[i]; i++)
933 if (int_buf[i] == ' ' || int_buf[i] == '<' || int_buf[i] == '>') {
934 if (int_buf[i] == ' ' && command_mode == FIND_EXE_ONLY
935 && matchBuf[pos_buf[0]]=='c'
936 && matchBuf[pos_buf[1]]=='d' )
937 command_mode = FIND_DIR_ONLY;
939 command_mode = FIND_FILE_ONLY;
944 for (i = 0; int_buf[i]; i++);
946 for (--i; i >= 0; i--) {
948 if (c == ' ' || c == '<' || c == '>' || c == '|' || c == '&') {
949 collapse_pos(0, i + 1);
953 /* skip first not quoted '\'' or '"' */
954 for (i = 0; int_buf[i] == '\'' || int_buf[i] == '"'; i++);
955 /* collapse quote or unquote // or /~ */
956 while ((int_buf[i] & ~QUOT) == '/' &&
957 ((int_buf[i + 1] & ~QUOT) == '/'
958 || (int_buf[i + 1] & ~QUOT) == '~')) {
962 /* set only match and destroy quotes */
964 for (c = 0; pos_buf[i] >= 0; i++) {
965 matchBuf[c++] = matchBuf[pos_buf[i]];
969 /* old lenght matchBuf with quotes symbols */
970 *len_with_quotes = j ? j - pos_buf[0] : 0;
976 display by column original ideas from ls applet,
977 very optimize by my :)
979 static void showfiles(void)
982 int column_width = 0;
983 int nfiles = num_matches;
988 /* find the longest file name- use that as the column width */
989 for (row = 0; row < nrows; row++) {
990 l = strlen(matches[row]);
991 if(add_char_to_match[row])
993 if (column_width < l)
996 column_width += 2; /* min space for columns */
997 ncols = cmdedit_termw / column_width;
1002 nrows++; /* round up fractionals */
1007 for (row = 0; row < nrows; row++) {
1012 for(nc = 1; nc < ncols && n+nrows < nfiles; n += nrows, nc++) {
1013 str_add_chr[0] = add_char_to_match[n];
1014 acol = str_add_chr[0] ? column_width - 1 : column_width;
1015 printf("%s%s", matches[n], str_add_chr);
1016 l = strlen(matches[n]);
1022 str_add_chr[0] = add_char_to_match[n];
1023 printf("%s%s\n", matches[n], str_add_chr);
1028 static void input_tab(int *lastWasTab)
1030 /* Do TAB completion */
1031 if (lastWasTab == 0) { /* free all memory */
1033 while (num_matches > 0)
1034 free(matches[--num_matches]);
1036 matches = (char **) NULL;
1037 free(add_char_to_match);
1038 add_char_to_match = NULL;
1042 if (! *lastWasTab) {
1046 char matchBuf[BUFSIZ];
1050 *lastWasTab = TRUE; /* flop trigger */
1052 /* Make a local copy of the string -- up
1053 * to the position of the cursor */
1054 tmp = strncpy(matchBuf, command_ps, cursor);
1057 find_type = find_match(matchBuf, &recalc_pos);
1059 /* Free up any memory already allocated */
1062 #ifdef CONFIG_FEATURE_COMMAND_USERNAME_COMPLETION
1063 /* If the word starts with `~' and there is no slash in the word,
1064 * then try completing this word as a username. */
1066 if (matchBuf[0] == '~' && strchr(matchBuf, '/') == 0)
1067 username_tab_completion(matchBuf, NULL);
1070 /* Try to match any executable in our path and everything
1071 * in the current working directory that matches. */
1072 exe_n_cwd_tab_completion(matchBuf, find_type);
1073 /* Remove duplicate found and sort */
1078 for(i=0; i<(n-1); i++) {
1079 for(j=i+1; j<n; j++) {
1080 if(matches[i]!=NULL && matches[j]!=NULL) {
1081 srt = strcmp(matches[i], matches[j]);
1085 } else if(srt > 0) {
1087 matches[i] = matches[j];
1089 srt = add_char_to_match[i];
1090 add_char_to_match[i] = add_char_to_match[j];
1091 add_char_to_match[j] = srt;
1100 matches[n]=matches[i];
1101 add_char_to_match[n]=add_char_to_match[i];
1106 /* Did we find exactly one match? */
1107 if (!matches || num_matches > 1) {
1111 return; /* not found */
1112 /* find minimal match */
1113 tmp1 = xstrdup(matches[0]);
1114 for (tmp = tmp1; *tmp; tmp++)
1115 for (len_found = 1; len_found < num_matches; len_found++)
1116 if (matches[len_found][(tmp - tmp1)] != *tmp) {
1120 if (*tmp1 == 0) { /* have unique */
1124 tmp = add_quote_for_spec_chars(tmp1, 0);
1126 } else { /* one match */
1127 tmp = add_quote_for_spec_chars(matches[0], add_char_to_match[0]);
1128 /* for next completion current found */
1129 *lastWasTab = FALSE;
1131 len_found = strlen(tmp);
1132 /* have space to placed match? */
1133 if ((len_found - strlen(matchBuf) + len) < BUFSIZ) {
1135 /* before word for match */
1136 command_ps[cursor - recalc_pos] = 0;
1137 /* save tail line */
1138 strcpy(matchBuf, command_ps + cursor);
1140 strcat(command_ps, tmp);
1142 strcat(command_ps, matchBuf);
1143 /* back to begin word for match */
1144 input_backward(recalc_pos);
1146 recalc_pos = cursor + len_found;
1148 len = strlen(command_ps);
1149 /* write out the matched command */
1150 redraw(cmdedit_y, len - recalc_pos);
1154 /* Ok -- the last char was a TAB. Since they
1155 * just hit TAB again, print a list of all the
1156 * available choices... */
1157 if (matches && num_matches > 0) {
1158 int sav_cursor = cursor; /* change goto_new_line() */
1160 /* Go to the next line */
1163 redraw(0, len - sav_cursor);
1167 #endif /* CONFIG_FEATURE_COMMAND_TAB_COMPLETION */
1169 #if MAX_HISTORY >= 1
1170 static void get_previous_history(void)
1172 if(command_ps[0] != 0 || history[cur_history] == 0) {
1173 free(history[cur_history]);
1174 history[cur_history] = xstrdup(command_ps);
1179 static int get_next_history(void)
1181 int ch = cur_history;
1183 if (ch < n_history) {
1184 get_previous_history(); /* save the current history line */
1185 return (cur_history = ch+1);
1192 #ifdef CONFIG_FEATURE_COMMAND_SAVEHISTORY
1193 void load_history ( const char *fromfile )
1200 for(hi = n_history; hi > 0; ) {
1202 free ( history [hi] );
1205 if (( fp = fopen ( fromfile, "r" ))) {
1207 for ( hi = 0; hi < MAX_HISTORY; ) {
1208 char * hl = xmalloc_getline(fp);
1216 if(l == 0 || hl[0] == ' ') {
1220 history [hi++] = hl;
1224 cur_history = n_history = hi;
1227 void save_history ( const char *tofile )
1229 FILE *fp = fopen ( tofile, "w" );
1234 for ( i = 0; i < n_history; i++ ) {
1235 fprintf(fp, "%s\n", history [i]);
1251 * This function is used to grab a character buffer
1252 * from the input file descriptor and allows you to
1253 * a string with full command editing (sort of like
1256 * The following standard commands are not implemented:
1257 * ESC-b -- Move back one word
1258 * ESC-f -- Move forward one word
1259 * ESC-d -- Delete back one word
1260 * ESC-h -- Delete forward one word
1261 * CTL-t -- Transpose two characters
1263 * Minimalist vi-style command line editing available if configured.
1264 * vi mode implemented 2005 by Paul Fox <pgf@foxharp.boston.ma.us>
1268 #ifdef CONFIG_FEATURE_COMMAND_EDITING_VI
1271 void setvimode ( int viflag )
1277 vi_Word_motion(char *command, int eat)
1279 while (cursor < len && !isspace(command[cursor]))
1281 if (eat) while (cursor < len && isspace(command[cursor]))
1286 vi_word_motion(char *command, int eat)
1288 if (isalnum(command[cursor]) || command[cursor] == '_') {
1289 while (cursor < len &&
1290 (isalnum(command[cursor+1]) ||
1291 command[cursor+1] == '_'))
1293 } else if (ispunct(command[cursor])) {
1294 while (cursor < len &&
1295 (ispunct(command[cursor+1])))
1302 if (eat && cursor < len && isspace(command[cursor]))
1303 while (cursor < len && isspace(command[cursor]))
1308 vi_End_motion(char *command)
1311 while (cursor < len && isspace(command[cursor]))
1313 while (cursor < len-1 && !isspace(command[cursor+1]))
1318 vi_end_motion(char *command)
1320 if (cursor >= len-1)
1323 while (cursor < len-1 && isspace(command[cursor]))
1325 if (cursor >= len-1)
1327 if (isalnum(command[cursor]) || command[cursor] == '_') {
1328 while (cursor < len-1 &&
1329 (isalnum(command[cursor+1]) ||
1330 command[cursor+1] == '_'))
1332 } else if (ispunct(command[cursor])) {
1333 while (cursor < len-1 &&
1334 (ispunct(command[cursor+1])))
1340 vi_Back_motion(char *command)
1342 while (cursor > 0 && isspace(command[cursor-1]))
1344 while (cursor > 0 && !isspace(command[cursor-1]))
1349 vi_back_motion(char *command)
1354 while (cursor > 0 && isspace(command[cursor]))
1358 if (isalnum(command[cursor]) || command[cursor] == '_') {
1359 while (cursor > 0 &&
1360 (isalnum(command[cursor-1]) ||
1361 command[cursor-1] == '_'))
1363 } else if (ispunct(command[cursor])) {
1364 while (cursor > 0 &&
1365 (ispunct(command[cursor-1])))
1372 * the emacs and vi modes share much of the code in the big
1373 * command loop. commands entered when in vi's command mode (aka
1374 * "escape mode") get an extra bit added to distinguish them --
1375 * this keeps them from being self-inserted. this clutters the
1376 * big switch a bit, but keeps all the code in one place.
1381 /* leave out the "vi-mode"-only case labels if vi editing isn't
1383 #define vi_case(caselabel) USE_FEATURE_COMMAND_EDITING(caselabel)
1385 /* convert uppercase ascii to equivalent control char, for readability */
1386 #define CNTRL(uc_char) ((uc_char) - 0x40)
1389 int cmdedit_read_input(char *prompt, char command[BUFSIZ])
1393 int lastWasTab = FALSE;
1396 #ifdef CONFIG_FEATURE_COMMAND_EDITING_VI
1400 /* prepare before init handlers */
1401 cmdedit_y = 0; /* quasireal y, not true work if line > xt*yt */
1403 command_ps = command;
1405 getTermSettings(0, (void *) &initial_settings);
1406 memcpy(&new_settings, &initial_settings, sizeof(struct termios));
1407 new_settings.c_lflag &= ~ICANON; /* unbuffered input */
1408 /* Turn off echoing and CTRL-C, so we can trap it */
1409 new_settings.c_lflag &= ~(ECHO | ECHONL | ISIG);
1410 /* Hmm, in linux c_cc[] not parsed if set ~ICANON */
1411 new_settings.c_cc[VMIN] = 1;
1412 new_settings.c_cc[VTIME] = 0;
1413 /* Turn off CTRL-C, so we can trap it */
1414 # ifndef _POSIX_VDISABLE
1415 # define _POSIX_VDISABLE '\0'
1417 new_settings.c_cc[VINTR] = _POSIX_VDISABLE;
1420 setTermSettings(0, (void *) &new_settings);
1421 handlers_sets |= SET_RESET_TERM;
1423 /* Now initialize things */
1425 /* Print out the command prompt */
1426 parse_prompt(prompt);
1430 fflush(stdout); /* buffered out to fast */
1432 if (safe_read(0, &c, 1) < 1)
1433 /* if we can't read input then exit */
1434 goto prepare_to_die;
1438 #ifdef CONFIG_FEATURE_COMMAND_EDITING_VI
1447 vi_case( case '\n'|vbit: )
1448 vi_case( case '\r'|vbit: )
1454 vi_case( case '0'|vbit: )
1455 /* Control-a -- Beginning of line */
1456 input_backward(cursor);
1459 vi_case( case 'h'|vbit: )
1460 vi_case( case '\b'|vbit: )
1461 vi_case( case DEL|vbit: )
1462 /* Control-b -- Move back one character */
1466 vi_case( case CNTRL('C')|vbit: )
1467 /* Control-c -- stop gathering input */
1476 break_out = -1; /* to control traps */
1480 /* Control-d -- Delete one character, or exit
1481 * if the len=0 and no chars to delete */
1485 #if !defined(CONFIG_ASH)
1488 /* cmdedit_reset_term() called in atexit */
1491 /* to control stopped jobs */
1492 len = break_out = -1;
1500 vi_case( case '$'|vbit: )
1501 /* Control-e -- End of line */
1505 vi_case( case 'l'|vbit: )
1506 vi_case( case ' '|vbit: )
1507 /* Control-f -- Move forward one character */
1512 /* Control-h and DEL */
1516 #ifdef CONFIG_FEATURE_COMMAND_TAB_COMPLETION
1517 input_tab(&lastWasTab);
1521 /* Control-k -- clear to end of line */
1522 *(command + cursor) = 0;
1527 vi_case( case CNTRL('L')|vbit: )
1528 /* Control-l -- clear screen */
1530 redraw(0, len-cursor);
1532 #if MAX_HISTORY >= 1
1534 vi_case( case CNTRL('N')|vbit: )
1535 vi_case( case 'j'|vbit: )
1536 /* Control-n -- Get next command in history */
1537 if (get_next_history())
1541 vi_case( case CNTRL('P')|vbit: )
1542 vi_case( case 'k'|vbit: )
1543 /* Control-p -- Get previous command from history */
1544 if (cur_history > 0) {
1545 get_previous_history();
1553 vi_case( case CNTRL('U')|vbit: )
1554 /* Control-U -- Clear line before cursor */
1556 strcpy(command, command + cursor);
1557 redraw(cmdedit_y, len -= cursor);
1561 vi_case( case CNTRL('W')|vbit: )
1562 /* Control-W -- Remove the last word */
1563 while (cursor > 0 && isspace(command[cursor-1]))
1565 while (cursor > 0 &&!isspace(command[cursor-1]))
1568 #if CONFIG_FEATURE_COMMAND_EDITING_VI
1573 input_backward(cursor);
1594 vi_Word_motion(command, 1);
1597 vi_word_motion(command, 1);
1600 vi_End_motion(command);
1603 vi_end_motion(command);
1606 vi_Back_motion(command);
1609 vi_back_motion(command);
1625 if (safe_read(0, &c, 1) < 1)
1626 goto prepare_to_die;
1627 if (c == (prevc & 0xff)) {
1629 input_backward(cursor);
1639 case 'w': /* "dw", "cw" */
1640 vi_word_motion(command, vi_cmdmode);
1642 case 'W': /* 'dW', 'cW' */
1643 vi_Word_motion(command, vi_cmdmode);
1645 case 'e': /* 'de', 'ce' */
1646 vi_end_motion(command);
1649 case 'E': /* 'dE', 'cE' */
1650 vi_End_motion(command);
1655 input_backward(cursor - sc);
1656 while (nc-- > cursor)
1659 case 'b': /* "db", "cb" */
1660 case 'B': /* implemented as B */
1662 vi_back_motion(command);
1664 vi_Back_motion(command);
1665 while (sc-- > cursor)
1668 case ' ': /* "d ", "c " */
1671 case '$': /* "d$", "c$" */
1673 while (cursor < len)
1686 if (safe_read(0, &c, 1) < 1)
1687 goto prepare_to_die;
1691 *(command + cursor) = c;
1696 #endif /* CONFIG_FEATURE_COMMAND_EDITING_VI */
1700 #if CONFIG_FEATURE_COMMAND_EDITING_VI
1702 /* ESC: insert mode --> command mode */
1708 /* escape sequence follows */
1709 if (safe_read(0, &c, 1) < 1)
1710 goto prepare_to_die;
1711 /* different vt100 emulations */
1712 if (c == '[' || c == 'O') {
1713 vi_case( case '['|vbit: )
1714 vi_case( case 'O'|vbit: )
1715 if (safe_read(0, &c, 1) < 1)
1716 goto prepare_to_die;
1718 if (c >= '1' && c <= '9') {
1719 unsigned char dummy;
1721 if (safe_read(0, &dummy, 1) < 1)
1722 goto prepare_to_die;
1727 #ifdef CONFIG_FEATURE_COMMAND_TAB_COMPLETION
1728 case '\t': /* Alt-Tab */
1730 input_tab(&lastWasTab);
1733 #if MAX_HISTORY >= 1
1735 /* Up Arrow -- Get previous command from history */
1736 if (cur_history > 0) {
1737 get_previous_history();
1744 /* Down Arrow -- Get next command in history */
1745 if (!get_next_history())
1747 /* Rewrite the line with the selected history item */
1749 /* change command */
1750 len = strlen(strcpy(command, history[cur_history]));
1751 /* redraw and go to eol (bol, in vi */
1752 #if CONFIG_FEATURE_COMMAND_EDITING_VI
1753 redraw(cmdedit_y, vi_mode ? 9999:0);
1755 redraw(cmdedit_y, 0);
1760 /* Right Arrow -- Move forward one character */
1764 /* Left Arrow -- Move back one character */
1774 input_backward(cursor);
1787 default: /* If it's regular input, do the normal thing */
1788 #ifdef CONFIG_FEATURE_NONPRINTABLE_INVERSE_PUT
1789 /* Control-V -- Add non-printable symbol */
1790 if (c == CNTRL('V')) {
1791 if (safe_read(0, &c, 1) < 1)
1792 goto prepare_to_die;
1800 #if CONFIG_FEATURE_COMMAND_EDITING_VI
1801 if (vi_cmdmode) /* don't self-insert */
1804 if (!Isprint(c)) /* Skip non-printable characters */
1808 if (len >= (BUFSIZ - 2)) /* Need to leave space for enter */
1813 if (cursor == (len - 1)) { /* Append if at the end of the line */
1814 *(command + cursor) = c;
1815 *(command + cursor + 1) = 0;
1816 cmdedit_set_out_char(0);
1817 } else { /* Insert otherwise */
1820 memmove(command + sc + 1, command + sc, len - sc);
1821 *(command + sc) = c;
1823 /* rewrite from cursor */
1825 /* to prev x pos + 1 */
1826 input_backward(cursor - sc);
1831 if (break_out) /* Enter is the command terminator, no more input. */
1838 setTermSettings(0, (void *) &initial_settings);
1839 handlers_sets &= ~SET_RESET_TERM;
1841 #if MAX_HISTORY >= 1
1842 /* Handle command history log */
1843 /* cleanup may be saved current command line */
1844 if (len> 0) { /* no put empty line */
1847 free(history[MAX_HISTORY]);
1848 history[MAX_HISTORY] = 0;
1849 /* After max history, remove the oldest command */
1850 if (i >= MAX_HISTORY) {
1852 for(i = 0; i < (MAX_HISTORY-1); i++)
1853 history[i] = history[i+1];
1855 history[i++] = xstrdup(command);
1858 #if defined(CONFIG_FEATURE_SH_FANCY_PROMPT)
1862 #else /* MAX_HISTORY < 1 */
1863 #if defined(CONFIG_FEATURE_SH_FANCY_PROMPT)
1864 if (len > 0) { /* no put empty line */
1868 #endif /* MAX_HISTORY >= 1 */
1869 if (break_out > 0) {
1870 command[len++] = '\n'; /* set '\n' */
1873 #if defined(CONFIG_FEATURE_CLEAN_UP) && defined(CONFIG_FEATURE_COMMAND_TAB_COMPLETION)
1874 input_tab(0); /* strong free */
1876 #if defined(CONFIG_FEATURE_SH_FANCY_PROMPT)
1877 free(cmdedit_prompt);
1879 cmdedit_reset_term();
1885 #endif /* CONFIG_FEATURE_COMMAND_EDITING */
1890 const char *applet_name = "debug stuff usage";
1892 #ifdef CONFIG_FEATURE_NONPRINTABLE_INVERSE_PUT
1896 int main(int argc, char **argv)
1900 #if defined(CONFIG_FEATURE_SH_FANCY_PROMPT)
1901 "\\[\\033[32;1m\\]\\u@\\[\\x1b[33;1m\\]\\h:\
1902 \\[\\033[34;1m\\]\\w\\[\\033[35;1m\\] \
1903 \\!\\[\\e[36;1m\\]\\$ \\[\\E[0m\\]";
1908 #ifdef CONFIG_FEATURE_NONPRINTABLE_INVERSE_PUT
1909 setlocale(LC_ALL, "");
1913 l = cmdedit_read_input(prompt, buff);
1914 if(l > 0 && buff[l-1] == '\n') {
1916 printf("*** cmdedit_read_input() returned line =%s=\n", buff);
1921 printf("*** cmdedit_read_input() detect ^D\n");