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