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