Convert a chunk of usage.h to USE_ and SKIP_ (more to do there), and fix a
[oweals/busybox.git] / shell / cmdedit.c
1 /* vi: set sw=4 ts=4: */
2 /*
3  * Termios command line History and Editing.
4  *
5  * Copyright (c) 1986-2003 may safely be consumed by a BSD or GPL license.
6  * Written by:   Vladimir Oleynik <dzo@simtreas.ru>
7  *
8  * Used ideas:
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)
13  *
14  * This code is 'as is' with no warranty.
15  *
16  *
17  */
18
19 /*
20    Usage and Known bugs:
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.
26
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
31  */
32
33
34 #include <stdio.h>
35 #include <errno.h>
36 #include <unistd.h>
37 #include <stdlib.h>
38 #include <string.h>
39 #include <sys/ioctl.h>
40 #include <ctype.h>
41 #include <signal.h>
42 #include <limits.h>
43
44 #include "busybox.h"
45
46 #include "../shell/cmdedit.h"
47
48
49 #ifdef CONFIG_LOCALE_SUPPORT
50 #define Isprint(c) isprint((c))
51 #else
52 #define Isprint(c) ( (c) >= ' ' && (c) != ((unsigned char)'\233') )
53 #endif
54
55 #ifdef TEST
56
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
63
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
69
70 #endif  /* TEST */
71
72 #ifdef CONFIG_FEATURE_COMMAND_TAB_COMPLETION
73 #include <dirent.h>
74 #include <sys/stat.h>
75 #endif
76
77 #ifdef CONFIG_FEATURE_COMMAND_EDITING
78
79 #if defined(CONFIG_FEATURE_COMMAND_USERNAME_COMPLETION) || defined(CONFIG_FEATURE_SH_FANCY_PROMPT)
80 #define CONFIG_FEATURE_GETUSERNAME_AND_HOMEDIR
81 #endif
82
83 #ifdef CONFIG_FEATURE_GETUSERNAME_AND_HOMEDIR
84 #include "pwd_.h"
85 #endif  /* advanced FEATURES */
86
87
88 /* Maximum length of the linked list for the command line history */
89 #ifndef CONFIG_FEATURE_COMMAND_HISTORY
90 #define MAX_HISTORY   15
91 #else
92 #define MAX_HISTORY   CONFIG_FEATURE_COMMAND_HISTORY
93 #endif
94
95 #if MAX_HISTORY < 1
96 #warning cmdedit: You set MAX_HISTORY < 1. The history algorithm switched off.
97 #else
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;
103 #endif
104
105 #include <termios.h>
106 #define setTermSettings(fd,argp) tcsetattr(fd,TCSANOW,argp)
107 #define getTermSettings(fd,argp) tcgetattr(fd, argp);
108
109 /* Current termio and the previous termio before starting sh */
110 static struct termios initial_settings, new_settings;
111
112
113 static
114 volatile int cmdedit_termw = 80;        /* actual terminal width */
115 static
116 volatile int handlers_sets = 0; /* Set next bites: */
117
118 enum {
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 */
123 };
124
125
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 */
129
130 static int cursor;              /* required global for signal handler */
131 static int len;                 /* --- "" - - "" - -"- --""-- --""--- */
132 static char *command_ps;        /* --- "" - - "" - -"- --""-- --""--- */
133 static
134 #ifndef CONFIG_FEATURE_SH_FANCY_PROMPT
135         const
136 #endif
137 char *cmdedit_prompt;           /* --- "" - - "" - -"- --""-- --""--- */
138
139 #ifdef CONFIG_FEATURE_GETUSERNAME_AND_HOMEDIR
140 static char *user_buf = "";
141 static char *home_pwd_buf = "";
142 static int my_euid;
143 #endif
144
145 #ifdef CONFIG_FEATURE_SH_FANCY_PROMPT
146 static char *hostname_buf;
147 static int num_ok_lines = 1;
148 #endif
149
150
151 #ifdef  CONFIG_FEATURE_COMMAND_TAB_COMPLETION
152
153 #ifndef CONFIG_FEATURE_GETUSERNAME_AND_HOMEDIR
154 static int my_euid;
155 #endif
156
157 static int my_uid;
158 static int my_gid;
159
160 #endif  /* CONFIG_FEATURE_COMMAND_TAB_COMPLETION */
161
162 static void cmdedit_setwidth(int w, int redraw_flg);
163
164 static void win_changed(int nsig)
165 {
166         static sighandler_t previous_SIGWINCH_handler;  /* for reset */
167
168         /*   emulate      || signal call */
169         if (nsig == -SIGWINCH || nsig == SIGWINCH) {
170                 int width = 0;
171                 get_terminal_width_height(0, &width, NULL);
172                 cmdedit_setwidth(width, nsig == SIGWINCH);
173         }
174         /* Unix not all standart in recall signal */
175
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       */
180         else                                            /* nsig == 0 */
181                 /* set previous handler    */
182                 signal(SIGWINCH, previous_SIGWINCH_handler);    /* reset    */
183 }
184
185 static void cmdedit_reset_term(void)
186 {
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;
191         }
192         if ((handlers_sets & SET_WCHG_HANDLERS) != 0) {
193                 /* reset SIGWINCH handler to previous (default) */
194                 win_changed(0);
195                 handlers_sets &= ~SET_WCHG_HANDLERS;
196         }
197         fflush(stdout);
198 }
199
200
201 /* special for recount position for scroll and remove terminal margin effect */
202 static void cmdedit_set_out_char(int next_char)
203 {
204
205         int c = (int)((unsigned char) command_ps[cursor]);
206
207         if (c == 0)
208                 c = ' ';        /* destroy end char? */
209 #ifdef CONFIG_FEATURE_NONPRINTABLE_INVERSE_PUT
210         if (!Isprint(c)) {      /* Inverse put non-printable characters */
211                 if (c >= 128)
212                         c -= 128;
213                 if (c < ' ')
214                         c += '@';
215                 if (c == 127)
216                         c = '?';
217                 printf("\033[7m%c\033[0m", c);
218         } else
219 #endif
220                 putchar(c);
221         if (++cmdedit_x >= cmdedit_termw) {
222                 /* terminal is scrolled down */
223                 cmdedit_y++;
224                 cmdedit_x = 0;
225
226                 if (!next_char)
227                         next_char = ' ';
228                 /* destroy "(auto)margin" */
229                 putchar(next_char);
230                 putchar('\b');
231         }
232         cursor++;
233 }
234
235 /* Move to end line. Bonus: rewrite line from cursor */
236 static void input_end(void)
237 {
238         while (cursor < len)
239                 cmdedit_set_out_char(0);
240 }
241
242 /* Go to the next line */
243 static void goto_new_line(void)
244 {
245         input_end();
246         if (cmdedit_x)
247                 putchar('\n');
248 }
249
250
251 static inline void out1str(const char *s)
252 {
253         if ( s )
254                 fputs(s, stdout);
255 }
256
257 static inline void beep(void)
258 {
259         putchar('\007');
260 }
261
262 /* Move back one character */
263 /* special for slow terminal */
264 static void input_backward(int num)
265 {
266         if (num > cursor)
267                 num = cursor;
268         cursor -= num;          /* new cursor (in command, not terminal) */
269
270         if (cmdedit_x >= num) {         /* no to up line */
271                 cmdedit_x -= num;
272                 if (num < 4)
273                         while (num-- > 0)
274                                 putchar('\b');
275
276                 else
277                         printf("\033[%dD", num);
278         } else {
279                 int count_y;
280
281                 if (cmdedit_x) {
282                         putchar('\r');          /* back to first terminal pos.  */
283                         num -= cmdedit_x;       /* set previous backward        */
284                 }
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   */
291         }
292 }
293
294 static void put_prompt(void)
295 {
296         out1str(cmdedit_prompt);
297         cmdedit_x = cmdedit_prmt_len;   /* count real x terminal position */
298         cursor = 0;
299         cmdedit_y = 0;                  /* new quasireal y */
300 }
301
302 #ifndef CONFIG_FEATURE_SH_FANCY_PROMPT
303 static void parse_prompt(const char *prmt_ptr)
304 {
305         cmdedit_prompt = prmt_ptr;
306         cmdedit_prmt_len = strlen(prmt_ptr);
307         put_prompt();
308 }
309 #else
310 static void parse_prompt(const char *prmt_ptr)
311 {
312         int prmt_len = 0;
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];
318         char  buf[2];
319         char  c;
320         char *pbuf;
321
322         if (!pwd_buf) {
323                 pwd_buf=(char *)bb_msg_unknown;
324         }
325
326         while (*prmt_ptr) {
327                 pbuf    = buf;
328                 pbuf[1] = 0;
329                 c = *prmt_ptr++;
330                 if (c == '\\') {
331                         const char *cp = prmt_ptr;
332                         int l;
333
334                         c = bb_process_escape_sequence(&prmt_ptr);
335                         if(prmt_ptr==cp) {
336                           if (*cp == 0)
337                                 break;
338                           c = *prmt_ptr++;
339                           switch (c) {
340 #ifdef CONFIG_FEATURE_GETUSERNAME_AND_HOMEDIR
341                           case 'u':
342                                 pbuf = user_buf;
343                                 break;
344 #endif
345                           case 'h':
346                                 pbuf = hostname_buf;
347                                 if (pbuf == 0) {
348                                         pbuf = xcalloc(256, 1);
349                                         if (gethostname(pbuf, 255) < 0) {
350                                                 strcpy(pbuf, "?");
351                                         } else {
352                                                 char *s = strchr(pbuf, '.');
353
354                                                 if (s)
355                                                         *s = 0;
356                                         }
357                                         hostname_buf = pbuf;
358                                 }
359                                 break;
360                           case '$':
361                                 c = my_euid == 0 ? '#' : '$';
362                                 break;
363 #ifdef CONFIG_FEATURE_GETUSERNAME_AND_HOMEDIR
364                           case 'w':
365                                 pbuf = pwd_buf;
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) {
371                                         pbuf = buf2;
372                                         *pbuf = '~';
373                                         strcpy(pbuf+1, pwd_buf+l);
374                                         }
375                                 break;
376 #endif
377                           case 'W':
378                                 pbuf = pwd_buf;
379                                 cp = strrchr(pbuf,'/');
380                                 if ( (cp != NULL) && (cp != pbuf) )
381                                         pbuf += (cp-pbuf)+1;
382                                 break;
383                           case '!':
384                                 snprintf(pbuf = buf2, sizeof(buf2), "%d", num_ok_lines);
385                                 break;
386                           case 'e': case 'E':     /* \e \E = \033 */
387                                 c = '\033';
388                                 break;
389                           case 'x': case 'X':
390                                 for (l = 0; l < 3;) {
391                                         int h;
392                                         buf2[l++] = *prmt_ptr;
393                                         buf2[l] = 0;
394                                         h = strtol(buf2, &pbuf, 16);
395                                         if (h > UCHAR_MAX || (pbuf - buf2) < l) {
396                                                 l--;
397                                                 break;
398                                         }
399                                         prmt_ptr++;
400                                 }
401                                 buf2[l] = 0;
402                                 c = (char)strtol(buf2, 0, 16);
403                                 if(c==0)
404                                         c = '?';
405                                 pbuf = buf;
406                                 break;
407                           case '[': case ']':
408                                 if (c == flg_not_length) {
409                                         flg_not_length = flg_not_length == '[' ? ']' : '[';
410                                         continue;
411                                 }
412                                 break;
413                           }
414                         }
415                 }
416                 if(pbuf == buf)
417                         *pbuf = c;
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);
423         }
424         if(pwd_buf!=(char *)bb_msg_unknown)
425                 free(pwd_buf);
426         cmdedit_prompt = prmt_mem_ptr;
427         put_prompt();
428 }
429 #endif
430
431
432 /* draw prompt, editor line, and clear tail */
433 static void redraw(int y, int back_cursor)
434 {
435         if (y > 0)                              /* up to start y */
436                 printf("\033[%dA", y);
437         putchar('\r');
438         put_prompt();
439         input_end();                            /* rewrite */
440         printf("\033[J");                       /* destroy tail after cursor */
441         input_backward(back_cursor);
442 }
443
444 #ifdef CONFIG_FEATURE_COMMAND_EDITING_VI
445 #define DELBUFSIZ 128
446 static char *delbuf;  /* a (malloced) place to store deleted characters */
447 static char *delp;
448 static char newdelflag;      /* whether delbuf should be reused yet */
449 #endif
450
451 /* Delete the char in front of the cursor, optionally saving it
452  * for later putback */
453 static void input_delete(int save)
454 {
455         int j = cursor;
456
457         if (j == len)
458                 return;
459
460 #ifdef CONFIG_FEATURE_COMMAND_EDITING_VI
461         if (save) {
462                 if (newdelflag) {
463                         if (!delbuf)
464                                 delbuf = malloc(DELBUFSIZ);
465                         /* safe if malloc fails */
466                         delp = delbuf;
467                         newdelflag = 0;
468                 }
469                 if (delbuf && (delp - delbuf < DELBUFSIZ))
470                         *delp++ = command_ps[j];
471         }
472 #endif
473
474         strcpy(command_ps + j, command_ps + j + 1);
475         len--;
476         input_end();                    /* rewrite new line */
477         cmdedit_set_out_char(0);        /* destroy end char */
478         input_backward(cursor - j);     /* back to old pos cursor */
479 }
480
481 #ifdef CONFIG_FEATURE_COMMAND_EDITING_VI
482 static void put(void)
483 {
484         int ocursor, j = delp - delbuf;
485         if (j == 0)
486                 return;
487         ocursor = cursor;
488         /* open hole and then fill it */
489         memmove(command_ps + cursor + j, command_ps + cursor, len - cursor + 1);
490         strncpy(command_ps + cursor, delbuf, j);
491         len += j;
492         input_end();                    /* rewrite new line */
493         input_backward(cursor-ocursor-j+1); /* at end of new text */
494 }
495 #endif
496
497 /* Delete the char in back of the cursor */
498 static void input_backspace(void)
499 {
500         if (cursor > 0) {
501                 input_backward(1);
502                 input_delete(0);
503         }
504 }
505
506
507 /* Move forward one character */
508 static void input_forward(void)
509 {
510         if (cursor < len)
511                 cmdedit_set_out_char(command_ps[cursor + 1]);
512 }
513
514 static void cmdedit_setwidth(int w, int redraw_flg)
515 {
516         cmdedit_termw = cmdedit_prmt_len + 2;
517         if (w <= cmdedit_termw) {
518                 cmdedit_termw = cmdedit_termw % w;
519         }
520         if (w > cmdedit_termw) {
521                 cmdedit_termw = w;
522
523                 if (redraw_flg) {
524                         /* new y for current cursor */
525                         int new_y = (cursor + cmdedit_prmt_len) / w;
526
527                         /* redraw */
528                         redraw((new_y >= cmdedit_y ? new_y : cmdedit_y), len - cursor);
529                         fflush(stdout);
530                 }
531         }
532 }
533
534 static void cmdedit_init(void)
535 {
536         cmdedit_prmt_len = 0;
537         if ((handlers_sets & SET_WCHG_HANDLERS) == 0) {
538                 /* emulate usage handler to set handler and call yours work */
539                 win_changed(-SIGWINCH);
540                 handlers_sets |= SET_WCHG_HANDLERS;
541         }
542
543         if ((handlers_sets & SET_ATEXIT) == 0) {
544 #ifdef CONFIG_FEATURE_GETUSERNAME_AND_HOMEDIR
545                 struct passwd *entry;
546
547                 my_euid = geteuid();
548                 entry = getpwuid(my_euid);
549                 if (entry) {
550                         user_buf = bb_xstrdup(entry->pw_name);
551                         home_pwd_buf = bb_xstrdup(entry->pw_dir);
552                 }
553 #endif
554
555 #ifdef  CONFIG_FEATURE_COMMAND_TAB_COMPLETION
556
557 #ifndef CONFIG_FEATURE_GETUSERNAME_AND_HOMEDIR
558                 my_euid = geteuid();
559 #endif
560                 my_uid = getuid();
561                 my_gid = getgid();
562 #endif  /* CONFIG_FEATURE_COMMAND_TAB_COMPLETION */
563                 handlers_sets |= SET_ATEXIT;
564                 atexit(cmdedit_reset_term);     /* be sure to do this only once */
565         }
566 }
567
568 #ifdef CONFIG_FEATURE_COMMAND_TAB_COMPLETION
569
570 static char **matches;
571 static int num_matches;
572 static char *add_char_to_match;
573
574 static void add_match(char *matched, int add_char)
575 {
576         int nm = num_matches;
577         int nm1 = nm + 1;
578
579         matches = xrealloc(matches, nm1 * sizeof(char *));
580         add_char_to_match = xrealloc(add_char_to_match, nm1);
581         matches[nm] = matched;
582         add_char_to_match[nm] = (char)add_char;
583         num_matches++;
584 }
585
586 static int is_execute(const struct stat *st)
587 {
588         if ((!my_euid && (st->st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))) ||
589                 (my_uid == st->st_uid && (st->st_mode & S_IXUSR)) ||
590                 (my_gid == st->st_gid && (st->st_mode & S_IXGRP)) ||
591                 (st->st_mode & S_IXOTH)) return TRUE;
592         return FALSE;
593 }
594
595 #ifdef CONFIG_FEATURE_COMMAND_USERNAME_COMPLETION
596
597 static void username_tab_completion(char *ud, char *with_shash_flg)
598 {
599         struct passwd *entry;
600         int userlen;
601
602         ud++;                           /* ~user/... to user/... */
603         userlen = strlen(ud);
604
605         if (with_shash_flg) {           /* "~/..." or "~user/..." */
606                 char *sav_ud = ud - 1;
607                 char *home = 0;
608                 char *temp;
609
610                 if (*ud == '/') {       /* "~/..."     */
611                         home = home_pwd_buf;
612                 } else {
613                         /* "~user/..." */
614                         temp = strchr(ud, '/');
615                         *temp = 0;              /* ~user\0 */
616                         entry = getpwnam(ud);
617                         *temp = '/';            /* restore ~user/... */
618                         ud = temp;
619                         if (entry)
620                                 home = entry->pw_dir;
621                 }
622                 if (home) {
623                         if ((userlen + strlen(home) + 1) < BUFSIZ) {
624                                 char temp2[BUFSIZ];     /* argument size */
625
626                                 /* /home/user/... */
627                                 sprintf(temp2, "%s%s", home, ud);
628                                 strcpy(sav_ud, temp2);
629                         }
630                 }
631         } else {
632                 /* "~[^/]*" */
633                 setpwent();
634
635                 while ((entry = getpwent()) != NULL) {
636                         /* Null usernames should result in all users as possible completions. */
637                         if ( /*!userlen || */ !strncmp(ud, entry->pw_name, userlen)) {
638                                 add_match(bb_xasprintf("~%s", entry->pw_name), '/');
639                         }
640                 }
641
642                 endpwent();
643         }
644 }
645 #endif  /* CONFIG_FEATURE_COMMAND_USERNAME_COMPLETION */
646
647 enum {
648         FIND_EXE_ONLY = 0,
649         FIND_DIR_ONLY = 1,
650         FIND_FILE_ONLY = 2,
651 };
652
653 #ifdef CONFIG_ASH
654 const char *cmdedit_path_lookup;
655 #else
656 #define cmdedit_path_lookup getenv("PATH")
657 #endif
658
659 static int path_parse(char ***p, int flags)
660 {
661         int npth;
662         const char *tmp;
663         const char *pth;
664
665         /* if not setenv PATH variable, to search cur dir "." */
666         if (flags != FIND_EXE_ONLY || (pth = cmdedit_path_lookup) == 0 ||
667                 /* PATH=<empty> or PATH=:<empty> */
668                 *pth == 0 || (*pth == ':' && *(pth + 1) == 0)) {
669                 return 1;
670         }
671
672         tmp = pth;
673         npth = 0;
674
675         for (;;) {
676                 npth++;                 /* count words is + 1 count ':' */
677                 tmp = strchr(tmp, ':');
678                 if (tmp) {
679                         if (*++tmp == 0)
680                                 break;  /* :<empty> */
681                 } else
682                         break;
683         }
684
685         *p = xmalloc(npth * sizeof(char *));
686
687         tmp = pth;
688         (*p)[0] = bb_xstrdup(tmp);
689         npth = 1;                       /* count words is + 1 count ':' */
690
691         for (;;) {
692                 tmp = strchr(tmp, ':');
693                 if (tmp) {
694                         (*p)[0][(tmp - pth)] = 0;       /* ':' -> '\0' */
695                         if (*++tmp == 0)
696                                 break;                  /* :<empty> */
697                 } else
698                         break;
699                 (*p)[npth++] = &(*p)[0][(tmp - pth)];   /* p[next]=p[0][&'\0'+1] */
700         }
701
702         return npth;
703 }
704
705 static char *add_quote_for_spec_chars(char *found, int add)
706 {
707         int l = 0;
708         char *s = xmalloc((strlen(found) + 1) * 2);
709
710         while (*found) {
711                 if (strchr(" `\"#$%^&*()=+{}[]:;\'|\\<>", *found))
712                         s[l++] = '\\';
713                 s[l++] = *found++;
714         }
715         if(add)
716             s[l++] = (char)add;
717         s[l] = 0;
718         return s;
719 }
720
721 static void exe_n_cwd_tab_completion(char *command, int type)
722 {
723         DIR *dir;
724         struct dirent *next;
725         char dirbuf[BUFSIZ];
726         struct stat st;
727         char *path1[1];
728         char **paths = path1;
729         int npaths;
730         int i;
731         char *found;
732         char *pfind = strrchr(command, '/');
733
734         path1[0] = ".";
735
736         if (pfind == NULL) {
737                 /* no dir, if flags==EXE_ONLY - get paths, else "." */
738                 npaths = path_parse(&paths, type);
739                 pfind = command;
740         } else {
741                 /* with dir */
742                 /* save for change */
743                 strcpy(dirbuf, command);
744                 /* set dir only */
745                 dirbuf[(pfind - command) + 1] = 0;
746 #ifdef CONFIG_FEATURE_COMMAND_USERNAME_COMPLETION
747                 if (dirbuf[0] == '~')   /* ~/... or ~user/... */
748                         username_tab_completion(dirbuf, dirbuf);
749 #endif
750                 /* "strip" dirname in command */
751                 pfind++;
752
753                 paths[0] = dirbuf;
754                 npaths = 1;                             /* only 1 dir */
755         }
756
757         for (i = 0; i < npaths; i++) {
758
759                 dir = opendir(paths[i]);
760                 if (!dir)                       /* Don't print an error */
761                         continue;
762
763                 while ((next = readdir(dir)) != NULL) {
764                         char *str_found = next->d_name;
765                         int add_chr = 0;
766
767                         /* matched ? */
768                         if (strncmp(str_found, pfind, strlen(pfind)))
769                                 continue;
770                         /* not see .name without .match */
771                         if (*str_found == '.' && *pfind == 0) {
772                                 if (*paths[i] == '/' && paths[i][1] == 0
773                                         && str_found[1] == 0) str_found = "";   /* only "/" */
774                                 else
775                                         continue;
776                         }
777                         found = concat_path_file(paths[i], str_found);
778                         /* hmm, remover in progress? */
779                         if (stat(found, &st) < 0)
780                                 goto cont;
781                         /* find with dirs ? */
782                         if (paths[i] != dirbuf)
783                                 strcpy(found, next->d_name);    /* only name */
784                         if (S_ISDIR(st.st_mode)) {
785                                 /* name is directory      */
786                                 char *e = found + strlen(found) - 1;
787
788                                 add_chr = '/';
789                                 if(*e == '/')
790                                         *e = '\0';
791                         } else {
792                                 /* not put found file if search only dirs for cd */
793                                 if (type == FIND_DIR_ONLY)
794                                         goto cont;
795                                 if (type == FIND_FILE_ONLY ||
796                                         (type == FIND_EXE_ONLY && is_execute(&st)))
797                                         add_chr = ' ';
798                         }
799                         /* Add it to the list */
800                         add_match(found, add_chr);
801                         continue;
802 cont:
803                         free(found);
804                 }
805                 closedir(dir);
806         }
807         if (paths != path1) {
808                 free(paths[0]);                 /* allocated memory only in first member */
809                 free(paths);
810         }
811 }
812
813
814 #define QUOT    (UCHAR_MAX+1)
815
816 #define collapse_pos(is, in) { \
817         memmove(int_buf+(is), int_buf+(in), (BUFSIZ+1-(is)-(in))*sizeof(int)); \
818         memmove(pos_buf+(is), pos_buf+(in), (BUFSIZ+1-(is)-(in))*sizeof(int)); }
819
820 static int find_match(char *matchBuf, int *len_with_quotes)
821 {
822         int i, j;
823         int command_mode;
824         int c, c2;
825         int int_buf[BUFSIZ + 1];
826         int pos_buf[BUFSIZ + 1];
827
828         /* set to integer dimension characters and own positions */
829         for (i = 0;; i++) {
830                 int_buf[i] = (int) ((unsigned char) matchBuf[i]);
831                 if (int_buf[i] == 0) {
832                         pos_buf[i] = -1;        /* indicator end line */
833                         break;
834                 } else
835                         pos_buf[i] = i;
836         }
837
838         /* mask \+symbol and convert '\t' to ' ' */
839         for (i = j = 0; matchBuf[i]; i++, j++)
840                 if (matchBuf[i] == '\\') {
841                         collapse_pos(j, j + 1);
842                         int_buf[j] |= QUOT;
843                         i++;
844 #ifdef CONFIG_FEATURE_NONPRINTABLE_INVERSE_PUT
845                         if (matchBuf[i] == '\t')        /* algorithm equivalent */
846                                 int_buf[j] = ' ' | QUOT;
847 #endif
848                 }
849 #ifdef CONFIG_FEATURE_NONPRINTABLE_INVERSE_PUT
850                 else if (matchBuf[i] == '\t')
851                         int_buf[j] = ' ';
852 #endif
853
854         /* mask "symbols" or 'symbols' */
855         c2 = 0;
856         for (i = 0; int_buf[i]; i++) {
857                 c = int_buf[i];
858                 if (c == '\'' || c == '"') {
859                         if (c2 == 0)
860                                 c2 = c;
861                         else {
862                                 if (c == c2)
863                                         c2 = 0;
864                                 else
865                                         int_buf[i] |= QUOT;
866                         }
867                 } else if (c2 != 0 && c != '$')
868                         int_buf[i] |= QUOT;
869         }
870
871         /* skip commands with arguments if line have commands delimiters */
872         /* ';' ';;' '&' '|' '&&' '||' but `>&' `<&' `>|' */
873         for (i = 0; int_buf[i]; i++) {
874                 c = int_buf[i];
875                 c2 = int_buf[i + 1];
876                 j = i ? int_buf[i - 1] : -1;
877                 command_mode = 0;
878                 if (c == ';' || c == '&' || c == '|') {
879                         command_mode = 1 + (c == c2);
880                         if (c == '&') {
881                                 if (j == '>' || j == '<')
882                                         command_mode = 0;
883                         } else if (c == '|' && j == '>')
884                                 command_mode = 0;
885                 }
886                 if (command_mode) {
887                         collapse_pos(0, i + command_mode);
888                         i = -1;                         /* hack incremet */
889                 }
890         }
891         /* collapse `command...` */
892         for (i = 0; int_buf[i]; i++)
893                 if (int_buf[i] == '`') {
894                         for (j = i + 1; int_buf[j]; j++)
895                                 if (int_buf[j] == '`') {
896                                         collapse_pos(i, j + 1);
897                                         j = 0;
898                                         break;
899                                 }
900                         if (j) {
901                                 /* not found close ` - command mode, collapse all previous */
902                                 collapse_pos(0, i + 1);
903                                 break;
904                         } else
905                                 i--;                    /* hack incremet */
906                 }
907
908         /* collapse (command...(command...)...) or {command...{command...}...} */
909         c = 0;                                          /* "recursive" level */
910         c2 = 0;
911         for (i = 0; int_buf[i]; i++)
912                 if (int_buf[i] == '(' || int_buf[i] == '{') {
913                         if (int_buf[i] == '(')
914                                 c++;
915                         else
916                                 c2++;
917                         collapse_pos(0, i + 1);
918                         i = -1;                         /* hack incremet */
919                 }
920         for (i = 0; pos_buf[i] >= 0 && (c > 0 || c2 > 0); i++)
921                 if ((int_buf[i] == ')' && c > 0) || (int_buf[i] == '}' && c2 > 0)) {
922                         if (int_buf[i] == ')')
923                                 c--;
924                         else
925                                 c2--;
926                         collapse_pos(0, i + 1);
927                         i = -1;                         /* hack incremet */
928                 }
929
930         /* skip first not quote space */
931         for (i = 0; int_buf[i]; i++)
932                 if (int_buf[i] != ' ')
933                         break;
934         if (i)
935                 collapse_pos(0, i);
936
937         /* set find mode for completion */
938         command_mode = FIND_EXE_ONLY;
939         for (i = 0; int_buf[i]; i++)
940                 if (int_buf[i] == ' ' || int_buf[i] == '<' || int_buf[i] == '>') {
941                         if (int_buf[i] == ' ' && command_mode == FIND_EXE_ONLY
942                                 && matchBuf[pos_buf[0]]=='c'
943                                 && matchBuf[pos_buf[1]]=='d' )
944                                 command_mode = FIND_DIR_ONLY;
945                         else {
946                                 command_mode = FIND_FILE_ONLY;
947                                 break;
948                         }
949                 }
950         /* "strlen" */
951         for (i = 0; int_buf[i]; i++);
952         /* find last word */
953         for (--i; i >= 0; i--) {
954                 c = int_buf[i];
955                 if (c == ' ' || c == '<' || c == '>' || c == '|' || c == '&') {
956                         collapse_pos(0, i + 1);
957                         break;
958                 }
959         }
960         /* skip first not quoted '\'' or '"' */
961         for (i = 0; int_buf[i] == '\'' || int_buf[i] == '"'; i++);
962         /* collapse quote or unquote // or /~ */
963         while ((int_buf[i] & ~QUOT) == '/' &&
964                         ((int_buf[i + 1] & ~QUOT) == '/'
965                          || (int_buf[i + 1] & ~QUOT) == '~')) {
966                 i++;
967         }
968
969         /* set only match and destroy quotes */
970         j = 0;
971         for (c = 0; pos_buf[i] >= 0; i++) {
972                 matchBuf[c++] = matchBuf[pos_buf[i]];
973                 j = pos_buf[i] + 1;
974         }
975         matchBuf[c] = 0;
976         /* old lenght matchBuf with quotes symbols */
977         *len_with_quotes = j ? j - pos_buf[0] : 0;
978
979         return command_mode;
980 }
981
982 /*
983    display by column original ideas from ls applet,
984    very optimize by my :)
985 */
986 static void showfiles(void)
987 {
988         int ncols, row;
989         int column_width = 0;
990         int nfiles = num_matches;
991         int nrows = nfiles;
992         char str_add_chr[2];
993         int l;
994
995         /* find the longest file name-  use that as the column width */
996         for (row = 0; row < nrows; row++) {
997                 l = strlen(matches[row]);
998                 if(add_char_to_match[row])
999                     l++;
1000                 if (column_width < l)
1001                         column_width = l;
1002         }
1003         column_width += 2;              /* min space for columns */
1004         ncols = cmdedit_termw / column_width;
1005
1006         if (ncols > 1) {
1007                 nrows /= ncols;
1008                 if(nfiles % ncols)
1009                         nrows++;        /* round up fractionals */
1010         } else {
1011                 ncols = 1;
1012         }
1013         str_add_chr[1] = 0;
1014         for (row = 0; row < nrows; row++) {
1015                 int n = row;
1016                 int nc;
1017                 int acol;
1018
1019                 for(nc = 1; nc < ncols && n+nrows < nfiles; n += nrows, nc++) {
1020                         str_add_chr[0] = add_char_to_match[n];
1021                         acol = str_add_chr[0] ? column_width - 1 : column_width;
1022                         printf("%s%s", matches[n], str_add_chr);
1023                         l = strlen(matches[n]);
1024                         while(l < acol) {
1025                             putchar(' ');
1026                             l++;
1027                         }
1028                 }
1029                 str_add_chr[0] = add_char_to_match[n];
1030                 printf("%s%s\n", matches[n], str_add_chr);
1031         }
1032 }
1033
1034
1035 static void input_tab(int *lastWasTab)
1036 {
1037         /* Do TAB completion */
1038         if (lastWasTab == 0) {          /* free all memory */
1039                 if (matches) {
1040                         while (num_matches > 0)
1041                                 free(matches[--num_matches]);
1042                         free(matches);
1043                         matches = (char **) NULL;
1044                         free(add_char_to_match);
1045                         add_char_to_match = NULL;
1046                 }
1047                 return;
1048         }
1049         if (! *lastWasTab) {
1050
1051                 char *tmp, *tmp1;
1052                 int len_found;
1053                 char matchBuf[BUFSIZ];
1054                 int find_type;
1055                 int recalc_pos;
1056
1057                 *lastWasTab = TRUE;             /* flop trigger */
1058
1059                 /* Make a local copy of the string -- up
1060                  * to the position of the cursor */
1061                 tmp = strncpy(matchBuf, command_ps, cursor);
1062                 tmp[cursor] = 0;
1063
1064                 find_type = find_match(matchBuf, &recalc_pos);
1065
1066                 /* Free up any memory already allocated */
1067                 input_tab(0);
1068
1069 #ifdef CONFIG_FEATURE_COMMAND_USERNAME_COMPLETION
1070                 /* If the word starts with `~' and there is no slash in the word,
1071                  * then try completing this word as a username. */
1072
1073                 if (matchBuf[0] == '~' && strchr(matchBuf, '/') == 0)
1074                         username_tab_completion(matchBuf, NULL);
1075                 if (!matches)
1076 #endif
1077                 /* Try to match any executable in our path and everything
1078                  * in the current working directory that matches.  */
1079                         exe_n_cwd_tab_completion(matchBuf, find_type);
1080                 /* Remove duplicate found and sort */
1081                 if(matches) {
1082                         int i, j, n, srt;
1083                         /* bubble */
1084                         n = num_matches;
1085                         for(i=0; i<(n-1); i++)
1086                             for(j=i+1; j<n; j++)
1087                                 if(matches[i]!=NULL && matches[j]!=NULL) {
1088                                     srt = strcmp(matches[i], matches[j]);
1089                                     if(srt == 0) {
1090                                         free(matches[j]);
1091                                         matches[j]=0;
1092                                     } else if(srt > 0) {
1093                                         tmp1 = matches[i];
1094                                         matches[i] = matches[j];
1095                                         matches[j] = tmp1;
1096                                         srt = add_char_to_match[i];
1097                                         add_char_to_match[i] = add_char_to_match[j];
1098                                         add_char_to_match[j] = srt;
1099                                     }
1100                                 }
1101                         j = n;
1102                         n = 0;
1103                         for(i=0; i<j; i++)
1104                             if(matches[i]) {
1105                                 matches[n]=matches[i];
1106                                 add_char_to_match[n]=add_char_to_match[i];
1107                                 n++;
1108                             }
1109                         num_matches = n;
1110                 }
1111                 /* Did we find exactly one match? */
1112                 if (!matches || num_matches > 1) {
1113
1114                         beep();
1115                         if (!matches)
1116                                 return;         /* not found */
1117                         /* find minimal match */
1118                         tmp1 = bb_xstrdup(matches[0]);
1119                         for (tmp = tmp1; *tmp; tmp++)
1120                                 for (len_found = 1; len_found < num_matches; len_found++)
1121                                         if (matches[len_found][(tmp - tmp1)] != *tmp) {
1122                                                 *tmp = 0;
1123                                                 break;
1124                                         }
1125                         if (*tmp1 == 0) {        /* have unique */
1126                                 free(tmp1);
1127                                 return;
1128                         }
1129                         tmp = add_quote_for_spec_chars(tmp1, 0);
1130                         free(tmp1);
1131                 } else {                        /* one match */
1132                         tmp = add_quote_for_spec_chars(matches[0], add_char_to_match[0]);
1133                         /* for next completion current found */
1134                         *lastWasTab = FALSE;
1135                 }
1136                 len_found = strlen(tmp);
1137                 /* have space to placed match? */
1138                 if ((len_found - strlen(matchBuf) + len) < BUFSIZ) {
1139
1140                         /* before word for match   */
1141                         command_ps[cursor - recalc_pos] = 0;
1142                         /* save   tail line        */
1143                         strcpy(matchBuf, command_ps + cursor);
1144                         /* add    match            */
1145                         strcat(command_ps, tmp);
1146                         /* add    tail             */
1147                         strcat(command_ps, matchBuf);
1148                         /* back to begin word for match    */
1149                         input_backward(recalc_pos);
1150                         /* new pos                         */
1151                         recalc_pos = cursor + len_found;
1152                         /* new len                         */
1153                         len = strlen(command_ps);
1154                         /* write out the matched command   */
1155                         redraw(cmdedit_y, len - recalc_pos);
1156                 }
1157                 free(tmp);
1158         } else {
1159                 /* Ok -- the last char was a TAB.  Since they
1160                  * just hit TAB again, print a list of all the
1161                  * available choices... */
1162                 if (matches && num_matches > 0) {
1163                         int sav_cursor = cursor;        /* change goto_new_line() */
1164
1165                         /* Go to the next line */
1166                         goto_new_line();
1167                         showfiles();
1168                         redraw(0, len - sav_cursor);
1169                 }
1170         }
1171 }
1172 #endif  /* CONFIG_FEATURE_COMMAND_TAB_COMPLETION */
1173
1174 #if MAX_HISTORY >= 1
1175 static void get_previous_history(void)
1176 {
1177         if(command_ps[0] != 0 || history[cur_history] == 0) {
1178                 free(history[cur_history]);
1179                 history[cur_history] = bb_xstrdup(command_ps);
1180         }
1181         cur_history--;
1182 }
1183
1184 static int get_next_history(void)
1185 {
1186         int ch = cur_history;
1187
1188         if (ch < n_history) {
1189                 get_previous_history(); /* save the current history line */
1190                 return (cur_history = ch+1);
1191         } else {
1192                 beep();
1193                 return 0;
1194         }
1195 }
1196
1197 #ifdef CONFIG_FEATURE_COMMAND_SAVEHISTORY
1198 void load_history ( const char *fromfile )
1199 {
1200         FILE *fp;
1201         int hi;
1202
1203         /* cleanup old */
1204
1205         for(hi = n_history; hi > 0; ) {
1206                 hi--;
1207                 free ( history [hi] );
1208         }
1209
1210         if (( fp = fopen ( fromfile, "r" ))) {
1211
1212                 for ( hi = 0; hi < MAX_HISTORY; ) {
1213                         char * hl = bb_get_chomped_line_from_file(fp);
1214                         int l;
1215
1216                         if(!hl)
1217                                 break;
1218                         l = strlen(hl);
1219                         if(l >= BUFSIZ)
1220                                 hl[BUFSIZ-1] = 0;
1221                         if(l == 0 || hl[0] == ' ') {
1222                                 free(hl);
1223                                 continue;
1224                         }
1225                         history [hi++] = hl;
1226                 }
1227                 fclose ( fp );
1228         }
1229         cur_history = n_history = hi;
1230 }
1231
1232 void save_history ( const char *tofile )
1233 {
1234         FILE *fp = fopen ( tofile, "w" );
1235
1236         if ( fp ) {
1237                 int i;
1238
1239                 for ( i = 0; i < n_history; i++ ) {
1240                         fprintf(fp, "%s\n", history [i]);
1241                 }
1242                 fclose ( fp );
1243         }
1244 }
1245 #endif
1246
1247 #endif
1248
1249 enum {
1250         ESC = 27,
1251         DEL = 127,
1252 };
1253
1254
1255 /*
1256  * This function is used to grab a character buffer
1257  * from the input file descriptor and allows you to
1258  * a string with full command editing (sort of like
1259  * a mini readline).
1260  *
1261  * The following standard commands are not implemented:
1262  * ESC-b -- Move back one word
1263  * ESC-f -- Move forward one word
1264  * ESC-d -- Delete back one word
1265  * ESC-h -- Delete forward one word
1266  * CTL-t -- Transpose two characters
1267  *
1268  * Minimalist vi-style command line editing available if configured.
1269  *  vi mode implemented 2005 by Paul Fox <pgf@foxharp.boston.ma.us>
1270  *
1271  */
1272
1273 #ifdef CONFIG_FEATURE_COMMAND_EDITING_VI
1274 static int vi_mode;
1275
1276 void setvimode ( int viflag )
1277 {
1278         vi_mode = viflag;
1279 }
1280
1281 static void
1282 vi_Word_motion(char *command, int eat)
1283 {
1284         while (cursor < len && !isspace(command[cursor]))
1285                 input_forward();
1286         if (eat) while (cursor < len && isspace(command[cursor]))
1287                 input_forward();
1288 }
1289
1290 static void
1291 vi_word_motion(char *command, int eat)
1292 {
1293         if (isalnum(command[cursor]) || command[cursor] == '_') {
1294                 while (cursor < len &&
1295                     (isalnum(command[cursor+1]) ||
1296                                 command[cursor+1] == '_'))
1297                         input_forward();
1298         } else if (ispunct(command[cursor])) {
1299                 while (cursor < len &&
1300                     (ispunct(command[cursor+1])))
1301                         input_forward();
1302         }
1303
1304         if (cursor < len)
1305                 input_forward();
1306
1307         if (eat && cursor < len && isspace(command[cursor]))
1308                 while (cursor < len && isspace(command[cursor]))
1309                         input_forward();
1310 }
1311
1312 static void
1313 vi_End_motion(char *command)
1314 {
1315         input_forward();
1316         while (cursor < len && isspace(command[cursor]))
1317                 input_forward();
1318         while (cursor < len-1 && !isspace(command[cursor+1]))
1319                 input_forward();
1320 }
1321
1322 static void
1323 vi_end_motion(char *command)
1324 {
1325         if (cursor >= len-1)
1326                 return;
1327         input_forward();
1328         while (cursor < len-1 && isspace(command[cursor]))
1329                 input_forward();
1330         if (cursor >= len-1)
1331                 return;
1332         if (isalnum(command[cursor]) || command[cursor] == '_') {
1333                 while (cursor < len-1 &&
1334                     (isalnum(command[cursor+1]) ||
1335                                 command[cursor+1] == '_'))
1336                         input_forward();
1337         } else if (ispunct(command[cursor])) {
1338                 while (cursor < len-1 &&
1339                     (ispunct(command[cursor+1])))
1340                         input_forward();
1341         }
1342 }
1343
1344 static void
1345 vi_Back_motion(char *command)
1346 {
1347         while (cursor > 0 && isspace(command[cursor-1]))
1348                 input_backward(1);
1349         while (cursor > 0 && !isspace(command[cursor-1]))
1350                 input_backward(1);
1351 }
1352
1353 static void
1354 vi_back_motion(char *command)
1355 {
1356         if (cursor <= 0)
1357                 return;
1358         input_backward(1);
1359         while (cursor > 0 && isspace(command[cursor]))
1360                 input_backward(1);
1361         if (cursor <= 0)
1362                 return;
1363         if (isalnum(command[cursor]) || command[cursor] == '_') {
1364                 while (cursor > 0 &&
1365                     (isalnum(command[cursor-1]) ||
1366                                 command[cursor-1] == '_'))
1367                         input_backward(1);
1368         } else if (ispunct(command[cursor])) {
1369                 while (cursor > 0 &&
1370                     (ispunct(command[cursor-1])))
1371                         input_backward(1);
1372         }
1373 }
1374 #endif
1375
1376 /*
1377  * the emacs and vi modes share much of the code in the big
1378  * command loop.  commands entered when in vi's command mode (aka
1379  * "escape mode") get an extra bit added to distinguish them --
1380  * this keeps them from being self-inserted.  this clutters the
1381  * big switch a bit, but keeps all the code in one place.
1382  */
1383
1384 #define vbit 0x100
1385
1386 /* leave out the "vi-mode"-only case labels if vi editing isn't
1387  * configured. */
1388 #define vi_case(caselabel) USE_FEATURE_COMMAND_EDITING(caselabel)
1389
1390 /* convert uppercase ascii to equivalent control char, for readability */
1391 #define CNTRL(uc_char) ((uc_char) - 0x40)
1392
1393
1394 int cmdedit_read_input(char *prompt, char command[BUFSIZ])
1395 {
1396
1397         int break_out = 0;
1398         int lastWasTab = FALSE;
1399         unsigned char c;
1400         unsigned int ic;
1401 #ifdef CONFIG_FEATURE_COMMAND_EDITING_VI
1402         unsigned int prevc;
1403         int vi_cmdmode = 0;
1404 #endif
1405         /* prepare before init handlers */
1406         cmdedit_y = 0;  /* quasireal y, not true work if line > xt*yt */
1407         len = 0;
1408         command_ps = command;
1409
1410         getTermSettings(0, (void *) &initial_settings);
1411         memcpy(&new_settings, &initial_settings, sizeof(struct termios));
1412         new_settings.c_lflag &= ~ICANON;        /* unbuffered input */
1413         /* Turn off echoing and CTRL-C, so we can trap it */
1414         new_settings.c_lflag &= ~(ECHO | ECHONL | ISIG);
1415         /* Hmm, in linux c_cc[] not parsed if set ~ICANON */
1416         new_settings.c_cc[VMIN] = 1;
1417         new_settings.c_cc[VTIME] = 0;
1418         /* Turn off CTRL-C, so we can trap it */
1419 #       ifndef _POSIX_VDISABLE
1420 #               define _POSIX_VDISABLE '\0'
1421 #       endif
1422         new_settings.c_cc[VINTR] = _POSIX_VDISABLE;
1423         command[0] = 0;
1424
1425         setTermSettings(0, (void *) &new_settings);
1426         handlers_sets |= SET_RESET_TERM;
1427
1428         /* Now initialize things */
1429         cmdedit_init();
1430         /* Print out the command prompt */
1431         parse_prompt(prompt);
1432
1433         while (1) {
1434
1435                 fflush(stdout);                 /* buffered out to fast */
1436
1437                 if (safe_read(0, &c, 1) < 1)
1438                         /* if we can't read input then exit */
1439                         goto prepare_to_die;
1440
1441                 ic = c;
1442
1443 #ifdef CONFIG_FEATURE_COMMAND_EDITING_VI
1444                 newdelflag = 1;
1445                 if (vi_cmdmode)
1446                         ic |= vbit;
1447 #endif
1448                 switch (ic)
1449                 {
1450                 case '\n':
1451                 case '\r':
1452                 vi_case( case '\n'|vbit: )
1453                 vi_case( case '\r'|vbit: )
1454                         /* Enter */
1455                         goto_new_line();
1456                         break_out = 1;
1457                         break;
1458                 case CNTRL('A'):
1459                 vi_case( case '0'|vbit: )
1460                         /* Control-a -- Beginning of line */
1461                         input_backward(cursor);
1462                         break;
1463                 case CNTRL('B'):
1464                 vi_case( case 'h'|vbit: )
1465                 vi_case( case '\b'|vbit: )
1466                 vi_case( case DEL|vbit: )
1467                         /* Control-b -- Move back one character */
1468                         input_backward(1);
1469                         break;
1470                 case CNTRL('C'):
1471                 vi_case( case CNTRL('C')|vbit: )
1472                         /* Control-c -- stop gathering input */
1473                         goto_new_line();
1474 #ifndef CONFIG_ASH
1475                         command[0] = 0;
1476                         len = 0;
1477                         lastWasTab = FALSE;
1478                         put_prompt();
1479 #else
1480                         len = 0;
1481                         break_out = -1; /* to control traps */
1482 #endif
1483                         break;
1484                 case CNTRL('D'):
1485                         /* Control-d -- Delete one character, or exit
1486                          * if the len=0 and no chars to delete */
1487                         if (len == 0) {
1488                                         errno = 0;
1489 prepare_to_die:
1490 #if !defined(CONFIG_ASH)
1491                                 printf("exit");
1492                                 goto_new_line();
1493                                 /* cmdedit_reset_term() called in atexit */
1494                                 exit(EXIT_SUCCESS);
1495 #else
1496                                 /* to control stopped jobs */
1497                                 len = break_out = -1;
1498                                 break;
1499 #endif
1500                         } else {
1501                                 input_delete(0);
1502                         }
1503                         break;
1504                 case CNTRL('E'):
1505                 vi_case( case '$'|vbit: )
1506                         /* Control-e -- End of line */
1507                         input_end();
1508                         break;
1509                 case CNTRL('F'):
1510                 vi_case( case 'l'|vbit: )
1511                 vi_case( case ' '|vbit: )
1512                         /* Control-f -- Move forward one character */
1513                         input_forward();
1514                         break;
1515                 case '\b':
1516                 case DEL:
1517                         /* Control-h and DEL */
1518                         input_backspace();
1519                         break;
1520                 case '\t':
1521 #ifdef CONFIG_FEATURE_COMMAND_TAB_COMPLETION
1522                         input_tab(&lastWasTab);
1523 #endif
1524                         break;
1525                 case CNTRL('K'):
1526                         /* Control-k -- clear to end of line */
1527                         *(command + cursor) = 0;
1528                         len = cursor;
1529                         printf("\033[J");
1530                         break;
1531                 case CNTRL('L'):
1532                 vi_case( case CNTRL('L')|vbit: )
1533                         /* Control-l -- clear screen */
1534                         printf("\033[H");
1535                         redraw(0, len-cursor);
1536                         break;
1537 #if MAX_HISTORY >= 1
1538                 case CNTRL('N'):
1539                 vi_case( case CNTRL('N')|vbit: )
1540                 vi_case( case 'j'|vbit: )
1541                         /* Control-n -- Get next command in history */
1542                         if (get_next_history())
1543                                 goto rewrite_line;
1544                         break;
1545                 case CNTRL('P'):
1546                 vi_case( case CNTRL('P')|vbit: )
1547                 vi_case( case 'k'|vbit: )
1548                         /* Control-p -- Get previous command from history */
1549                         if (cur_history > 0) {
1550                                 get_previous_history();
1551                                 goto rewrite_line;
1552                         } else {
1553                                 beep();
1554                         }
1555                         break;
1556 #endif
1557                 case CNTRL('U'):
1558                 vi_case( case CNTRL('U')|vbit: )
1559                         /* Control-U -- Clear line before cursor */
1560                         if (cursor) {
1561                                 strcpy(command, command + cursor);
1562                                 redraw(cmdedit_y, len -= cursor);
1563                         }
1564                         break;
1565                 case CNTRL('W'):
1566                 vi_case( case CNTRL('W')|vbit: )
1567                         /* Control-W -- Remove the last word */
1568                         while (cursor > 0 && isspace(command[cursor-1]))
1569                                 input_backspace();
1570                         while (cursor > 0 &&!isspace(command[cursor-1]))
1571                                 input_backspace();
1572                         break;
1573 #if CONFIG_FEATURE_COMMAND_EDITING_VI
1574                 case 'i'|vbit:
1575                         vi_cmdmode = 0;
1576                         break;
1577                 case 'I'|vbit:
1578                         input_backward(cursor);
1579                         vi_cmdmode = 0;
1580                         break;
1581                 case 'a'|vbit:
1582                         input_forward();
1583                         vi_cmdmode = 0;
1584                         break;
1585                 case 'A'|vbit:
1586                         input_end();
1587                         vi_cmdmode = 0;
1588                         break;
1589                 case 'x'|vbit:
1590                         input_delete(1);
1591                         break;
1592                 case 'X'|vbit:
1593                         if (cursor > 0) {
1594                                 input_backward(1);
1595                                 input_delete(1);
1596                         }
1597                         break;
1598                 case 'W'|vbit:
1599                         vi_Word_motion(command, 1);
1600                         break;
1601                 case 'w'|vbit:
1602                         vi_word_motion(command, 1);
1603                         break;
1604                 case 'E'|vbit:
1605                         vi_End_motion(command);
1606                         break;
1607                 case 'e'|vbit:
1608                         vi_end_motion(command);
1609                         break;
1610                 case 'B'|vbit:
1611                         vi_Back_motion(command);
1612                         break;
1613                 case 'b'|vbit:
1614                         vi_back_motion(command);
1615                         break;
1616                 case 'C'|vbit:
1617                         vi_cmdmode = 0;
1618                         /* fall through */
1619                 case 'D'|vbit:
1620                         goto clear_to_eol;
1621
1622                 case 'c'|vbit:
1623                         vi_cmdmode = 0;
1624                         /* fall through */
1625                 case 'd'|vbit:
1626                         {
1627                         int nc, sc;
1628                         sc = cursor;
1629                         prevc = ic;
1630                         if (safe_read(0, &c, 1) < 1)
1631                                 goto prepare_to_die;
1632                         if (c == (prevc & 0xff)) {
1633                             /* "cc", "dd" */
1634                             input_backward(cursor);
1635                             goto clear_to_eol;
1636                             break;
1637                         }
1638                         switch(c) {
1639                         case 'w':
1640                         case 'W':
1641                         case 'e':
1642                         case 'E':
1643                             switch (c) {
1644                             case 'w':   /* "dw", "cw" */
1645                                     vi_word_motion(command, vi_cmdmode);
1646                                     break;
1647                             case 'W':   /* 'dW', 'cW' */
1648                                     vi_Word_motion(command, vi_cmdmode);
1649                                     break;
1650                             case 'e':   /* 'de', 'ce' */
1651                                     vi_end_motion(command);
1652                                     input_forward();
1653                                     break;
1654                             case 'E':   /* 'dE', 'cE' */
1655                                     vi_End_motion(command);
1656                                     input_forward();
1657                                     break;
1658                             }
1659                             nc = cursor;
1660                             input_backward(cursor - sc);
1661                             while (nc-- > cursor)
1662                                     input_delete(1);
1663                             break;
1664                         case 'b':  /* "db", "cb" */
1665                         case 'B':  /* implemented as B */
1666                             if (c == 'b')
1667                                     vi_back_motion(command);
1668                             else
1669                                     vi_Back_motion(command);
1670                             while (sc-- > cursor)
1671                                     input_delete(1);
1672                             break;
1673                         case ' ':  /* "d ", "c " */
1674                             input_delete(1);
1675                             break;
1676                         case '$':  /* "d$", "c$" */
1677                         clear_to_eol:
1678                             while (cursor < len)
1679                                     input_delete(1);
1680                             break;
1681                         }
1682                         }
1683                         break;
1684                 case 'p'|vbit:
1685                         input_forward();
1686                         /* fallthrough */
1687                 case 'P'|vbit:
1688                         put();
1689                         break;
1690                 case 'r'|vbit:
1691                         if (safe_read(0, &c, 1) < 1)
1692                                 goto prepare_to_die;
1693                         if (c == 0)
1694                                 beep();
1695                         else {
1696                                 *(command + cursor) = c;
1697                                 putchar(c);
1698                                 putchar('\b');
1699                         }
1700                         break;
1701 #endif /* CONFIG_FEATURE_COMMAND_EDITING_VI */
1702
1703                 case ESC:
1704
1705 #if CONFIG_FEATURE_COMMAND_EDITING_VI
1706                         if (vi_mode) {
1707                                 /* ESC: insert mode --> command mode */
1708                                 vi_cmdmode = 1;
1709                                 input_backward(1);
1710                                 break;
1711                         }
1712 #endif
1713                         /* escape sequence follows */
1714                         if (safe_read(0, &c, 1) < 1)
1715                                 goto prepare_to_die;
1716                         /* different vt100 emulations */
1717                         if (c == '[' || c == 'O') {
1718                 vi_case( case '['|vbit: )
1719                 vi_case( case 'O'|vbit: )
1720                                 if (safe_read(0, &c, 1) < 1)
1721                                         goto prepare_to_die;
1722                         }
1723                         if (c >= '1' && c <= '9') {
1724                                 unsigned char dummy;
1725
1726                                 if (safe_read(0, &dummy, 1) < 1)
1727                                         goto prepare_to_die;
1728                                 if(dummy != '~')
1729                                         c = 0;
1730                         }
1731                         switch (c) {
1732 #ifdef CONFIG_FEATURE_COMMAND_TAB_COMPLETION
1733                         case '\t':                      /* Alt-Tab */
1734
1735                                 input_tab(&lastWasTab);
1736                                 break;
1737 #endif
1738 #if MAX_HISTORY >= 1
1739                         case 'A':
1740                                 /* Up Arrow -- Get previous command from history */
1741                                 if (cur_history > 0) {
1742                                         get_previous_history();
1743                                         goto rewrite_line;
1744                                 } else {
1745                                         beep();
1746                                 }
1747                                 break;
1748                         case 'B':
1749                                 /* Down Arrow -- Get next command in history */
1750                                 if (!get_next_history())
1751                                         break;
1752                                 /* Rewrite the line with the selected history item */
1753 rewrite_line:
1754                                 /* change command */
1755                                 len = strlen(strcpy(command, history[cur_history]));
1756                                 /* redraw and go to eol (bol, in vi */
1757 #if CONFIG_FEATURE_COMMAND_EDITING_VI
1758                                 redraw(cmdedit_y, vi_mode ? 9999:0);
1759 #else
1760                                 redraw(cmdedit_y, 0);
1761 #endif
1762                                 break;
1763 #endif
1764                         case 'C':
1765                                 /* Right Arrow -- Move forward one character */
1766                                 input_forward();
1767                                 break;
1768                         case 'D':
1769                                 /* Left Arrow -- Move back one character */
1770                                 input_backward(1);
1771                                 break;
1772                         case '3':
1773                                 /* Delete */
1774                                 input_delete(0);
1775                                 break;
1776                         case '1':
1777                         case 'H':
1778                                 /* <Home> */
1779                                 input_backward(cursor);
1780                                 break;
1781                         case '4':
1782                         case 'F':
1783                                 /* <End> */
1784                                 input_end();
1785                                 break;
1786                         default:
1787                                 c = 0;
1788                                 beep();
1789                         }
1790                         break;
1791
1792                 default:        /* If it's regular input, do the normal thing */
1793 #ifdef CONFIG_FEATURE_NONPRINTABLE_INVERSE_PUT
1794                         /* Control-V -- Add non-printable symbol */
1795                         if (c == CNTRL('V')) {
1796                                 if (safe_read(0, &c, 1) < 1)
1797                                         goto prepare_to_die;
1798                                 if (c == 0) {
1799                                         beep();
1800                                         break;
1801                                 }
1802                         } else
1803 #endif
1804                         {
1805 #if CONFIG_FEATURE_COMMAND_EDITING_VI
1806                                 if (vi_cmdmode)  /* don't self-insert */
1807                                         break;
1808 #endif
1809                                 if (!Isprint(c)) /* Skip non-printable characters */
1810                                         break;
1811                         }
1812
1813                         if (len >= (BUFSIZ - 2))        /* Need to leave space for enter */
1814                                 break;
1815
1816                         len++;
1817
1818                         if (cursor == (len - 1)) {      /* Append if at the end of the line */
1819                                 *(command + cursor) = c;
1820                                 *(command + cursor + 1) = 0;
1821                                 cmdedit_set_out_char(0);
1822                         } else {                        /* Insert otherwise */
1823                                 int sc = cursor;
1824
1825                                 memmove(command + sc + 1, command + sc, len - sc);
1826                                 *(command + sc) = c;
1827                                 sc++;
1828                                 /* rewrite from cursor */
1829                                 input_end();
1830                                 /* to prev x pos + 1 */
1831                                 input_backward(cursor - sc);
1832                         }
1833
1834                         break;
1835                 }
1836                 if (break_out)                  /* Enter is the command terminator, no more input. */
1837                         break;
1838
1839                 if (c != '\t')
1840                         lastWasTab = FALSE;
1841         }
1842
1843         setTermSettings(0, (void *) &initial_settings);
1844         handlers_sets &= ~SET_RESET_TERM;
1845
1846 #if MAX_HISTORY >= 1
1847         /* Handle command history log */
1848         /* cleanup may be saved current command line */
1849         if (len> 0) {                                      /* no put empty line */
1850                 int i = n_history;
1851
1852                 free(history[MAX_HISTORY]);
1853                 history[MAX_HISTORY] = 0;
1854                         /* After max history, remove the oldest command */
1855                 if (i >= MAX_HISTORY) {
1856                         free(history[0]);
1857                         for(i = 0; i < (MAX_HISTORY-1); i++)
1858                                 history[i] = history[i+1];
1859                 }
1860                 history[i++] = bb_xstrdup(command);
1861                 cur_history = i;
1862                 n_history = i;
1863 #if defined(CONFIG_FEATURE_SH_FANCY_PROMPT)
1864                 num_ok_lines++;
1865 #endif
1866         }
1867 #else  /* MAX_HISTORY < 1 */
1868 #if defined(CONFIG_FEATURE_SH_FANCY_PROMPT)
1869         if (len > 0) {              /* no put empty line */
1870                 num_ok_lines++;
1871         }
1872 #endif
1873 #endif  /* MAX_HISTORY >= 1 */
1874         if (break_out > 0) {
1875                 command[len++] = '\n';          /* set '\n' */
1876                 command[len] = 0;
1877         }
1878 #if defined(CONFIG_FEATURE_CLEAN_UP) && defined(CONFIG_FEATURE_COMMAND_TAB_COMPLETION)
1879         input_tab(0);                           /* strong free */
1880 #endif
1881 #if defined(CONFIG_FEATURE_SH_FANCY_PROMPT)
1882         free(cmdedit_prompt);
1883 #endif
1884         cmdedit_reset_term();
1885         return len;
1886 }
1887
1888
1889
1890 #endif  /* CONFIG_FEATURE_COMMAND_EDITING */
1891
1892
1893 #ifdef TEST
1894
1895 const char *bb_applet_name = "debug stuff usage";
1896
1897 #ifdef CONFIG_FEATURE_NONPRINTABLE_INVERSE_PUT
1898 #include <locale.h>
1899 #endif
1900
1901 int main(int argc, char **argv)
1902 {
1903         char buff[BUFSIZ];
1904         char *prompt =
1905 #if defined(CONFIG_FEATURE_SH_FANCY_PROMPT)
1906                 "\\[\\033[32;1m\\]\\u@\\[\\x1b[33;1m\\]\\h:\
1907 \\[\\033[34;1m\\]\\w\\[\\033[35;1m\\] \
1908 \\!\\[\\e[36;1m\\]\\$ \\[\\E[0m\\]";
1909 #else
1910                 "% ";
1911 #endif
1912
1913 #ifdef CONFIG_FEATURE_NONPRINTABLE_INVERSE_PUT
1914         setlocale(LC_ALL, "");
1915 #endif
1916         while(1) {
1917                 int l;
1918                 l = cmdedit_read_input(prompt, buff);
1919                 if(l > 0 && buff[l-1] == '\n') {
1920                         buff[l-1] = 0;
1921                         printf("*** cmdedit_read_input() returned line =%s=\n", buff);
1922                 } else {
1923                         break;
1924                 }
1925         }
1926         printf("*** cmdedit_read_input() detect ^D\n");
1927         return 0;
1928 }
1929
1930 #endif  /* TEST */