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