a9fc84798622019ded8b42920e1d07e7da41d624
[platform/upstream/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                 if (initial_settings.c_lflag & ECHO) 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 void out1str(const char *s)
251 {
252         if ( s )
253                 fputs(s, stdout);
254 }
255
256 static 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                 else
275                         printf("\033[%dD", num);
276         } else {
277                 int count_y;
278
279                 if (cmdedit_x) {
280                         putchar('\r');          /* back to first terminal pos.  */
281                         num -= cmdedit_x;       /* set previous backward        */
282                 }
283                 count_y = 1 + num / cmdedit_termw;
284                 printf("\033[%dA", count_y);
285                 cmdedit_y -= count_y;
286                 /*  require  forward  after  uping   */
287                 cmdedit_x = cmdedit_termw * count_y - num;
288                 printf("\033[%dC", cmdedit_x);  /* set term cursor   */
289         }
290 }
291
292 static void put_prompt(void)
293 {
294         out1str(cmdedit_prompt);
295         cmdedit_x = cmdedit_prmt_len;   /* count real x terminal position */
296         cursor = 0;
297         cmdedit_y = 0;                  /* new quasireal y */
298 }
299
300 #ifndef CONFIG_FEATURE_SH_FANCY_PROMPT
301 static void parse_prompt(const char *prmt_ptr)
302 {
303         cmdedit_prompt = prmt_ptr;
304         cmdedit_prmt_len = strlen(prmt_ptr);
305         put_prompt();
306 }
307 #else
308 static void parse_prompt(const char *prmt_ptr)
309 {
310         int prmt_len = 0;
311         size_t cur_prmt_len = 0;
312         char  flg_not_length = '[';
313         char *prmt_mem_ptr = xzalloc(1);
314         char *pwd_buf = xgetcwd(0);
315         char  buf2[PATH_MAX + 1];
316         char  buf[2];
317         char  c;
318         char *pbuf;
319
320         if (!pwd_buf) {
321                 pwd_buf=(char *)bb_msg_unknown;
322         }
323
324         while (*prmt_ptr) {
325                 pbuf    = buf;
326                 pbuf[1] = 0;
327                 c = *prmt_ptr++;
328                 if (c == '\\') {
329                         const char *cp = prmt_ptr;
330                         int l;
331
332                         c = bb_process_escape_sequence(&prmt_ptr);
333                         if(prmt_ptr==cp) {
334                           if (*cp == 0)
335                                 break;
336                           c = *prmt_ptr++;
337                           switch (c) {
338 #ifdef CONFIG_FEATURE_GETUSERNAME_AND_HOMEDIR
339                           case 'u':
340                                 pbuf = user_buf;
341                                 break;
342 #endif
343                           case 'h':
344                                 pbuf = hostname_buf;
345                                 if (pbuf == 0) {
346                                         pbuf = xzalloc(256);
347                                         if (gethostname(pbuf, 255) < 0) {
348                                                 strcpy(pbuf, "?");
349                                         } else {
350                                                 char *s = strchr(pbuf, '.');
351
352                                                 if (s)
353                                                         *s = 0;
354                                         }
355                                         hostname_buf = pbuf;
356                                 }
357                                 break;
358                           case '$':
359                                 c = my_euid == 0 ? '#' : '$';
360                                 break;
361 #ifdef CONFIG_FEATURE_GETUSERNAME_AND_HOMEDIR
362                           case 'w':
363                                 pbuf = pwd_buf;
364                                 l = strlen(home_pwd_buf);
365                                 if (home_pwd_buf[0] != 0 &&
366                                     strncmp(home_pwd_buf, pbuf, l) == 0 &&
367                                     (pbuf[l]=='/' || pbuf[l]=='\0') &&
368                                     strlen(pwd_buf+l)<PATH_MAX) {
369                                         pbuf = buf2;
370                                         *pbuf = '~';
371                                         strcpy(pbuf+1, pwd_buf+l);
372                                 }
373                                 break;
374 #endif
375                           case 'W':
376                                 pbuf = pwd_buf;
377                                 cp = strrchr(pbuf,'/');
378                                 if ( (cp != NULL) && (cp != pbuf) )
379                                         pbuf += (cp-pbuf)+1;
380                                 break;
381                           case '!':
382                                 snprintf(pbuf = buf2, sizeof(buf2), "%d", num_ok_lines);
383                                 break;
384                           case 'e': case 'E':     /* \e \E = \033 */
385                                 c = '\033';
386                                 break;
387                           case 'x': case 'X':
388                                 for (l = 0; l < 3;) {
389                                         int h;
390                                         buf2[l++] = *prmt_ptr;
391                                         buf2[l] = 0;
392                                         h = strtol(buf2, &pbuf, 16);
393                                         if (h > UCHAR_MAX || (pbuf - buf2) < l) {
394                                                 l--;
395                                                 break;
396                                         }
397                                         prmt_ptr++;
398                                 }
399                                 buf2[l] = 0;
400                                 c = (char)strtol(buf2, 0, 16);
401                                 if(c==0)
402                                         c = '?';
403                                 pbuf = buf;
404                                 break;
405                           case '[': case ']':
406                                 if (c == flg_not_length) {
407                                         flg_not_length = flg_not_length == '[' ? ']' : '[';
408                                         continue;
409                                 }
410                                 break;
411                           }
412                         }
413                 }
414                 if(pbuf == buf)
415                         *pbuf = c;
416                 cur_prmt_len = strlen(pbuf);
417                 prmt_len += cur_prmt_len;
418                 if (flg_not_length != ']')
419                         cmdedit_prmt_len += cur_prmt_len;
420                 prmt_mem_ptr = strcat(xrealloc(prmt_mem_ptr, prmt_len+1), pbuf);
421         }
422         if(pwd_buf!=(char *)bb_msg_unknown)
423                 free(pwd_buf);
424         cmdedit_prompt = prmt_mem_ptr;
425         put_prompt();
426 }
427 #endif
428
429
430 /* draw prompt, editor line, and clear tail */
431 static void redraw(int y, int back_cursor)
432 {
433         if (y > 0)                              /* up to start y */
434                 printf("\033[%dA", y);
435         putchar('\r');
436         put_prompt();
437         input_end();                            /* rewrite */
438         printf("\033[J");                       /* destroy tail after cursor */
439         input_backward(back_cursor);
440 }
441
442 #ifdef CONFIG_FEATURE_COMMAND_EDITING_VI
443 #define DELBUFSIZ 128
444 static char *delbuf;  /* a (malloced) place to store deleted characters */
445 static char *delp;
446 static char newdelflag;      /* whether delbuf should be reused yet */
447 #endif
448
449 /* Delete the char in front of the cursor, optionally saving it
450  * for later putback */
451 static void input_delete(int save)
452 {
453         int j = cursor;
454
455         if (j == len)
456                 return;
457
458 #ifdef CONFIG_FEATURE_COMMAND_EDITING_VI
459         if (save) {
460                 if (newdelflag) {
461                         if (!delbuf)
462                                 delbuf = malloc(DELBUFSIZ);
463                         /* safe if malloc fails */
464                         delp = delbuf;
465                         newdelflag = 0;
466                 }
467                 if (delbuf && (delp - delbuf < DELBUFSIZ))
468                         *delp++ = command_ps[j];
469         }
470 #endif
471
472         strcpy(command_ps + j, command_ps + j + 1);
473         len--;
474         input_end();                    /* rewrite new line */
475         cmdedit_set_out_char(0);        /* destroy end char */
476         input_backward(cursor - j);     /* back to old pos cursor */
477 }
478
479 #ifdef CONFIG_FEATURE_COMMAND_EDITING_VI
480 static void put(void)
481 {
482         int ocursor, j = delp - delbuf;
483         if (j == 0)
484                 return;
485         ocursor = cursor;
486         /* open hole and then fill it */
487         memmove(command_ps + cursor + j, command_ps + cursor, len - cursor + 1);
488         strncpy(command_ps + cursor, delbuf, j);
489         len += j;
490         input_end();                    /* rewrite new line */
491         input_backward(cursor-ocursor-j+1); /* at end of new text */
492 }
493 #endif
494
495 /* Delete the char in back of the cursor */
496 static void input_backspace(void)
497 {
498         if (cursor > 0) {
499                 input_backward(1);
500                 input_delete(0);
501         }
502 }
503
504
505 /* Move forward one character */
506 static void input_forward(void)
507 {
508         if (cursor < len)
509                 cmdedit_set_out_char(command_ps[cursor + 1]);
510 }
511
512 static void cmdedit_setwidth(int w, int redraw_flg)
513 {
514         cmdedit_termw = cmdedit_prmt_len + 2;
515         if (w <= cmdedit_termw) {
516                 cmdedit_termw = cmdedit_termw % w;
517         }
518         if (w > cmdedit_termw) {
519                 cmdedit_termw = w;
520
521                 if (redraw_flg) {
522                         /* new y for current cursor */
523                         int new_y = (cursor + cmdedit_prmt_len) / w;
524
525                         /* redraw */
526                         redraw((new_y >= cmdedit_y ? new_y : cmdedit_y), len - cursor);
527                         fflush(stdout);
528                 }
529         }
530 }
531
532 static void cmdedit_init(void)
533 {
534         cmdedit_prmt_len = 0;
535         if ((handlers_sets & SET_WCHG_HANDLERS) == 0) {
536                 /* emulate usage handler to set handler and call yours work */
537                 win_changed(-SIGWINCH);
538                 handlers_sets |= SET_WCHG_HANDLERS;
539         }
540
541         if ((handlers_sets & SET_ATEXIT) == 0) {
542 #ifdef CONFIG_FEATURE_GETUSERNAME_AND_HOMEDIR
543                 struct passwd *entry;
544
545                 my_euid = geteuid();
546                 entry = getpwuid(my_euid);
547                 if (entry) {
548                         user_buf = xstrdup(entry->pw_name);
549                         home_pwd_buf = xstrdup(entry->pw_dir);
550                 }
551 #endif
552
553 #ifdef  CONFIG_FEATURE_COMMAND_TAB_COMPLETION
554
555 #ifndef CONFIG_FEATURE_GETUSERNAME_AND_HOMEDIR
556                 my_euid = geteuid();
557 #endif
558                 my_uid = getuid();
559                 my_gid = getgid();
560 #endif  /* CONFIG_FEATURE_COMMAND_TAB_COMPLETION */
561                 handlers_sets |= SET_ATEXIT;
562                 atexit(cmdedit_reset_term);     /* be sure to do this only once */
563         }
564 }
565
566 #ifdef CONFIG_FEATURE_COMMAND_TAB_COMPLETION
567
568 static char **matches;
569 static int num_matches;
570 static char *add_char_to_match;
571
572 static void add_match(char *matched, int add_char)
573 {
574         int nm = num_matches;
575         int nm1 = nm + 1;
576
577         matches = xrealloc(matches, nm1 * sizeof(char *));
578         add_char_to_match = xrealloc(add_char_to_match, nm1);
579         matches[nm] = matched;
580         add_char_to_match[nm] = (char)add_char;
581         num_matches++;
582 }
583
584 static int is_execute(const struct stat *st)
585 {
586         if ((!my_euid && (st->st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))) ||
587                 (my_uid == st->st_uid && (st->st_mode & S_IXUSR)) ||
588                 (my_gid == st->st_gid && (st->st_mode & S_IXGRP)) ||
589                 (st->st_mode & S_IXOTH)) return TRUE;
590         return FALSE;
591 }
592
593 #ifdef CONFIG_FEATURE_COMMAND_USERNAME_COMPLETION
594
595 static void username_tab_completion(char *ud, char *with_shash_flg)
596 {
597         struct passwd *entry;
598         int userlen;
599
600         ud++;                           /* ~user/... to user/... */
601         userlen = strlen(ud);
602
603         if (with_shash_flg) {           /* "~/..." or "~user/..." */
604                 char *sav_ud = ud - 1;
605                 char *home = 0;
606                 char *temp;
607
608                 if (*ud == '/') {       /* "~/..."     */
609                         home = home_pwd_buf;
610                 } else {
611                         /* "~user/..." */
612                         temp = strchr(ud, '/');
613                         *temp = 0;              /* ~user\0 */
614                         entry = getpwnam(ud);
615                         *temp = '/';            /* restore ~user/... */
616                         ud = temp;
617                         if (entry)
618                                 home = entry->pw_dir;
619                 }
620                 if (home) {
621                         if ((userlen + strlen(home) + 1) < BUFSIZ) {
622                                 char temp2[BUFSIZ];     /* argument size */
623
624                                 /* /home/user/... */
625                                 sprintf(temp2, "%s%s", home, ud);
626                                 strcpy(sav_ud, temp2);
627                         }
628                 }
629         } else {
630                 /* "~[^/]*" */
631                 setpwent();
632
633                 while ((entry = getpwent()) != NULL) {
634                         /* Null usernames should result in all users as possible completions. */
635                         if ( /*!userlen || */ !strncmp(ud, entry->pw_name, userlen)) {
636                                 add_match(xasprintf("~%s", entry->pw_name), '/');
637                         }
638                 }
639
640                 endpwent();
641         }
642 }
643 #endif  /* CONFIG_FEATURE_COMMAND_USERNAME_COMPLETION */
644
645 enum {
646         FIND_EXE_ONLY = 0,
647         FIND_DIR_ONLY = 1,
648         FIND_FILE_ONLY = 2,
649 };
650
651 #ifdef CONFIG_ASH
652 const char *cmdedit_path_lookup;
653 #else
654 #define cmdedit_path_lookup getenv("PATH")
655 #endif
656
657 static int path_parse(char ***p, int flags)
658 {
659         int npth;
660         const char *tmp;
661         const char *pth;
662
663         /* if not setenv PATH variable, to search cur dir "." */
664         if (flags != FIND_EXE_ONLY || (pth = cmdedit_path_lookup) == 0 ||
665                 /* PATH=<empty> or PATH=:<empty> */
666                 *pth == 0 || (*pth == ':' && *(pth + 1) == 0)) {
667                 return 1;
668         }
669
670         tmp = pth;
671         npth = 0;
672
673         for (;;) {
674                 npth++;                 /* count words is + 1 count ':' */
675                 tmp = strchr(tmp, ':');
676                 if (tmp) {
677                         if (*++tmp == 0)
678                                 break;  /* :<empty> */
679                 } else
680                         break;
681         }
682
683         *p = xmalloc(npth * sizeof(char *));
684
685         tmp = pth;
686         (*p)[0] = xstrdup(tmp);
687         npth = 1;                       /* count words is + 1 count ':' */
688
689         for (;;) {
690                 tmp = strchr(tmp, ':');
691                 if (tmp) {
692                         (*p)[0][(tmp - pth)] = 0;       /* ':' -> '\0' */
693                         if (*++tmp == 0)
694                                 break;                  /* :<empty> */
695                 } else
696                         break;
697                 (*p)[npth++] = &(*p)[0][(tmp - pth)];   /* p[next]=p[0][&'\0'+1] */
698         }
699
700         return npth;
701 }
702
703 static char *add_quote_for_spec_chars(char *found, int add)
704 {
705         int l = 0;
706         char *s = xmalloc((strlen(found) + 1) * 2);
707
708         while (*found) {
709                 if (strchr(" `\"#$%^&*()=+{}[]:;\'|\\<>", *found))
710                         s[l++] = '\\';
711                 s[l++] = *found++;
712         }
713         if(add)
714                 s[l++] = (char)add;
715         s[l] = 0;
716         return s;
717 }
718
719 static void exe_n_cwd_tab_completion(char *command, int type)
720 {
721         DIR *dir;
722         struct dirent *next;
723         char dirbuf[BUFSIZ];
724         struct stat st;
725         char *path1[1];
726         char **paths = path1;
727         int npaths;
728         int i;
729         char *found;
730         char *pfind = strrchr(command, '/');
731
732         path1[0] = ".";
733
734         if (pfind == NULL) {
735                 /* no dir, if flags==EXE_ONLY - get paths, else "." */
736                 npaths = path_parse(&paths, type);
737                 pfind = command;
738         } else {
739                 /* with dir */
740                 /* save for change */
741                 strcpy(dirbuf, command);
742                 /* set dir only */
743                 dirbuf[(pfind - command) + 1] = 0;
744 #ifdef CONFIG_FEATURE_COMMAND_USERNAME_COMPLETION
745                 if (dirbuf[0] == '~')   /* ~/... or ~user/... */
746                         username_tab_completion(dirbuf, dirbuf);
747 #endif
748                 /* "strip" dirname in command */
749                 pfind++;
750
751                 paths[0] = dirbuf;
752                 npaths = 1;                             /* only 1 dir */
753         }
754
755         for (i = 0; i < npaths; i++) {
756
757                 dir = opendir(paths[i]);
758                 if (!dir)                       /* Don't print an error */
759                         continue;
760
761                 while ((next = readdir(dir)) != NULL) {
762                         char *str_found = next->d_name;
763                         int add_chr = 0;
764
765                         /* matched ? */
766                         if (strncmp(str_found, pfind, strlen(pfind)))
767                                 continue;
768                         /* not see .name without .match */
769                         if (*str_found == '.' && *pfind == 0) {
770                                 if (*paths[i] == '/' && paths[i][1] == 0
771                                         && str_found[1] == 0) str_found = "";   /* only "/" */
772                                 else
773                                         continue;
774                         }
775                         found = concat_path_file(paths[i], str_found);
776                         /* hmm, remover in progress? */
777                         if (stat(found, &st) < 0)
778                                 goto cont;
779                         /* find with dirs ? */
780                         if (paths[i] != dirbuf)
781                                 strcpy(found, next->d_name);    /* only name */
782                         if (S_ISDIR(st.st_mode)) {
783                                 /* name is directory      */
784                                 char *e = found + strlen(found) - 1;
785
786                                 add_chr = '/';
787                                 if(*e == '/')
788                                         *e = '\0';
789                         } else {
790                                 /* not put found file if search only dirs for cd */
791                                 if (type == FIND_DIR_ONLY)
792                                         goto cont;
793                                 if (type == FIND_FILE_ONLY ||
794                                         (type == FIND_EXE_ONLY && is_execute(&st)))
795                                         add_chr = ' ';
796                         }
797                         /* Add it to the list */
798                         add_match(found, add_chr);
799                         continue;
800 cont:
801                         free(found);
802                 }
803                 closedir(dir);
804         }
805         if (paths != path1) {
806                 free(paths[0]);                 /* allocated memory only in first member */
807                 free(paths);
808         }
809 }
810
811
812 #define QUOT    (UCHAR_MAX+1)
813
814 #define collapse_pos(is, in) { \
815         memmove(int_buf+(is), int_buf+(in), (BUFSIZ+1-(is)-(in))*sizeof(int)); \
816         memmove(pos_buf+(is), pos_buf+(in), (BUFSIZ+1-(is)-(in))*sizeof(int)); }
817
818 static int find_match(char *matchBuf, int *len_with_quotes)
819 {
820         int i, j;
821         int command_mode;
822         int c, c2;
823         int int_buf[BUFSIZ + 1];
824         int pos_buf[BUFSIZ + 1];
825
826         /* set to integer dimension characters and own positions */
827         for (i = 0;; i++) {
828                 int_buf[i] = (int) ((unsigned char) matchBuf[i]);
829                 if (int_buf[i] == 0) {
830                         pos_buf[i] = -1;        /* indicator end line */
831                         break;
832                 } else
833                         pos_buf[i] = i;
834         }
835
836         /* mask \+symbol and convert '\t' to ' ' */
837         for (i = j = 0; matchBuf[i]; i++, j++)
838                 if (matchBuf[i] == '\\') {
839                         collapse_pos(j, j + 1);
840                         int_buf[j] |= QUOT;
841                         i++;
842 #ifdef CONFIG_FEATURE_NONPRINTABLE_INVERSE_PUT
843                         if (matchBuf[i] == '\t')        /* algorithm equivalent */
844                                 int_buf[j] = ' ' | QUOT;
845 #endif
846                 }
847 #ifdef CONFIG_FEATURE_NONPRINTABLE_INVERSE_PUT
848                 else if (matchBuf[i] == '\t')
849                         int_buf[j] = ' ';
850 #endif
851
852         /* mask "symbols" or 'symbols' */
853         c2 = 0;
854         for (i = 0; int_buf[i]; i++) {
855                 c = int_buf[i];
856                 if (c == '\'' || c == '"') {
857                         if (c2 == 0)
858                                 c2 = c;
859                         else {
860                                 if (c == c2)
861                                         c2 = 0;
862                                 else
863                                         int_buf[i] |= QUOT;
864                         }
865                 } else if (c2 != 0 && c != '$')
866                         int_buf[i] |= QUOT;
867         }
868
869         /* skip commands with arguments if line have commands delimiters */
870         /* ';' ';;' '&' '|' '&&' '||' but `>&' `<&' `>|' */
871         for (i = 0; int_buf[i]; i++) {
872                 c = int_buf[i];
873                 c2 = int_buf[i + 1];
874                 j = i ? int_buf[i - 1] : -1;
875                 command_mode = 0;
876                 if (c == ';' || c == '&' || c == '|') {
877                         command_mode = 1 + (c == c2);
878                         if (c == '&') {
879                                 if (j == '>' || j == '<')
880                                         command_mode = 0;
881                         } else if (c == '|' && j == '>')
882                                 command_mode = 0;
883                 }
884                 if (command_mode) {
885                         collapse_pos(0, i + command_mode);
886                         i = -1;                         /* hack incremet */
887                 }
888         }
889         /* collapse `command...` */
890         for (i = 0; int_buf[i]; i++)
891                 if (int_buf[i] == '`') {
892                         for (j = i + 1; int_buf[j]; j++)
893                                 if (int_buf[j] == '`') {
894                                         collapse_pos(i, j + 1);
895                                         j = 0;
896                                         break;
897                                 }
898                         if (j) {
899                                 /* not found close ` - command mode, collapse all previous */
900                                 collapse_pos(0, i + 1);
901                                 break;
902                         } else
903                                 i--;                    /* hack incremet */
904                 }
905
906         /* collapse (command...(command...)...) or {command...{command...}...} */
907         c = 0;                                          /* "recursive" level */
908         c2 = 0;
909         for (i = 0; int_buf[i]; i++)
910                 if (int_buf[i] == '(' || int_buf[i] == '{') {
911                         if (int_buf[i] == '(')
912                                 c++;
913                         else
914                                 c2++;
915                         collapse_pos(0, i + 1);
916                         i = -1;                         /* hack incremet */
917                 }
918         for (i = 0; pos_buf[i] >= 0 && (c > 0 || c2 > 0); i++)
919                 if ((int_buf[i] == ')' && c > 0) || (int_buf[i] == '}' && c2 > 0)) {
920                         if (int_buf[i] == ')')
921                                 c--;
922                         else
923                                 c2--;
924                         collapse_pos(0, i + 1);
925                         i = -1;                         /* hack incremet */
926                 }
927
928         /* skip first not quote space */
929         for (i = 0; int_buf[i]; i++)
930                 if (int_buf[i] != ' ')
931                         break;
932         if (i)
933                 collapse_pos(0, i);
934
935         /* set find mode for completion */
936         command_mode = FIND_EXE_ONLY;
937         for (i = 0; int_buf[i]; i++)
938                 if (int_buf[i] == ' ' || int_buf[i] == '<' || int_buf[i] == '>') {
939                         if (int_buf[i] == ' ' && command_mode == FIND_EXE_ONLY
940                                 && matchBuf[pos_buf[0]]=='c'
941                                 && matchBuf[pos_buf[1]]=='d' )
942                                 command_mode = FIND_DIR_ONLY;
943                         else {
944                                 command_mode = FIND_FILE_ONLY;
945                                 break;
946                         }
947                 }
948         /* "strlen" */
949         for (i = 0; int_buf[i]; i++);
950         /* find last word */
951         for (--i; i >= 0; i--) {
952                 c = int_buf[i];
953                 if (c == ' ' || c == '<' || c == '>' || c == '|' || c == '&') {
954                         collapse_pos(0, i + 1);
955                         break;
956                 }
957         }
958         /* skip first not quoted '\'' or '"' */
959         for (i = 0; int_buf[i] == '\'' || int_buf[i] == '"'; i++);
960         /* collapse quote or unquote // or /~ */
961         while ((int_buf[i] & ~QUOT) == '/' &&
962                         ((int_buf[i + 1] & ~QUOT) == '/'
963                          || (int_buf[i + 1] & ~QUOT) == '~')) {
964                 i++;
965         }
966
967         /* set only match and destroy quotes */
968         j = 0;
969         for (c = 0; pos_buf[i] >= 0; i++) {
970                 matchBuf[c++] = matchBuf[pos_buf[i]];
971                 j = pos_buf[i] + 1;
972         }
973         matchBuf[c] = 0;
974         /* old lenght matchBuf with quotes symbols */
975         *len_with_quotes = j ? j - pos_buf[0] : 0;
976
977         return command_mode;
978 }
979
980 /*
981    display by column original ideas from ls applet,
982    very optimize by my :)
983 */
984 static void showfiles(void)
985 {
986         int ncols, row;
987         int column_width = 0;
988         int nfiles = num_matches;
989         int nrows = nfiles;
990         char str_add_chr[2];
991         int l;
992
993         /* find the longest file name-  use that as the column width */
994         for (row = 0; row < nrows; row++) {
995                 l = strlen(matches[row]);
996                 if(add_char_to_match[row])
997                         l++;
998                 if (column_width < l)
999                         column_width = l;
1000         }
1001         column_width += 2;              /* min space for columns */
1002         ncols = cmdedit_termw / column_width;
1003
1004         if (ncols > 1) {
1005                 nrows /= ncols;
1006                 if(nfiles % ncols)
1007                         nrows++;        /* round up fractionals */
1008         } else {
1009                 ncols = 1;
1010         }
1011         str_add_chr[1] = 0;
1012         for (row = 0; row < nrows; row++) {
1013                 int n = row;
1014                 int nc;
1015                 int acol;
1016
1017                 for(nc = 1; nc < ncols && n+nrows < nfiles; n += nrows, nc++) {
1018                         str_add_chr[0] = add_char_to_match[n];
1019                         acol = str_add_chr[0] ? column_width - 1 : column_width;
1020                         printf("%s%s", matches[n], str_add_chr);
1021                         l = strlen(matches[n]);
1022                         while(l < acol) {
1023                                 putchar(' ');
1024                                 l++;
1025                         }
1026                 }
1027                 str_add_chr[0] = add_char_to_match[n];
1028                 printf("%s%s\n", matches[n], str_add_chr);
1029         }
1030 }
1031
1032
1033 static void input_tab(int *lastWasTab)
1034 {
1035         /* Do TAB completion */
1036         if (lastWasTab == 0) {          /* free all memory */
1037                 if (matches) {
1038                         while (num_matches > 0)
1039                                 free(matches[--num_matches]);
1040                         free(matches);
1041                         matches = (char **) NULL;
1042                         free(add_char_to_match);
1043                         add_char_to_match = NULL;
1044                 }
1045                 return;
1046         }
1047         if (! *lastWasTab) {
1048
1049                 char *tmp, *tmp1;
1050                 int len_found;
1051                 char matchBuf[BUFSIZ];
1052                 int find_type;
1053                 int recalc_pos;
1054
1055                 *lastWasTab = TRUE;             /* flop trigger */
1056
1057                 /* Make a local copy of the string -- up
1058                  * to the position of the cursor */
1059                 tmp = strncpy(matchBuf, command_ps, cursor);
1060                 tmp[cursor] = 0;
1061
1062                 find_type = find_match(matchBuf, &recalc_pos);
1063
1064                 /* Free up any memory already allocated */
1065                 input_tab(0);
1066
1067 #ifdef CONFIG_FEATURE_COMMAND_USERNAME_COMPLETION
1068                 /* If the word starts with `~' and there is no slash in the word,
1069                  * then try completing this word as a username. */
1070
1071                 if (matchBuf[0] == '~' && strchr(matchBuf, '/') == 0)
1072                         username_tab_completion(matchBuf, NULL);
1073                 if (!matches)
1074 #endif
1075                 /* Try to match any executable in our path and everything
1076                  * in the current working directory that matches.  */
1077                         exe_n_cwd_tab_completion(matchBuf, find_type);
1078                 /* Remove duplicate found and sort */
1079                 if(matches) {
1080                         int i, j, n, srt;
1081                         /* bubble */
1082                         n = num_matches;
1083                         for(i=0; i<(n-1); i++) {
1084                                 for(j=i+1; j<n; j++) {
1085                                         if(matches[i]!=NULL && matches[j]!=NULL) {
1086                                                 srt = strcmp(matches[i], matches[j]);
1087                                                 if(srt == 0) {
1088                                                         free(matches[j]);
1089                                                         matches[j]=0;
1090                                                 } else if(srt > 0) {
1091                                                         tmp1 = matches[i];
1092                                                         matches[i] = matches[j];
1093                                                         matches[j] = tmp1;
1094                                                         srt = add_char_to_match[i];
1095                                                         add_char_to_match[i] = add_char_to_match[j];
1096                                                         add_char_to_match[j] = srt;
1097                                                 }
1098                                         }
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 = 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] = 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++] = 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 *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 */