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