source: branches/3.2/mindi-busybox/libbb/lineedit.c @ 3232

Last change on this file since 3232 was 3232, checked in by bruno, 5 years ago
  • Update mindi-busybox to 1.21.1
  • Property svn:eol-style set to native
File size: 67.1 KB
Line 
1/* vi: set sw=4 ts=4: */
2/*
3 * Command line 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, more needs to be added.
20 * 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 * The following readline-like commands are not implemented:
26 * ESC-b -- Move back one word
27 * ESC-f -- Move forward one word
28 * ESC-d -- Delete forward one word
29 * CTL-t -- Transpose two characters
30 *
31 * lineedit does not know that the terminal escape sequences do not
32 * take up space on the screen. The redisplay code assumes, unless
33 * told otherwise, that each character in the prompt is a printable
34 * character that takes up one character position on the screen.
35 * You need to tell lineedit that some sequences of characters
36 * in the prompt take up no screen space. Compatibly with readline,
37 * use the \[ escape to begin a sequence of non-printing characters,
38 * and the \] escape to signal the end of such a sequence. Example:
39 *
40 * PS1='\[\033[01;32m\]\u@\h\[\033[01;34m\] \w \$\[\033[00m\] '
41 */
42#include "libbb.h"
43#include "unicode.h"
44#ifndef _POSIX_VDISABLE
45# define _POSIX_VDISABLE '\0'
46#endif
47
48
49#ifdef TEST
50# define ENABLE_FEATURE_EDITING 0
51# define ENABLE_FEATURE_TAB_COMPLETION 0
52# define ENABLE_FEATURE_USERNAME_COMPLETION 0
53#endif
54
55
56/* Entire file (except TESTing part) sits inside this #if */
57#if ENABLE_FEATURE_EDITING
58
59
60#define ENABLE_USERNAME_OR_HOMEDIR \
61    (ENABLE_FEATURE_USERNAME_COMPLETION || ENABLE_FEATURE_EDITING_FANCY_PROMPT)
62#define IF_USERNAME_OR_HOMEDIR(...)
63#if ENABLE_USERNAME_OR_HOMEDIR
64# undef IF_USERNAME_OR_HOMEDIR
65# define IF_USERNAME_OR_HOMEDIR(...) __VA_ARGS__
66#endif
67
68
69#undef CHAR_T
70#if ENABLE_UNICODE_SUPPORT
71# define BB_NUL ((wchar_t)0)
72# define CHAR_T wchar_t
73static bool BB_isspace(CHAR_T c) { return ((unsigned)c < 256 && isspace(c)); }
74# if ENABLE_FEATURE_EDITING_VI
75static bool BB_isalnum(CHAR_T c) { return ((unsigned)c < 256 && isalnum(c)); }
76# endif
77static bool BB_ispunct(CHAR_T c) { return ((unsigned)c < 256 && ispunct(c)); }
78# undef isspace
79# undef isalnum
80# undef ispunct
81# undef isprint
82# define isspace isspace_must_not_be_used
83# define isalnum isalnum_must_not_be_used
84# define ispunct ispunct_must_not_be_used
85# define isprint isprint_must_not_be_used
86#else
87# define BB_NUL '\0'
88# define CHAR_T char
89# define BB_isspace(c) isspace(c)
90# define BB_isalnum(c) isalnum(c)
91# define BB_ispunct(c) ispunct(c)
92#endif
93#if ENABLE_UNICODE_PRESERVE_BROKEN
94# define unicode_mark_raw_byte(wc)   ((wc) | 0x20000000)
95# define unicode_is_raw_byte(wc)     ((wc) & 0x20000000)
96#else
97# define unicode_is_raw_byte(wc)     0
98#endif
99
100
101#define ESC "\033"
102
103#define SEQ_CLEAR_TILL_END_OF_SCREEN  ESC"[J"
104//#define SEQ_CLEAR_TILL_END_OF_LINE  ESC"[K"
105
106
107enum {
108    MAX_LINELEN = CONFIG_FEATURE_EDITING_MAX_LEN < 0x7ff0
109                  ? CONFIG_FEATURE_EDITING_MAX_LEN
110                  : 0x7ff0
111};
112
113#if ENABLE_USERNAME_OR_HOMEDIR
114static const char null_str[] ALIGN1 = "";
115#endif
116
117/* We try to minimize both static and stack usage. */
118struct lineedit_statics {
119    line_input_t *state;
120
121    volatile unsigned cmdedit_termw; /* = 80; */ /* actual terminal width */
122    sighandler_t previous_SIGWINCH_handler;
123
124    unsigned cmdedit_x;        /* real x (col) terminal position */
125    unsigned cmdedit_y;        /* pseudoreal y (row) terminal position */
126    unsigned cmdedit_prmt_len; /* length of prompt (without colors etc) */
127
128    unsigned cursor;
129    int command_len; /* must be signed */
130    /* signed maxsize: we want x in "if (x > S.maxsize)"
131     * to _not_ be promoted to unsigned */
132    int maxsize;
133    CHAR_T *command_ps;
134
135    const char *cmdedit_prompt;
136#if ENABLE_FEATURE_EDITING_FANCY_PROMPT
137    int num_ok_lines; /* = 1; */
138#endif
139
140#if ENABLE_USERNAME_OR_HOMEDIR
141    char *user_buf;
142    char *home_pwd_buf; /* = (char*)null_str; */
143#endif
144
145#if ENABLE_FEATURE_TAB_COMPLETION
146    char **matches;
147    unsigned num_matches;
148#endif
149
150#if ENABLE_FEATURE_EDITING_VI
151# define DELBUFSIZ 128
152    CHAR_T *delptr;
153    smallint newdelflag;     /* whether delbuf should be reused yet */
154    CHAR_T delbuf[DELBUFSIZ];  /* a place to store deleted characters */
155#endif
156#if ENABLE_FEATURE_EDITING_ASK_TERMINAL
157    smallint sent_ESC_br6n;
158#endif
159};
160
161/* See lineedit_ptr_hack.c */
162extern struct lineedit_statics *const lineedit_ptr_to_statics;
163
164#define S (*lineedit_ptr_to_statics)
165#define state            (S.state           )
166#define cmdedit_termw    (S.cmdedit_termw   )
167#define previous_SIGWINCH_handler (S.previous_SIGWINCH_handler)
168#define cmdedit_x        (S.cmdedit_x       )
169#define cmdedit_y        (S.cmdedit_y       )
170#define cmdedit_prmt_len (S.cmdedit_prmt_len)
171#define cursor           (S.cursor          )
172#define command_len      (S.command_len     )
173#define command_ps       (S.command_ps      )
174#define cmdedit_prompt   (S.cmdedit_prompt  )
175#define num_ok_lines     (S.num_ok_lines    )
176#define user_buf         (S.user_buf        )
177#define home_pwd_buf     (S.home_pwd_buf    )
178#define matches          (S.matches         )
179#define num_matches      (S.num_matches     )
180#define delptr           (S.delptr          )
181#define newdelflag       (S.newdelflag      )
182#define delbuf           (S.delbuf          )
183
184#define INIT_S() do { \
185    (*(struct lineedit_statics**)&lineedit_ptr_to_statics) = xzalloc(sizeof(S)); \
186    barrier(); \
187    cmdedit_termw = 80; \
188    IF_FEATURE_EDITING_FANCY_PROMPT(num_ok_lines = 1;) \
189    IF_USERNAME_OR_HOMEDIR(home_pwd_buf = (char*)null_str;) \
190} while (0)
191
192static void deinit_S(void)
193{
194#if ENABLE_FEATURE_EDITING_FANCY_PROMPT
195    /* This one is allocated only if FANCY_PROMPT is on
196     * (otherwise it points to verbatim prompt (NOT malloced)) */
197    free((char*)cmdedit_prompt);
198#endif
199#if ENABLE_USERNAME_OR_HOMEDIR
200    free(user_buf);
201    if (home_pwd_buf != null_str)
202        free(home_pwd_buf);
203#endif
204    free(lineedit_ptr_to_statics);
205}
206#define DEINIT_S() deinit_S()
207
208
209#if ENABLE_UNICODE_SUPPORT
210static size_t load_string(const char *src)
211{
212    if (unicode_status == UNICODE_ON) {
213        ssize_t len = mbstowcs(command_ps, src, S.maxsize - 1);
214        if (len < 0)
215            len = 0;
216        command_ps[len] = BB_NUL;
217        return len;
218    } else {
219        unsigned i = 0;
220        while (src[i] && i < S.maxsize - 1) {
221            command_ps[i] = src[i];
222            i++;
223        }
224        command_ps[i] = BB_NUL;
225        return i;
226    }
227}
228static unsigned save_string(char *dst, unsigned maxsize)
229{
230    if (unicode_status == UNICODE_ON) {
231# if !ENABLE_UNICODE_PRESERVE_BROKEN
232        ssize_t len = wcstombs(dst, command_ps, maxsize - 1);
233        if (len < 0)
234            len = 0;
235        dst[len] = '\0';
236        return len;
237# else
238        unsigned dstpos = 0;
239        unsigned srcpos = 0;
240
241        maxsize--;
242        while (dstpos < maxsize) {
243            wchar_t wc;
244            int n = srcpos;
245
246            /* Convert up to 1st invalid byte (or up to end) */
247            while ((wc = command_ps[srcpos]) != BB_NUL
248                && !unicode_is_raw_byte(wc)
249            ) {
250                srcpos++;
251            }
252            command_ps[srcpos] = BB_NUL;
253            n = wcstombs(dst + dstpos, command_ps + n, maxsize - dstpos);
254            if (n < 0) /* should not happen */
255                break;
256            dstpos += n;
257            if (wc == BB_NUL) /* usually is */
258                break;
259
260            /* We do have invalid byte here! */
261            command_ps[srcpos] = wc; /* restore it */
262            srcpos++;
263            if (dstpos == maxsize)
264                break;
265            dst[dstpos++] = (char) wc;
266        }
267        dst[dstpos] = '\0';
268        return dstpos;
269# endif
270    } else {
271        unsigned i = 0;
272        while ((dst[i] = command_ps[i]) != 0)
273            i++;
274        return i;
275    }
276}
277/* I thought just fputwc(c, stdout) would work. But no... */
278static void BB_PUTCHAR(wchar_t c)
279{
280    if (unicode_status == UNICODE_ON) {
281        char buf[MB_CUR_MAX + 1];
282        mbstate_t mbst = { 0 };
283        ssize_t len = wcrtomb(buf, c, &mbst);
284        if (len > 0) {
285            buf[len] = '\0';
286            fputs(buf, stdout);
287        }
288    } else {
289        /* In this case, c is always one byte */
290        putchar(c);
291    }
292}
293# if ENABLE_UNICODE_COMBINING_WCHARS || ENABLE_UNICODE_WIDE_WCHARS
294static wchar_t adjust_width_and_validate_wc(unsigned *width_adj, wchar_t wc)
295# else
296static wchar_t adjust_width_and_validate_wc(wchar_t wc)
297#  define adjust_width_and_validate_wc(width_adj, wc) \
298    ((*(width_adj))++, adjust_width_and_validate_wc(wc))
299# endif
300{
301    int w = 1;
302
303    if (unicode_status == UNICODE_ON) {
304        if (wc > CONFIG_LAST_SUPPORTED_WCHAR) {
305            /* note: also true for unicode_is_raw_byte(wc) */
306            goto subst;
307        }
308        w = wcwidth(wc);
309        if ((ENABLE_UNICODE_COMBINING_WCHARS && w < 0)
310         || (!ENABLE_UNICODE_COMBINING_WCHARS && w <= 0)
311         || (!ENABLE_UNICODE_WIDE_WCHARS && w > 1)
312        ) {
313 subst:
314            w = 1;
315            wc = CONFIG_SUBST_WCHAR;
316        }
317    }
318
319# if ENABLE_UNICODE_COMBINING_WCHARS || ENABLE_UNICODE_WIDE_WCHARS
320    *width_adj += w;
321#endif
322    return wc;
323}
324#else /* !UNICODE */
325static size_t load_string(const char *src)
326{
327    safe_strncpy(command_ps, src, S.maxsize);
328    return strlen(command_ps);
329}
330# if ENABLE_FEATURE_TAB_COMPLETION
331static void save_string(char *dst, unsigned maxsize)
332{
333    safe_strncpy(dst, command_ps, maxsize);
334}
335# endif
336# define BB_PUTCHAR(c) bb_putchar(c)
337/* Should never be called: */
338int adjust_width_and_validate_wc(unsigned *width_adj, int wc);
339#endif
340
341
342/* Put 'command_ps[cursor]', cursor++.
343 * Advance cursor on screen. If we reached right margin, scroll text up
344 * and remove terminal margin effect by printing 'next_char' */
345#define HACK_FOR_WRONG_WIDTH 1
346static void put_cur_glyph_and_inc_cursor(void)
347{
348    CHAR_T c = command_ps[cursor];
349    unsigned width = 0;
350    int ofs_to_right;
351
352    if (c == BB_NUL) {
353        /* erase character after end of input string */
354        c = ' ';
355    } else {
356        /* advance cursor only if we aren't at the end yet */
357        cursor++;
358        if (unicode_status == UNICODE_ON) {
359            IF_UNICODE_WIDE_WCHARS(width = cmdedit_x;)
360            c = adjust_width_and_validate_wc(&cmdedit_x, c);
361            IF_UNICODE_WIDE_WCHARS(width = cmdedit_x - width;)
362        } else {
363            cmdedit_x++;
364        }
365    }
366
367    ofs_to_right = cmdedit_x - cmdedit_termw;
368    if (!ENABLE_UNICODE_WIDE_WCHARS || ofs_to_right <= 0) {
369        /* c fits on this line */
370        BB_PUTCHAR(c);
371    }
372
373    if (ofs_to_right >= 0) {
374        /* we go to the next line */
375#if HACK_FOR_WRONG_WIDTH
376        /* This works better if our idea of term width is wrong
377         * and it is actually wider (often happens on serial lines).
378         * Printing CR,LF *forces* cursor to next line.
379         * OTOH if terminal width is correct AND terminal does NOT
380         * have automargin (IOW: it is moving cursor to next line
381         * by itself (which is wrong for VT-10x terminals)),
382         * this will break things: there will be one extra empty line */
383        puts("\r"); /* + implicit '\n' */
384#else
385        /* VT-10x terminals don't wrap cursor to next line when last char
386         * on the line is printed - cursor stays "over" this char.
387         * Need to print _next_ char too (first one to appear on next line)
388         * to make cursor move down to next line.
389         */
390        /* Works ok only if cmdedit_termw is correct. */
391        c = command_ps[cursor];
392        if (c == BB_NUL)
393            c = ' ';
394        BB_PUTCHAR(c);
395        bb_putchar('\b');
396#endif
397        cmdedit_y++;
398        if (!ENABLE_UNICODE_WIDE_WCHARS || ofs_to_right == 0) {
399            width = 0;
400        } else { /* ofs_to_right > 0 */
401            /* wide char c didn't fit on prev line */
402            BB_PUTCHAR(c);
403        }
404        cmdedit_x = width;
405    }
406}
407
408/* Move to end of line (by printing all chars till the end) */
409static void put_till_end_and_adv_cursor(void)
410{
411    while (cursor < command_len)
412        put_cur_glyph_and_inc_cursor();
413}
414
415/* Go to the next line */
416static void goto_new_line(void)
417{
418    put_till_end_and_adv_cursor();
419    if (cmdedit_x != 0)
420        bb_putchar('\n');
421}
422
423static void beep(void)
424{
425    bb_putchar('\007');
426}
427
428static void put_prompt(void)
429{
430    unsigned w;
431
432    fputs(cmdedit_prompt, stdout);
433    fflush_all();
434    cursor = 0;
435    w = cmdedit_termw; /* read volatile var once */
436    cmdedit_y = cmdedit_prmt_len / w; /* new quasireal y */
437    cmdedit_x = cmdedit_prmt_len % w;
438}
439
440/* Move back one character */
441/* (optimized for slow terminals) */
442static void input_backward(unsigned num)
443{
444    if (num > cursor)
445        num = cursor;
446    if (num == 0)
447        return;
448    cursor -= num;
449
450    if ((ENABLE_UNICODE_COMBINING_WCHARS || ENABLE_UNICODE_WIDE_WCHARS)
451     && unicode_status == UNICODE_ON
452    ) {
453        /* correct NUM to be equal to _screen_ width */
454        int n = num;
455        num = 0;
456        while (--n >= 0)
457            adjust_width_and_validate_wc(&num, command_ps[cursor + n]);
458        if (num == 0)
459            return;
460    }
461
462    if (cmdedit_x >= num) {
463        cmdedit_x -= num;
464        if (num <= 4) {
465            /* This is longer by 5 bytes on x86.
466             * Also gets miscompiled for ARM users
467             * (busybox.net/bugs/view.php?id=2274).
468             * printf(("\b\b\b\b" + 4) - num);
469             * return;
470             */
471            do {
472                bb_putchar('\b');
473            } while (--num);
474            return;
475        }
476        printf(ESC"[%uD", num);
477        return;
478    }
479
480    /* Need to go one or more lines up */
481    if (ENABLE_UNICODE_WIDE_WCHARS) {
482        /* With wide chars, it is hard to "backtrack"
483         * and reliably figure out where to put cursor.
484         * Example (<> is a wide char; # is an ordinary char, _ cursor):
485         * |prompt: <><> |
486         * |<><><><><><> |
487         * |_            |
488         * and user presses left arrow. num = 1, cmdedit_x = 0,
489         * We need to go up one line, and then - how do we know that
490         * we need to go *10* positions to the right? Because
491         * |prompt: <>#<>|
492         * |<><><>#<><><>|
493         * |_            |
494         * in this situation we need to go *11* positions to the right.
495         *
496         * A simpler thing to do is to redraw everything from the start
497         * up to new cursor position (which is already known):
498         */
499        unsigned sv_cursor;
500        /* go to 1st column; go up to first line */
501        printf("\r" ESC"[%uA", cmdedit_y);
502        cmdedit_y = 0;
503        sv_cursor = cursor;
504        put_prompt(); /* sets cursor to 0 */
505        while (cursor < sv_cursor)
506            put_cur_glyph_and_inc_cursor();
507    } else {
508        int lines_up;
509        unsigned width;
510        /* num = chars to go back from the beginning of current line: */
511        num -= cmdedit_x;
512        width = cmdedit_termw; /* read volatile var once */
513        /* num=1...w: one line up, w+1...2w: two, etc: */
514        lines_up = 1 + (num - 1) / width;
515        cmdedit_x = (width * cmdedit_y - num) % width;
516        cmdedit_y -= lines_up;
517        /* go to 1st column; go up */
518        printf("\r" ESC"[%uA", lines_up);
519        /* go to correct column.
520         * xterm, konsole, Linux VT interpret 0 as 1 below! wow.
521         * need to *make sure* we skip it if cmdedit_x == 0 */
522        if (cmdedit_x)
523            printf(ESC"[%uC", cmdedit_x);
524    }
525}
526
527/* draw prompt, editor line, and clear tail */
528static void redraw(int y, int back_cursor)
529{
530    if (y > 0) /* up y lines */
531        printf(ESC"[%uA", y);
532    bb_putchar('\r');
533    put_prompt();
534    put_till_end_and_adv_cursor();
535    printf(SEQ_CLEAR_TILL_END_OF_SCREEN);
536    input_backward(back_cursor);
537}
538
539/* Delete the char in front of the cursor, optionally saving it
540 * for later putback */
541#if !ENABLE_FEATURE_EDITING_VI
542static void input_delete(void)
543#define input_delete(save) input_delete()
544#else
545static void input_delete(int save)
546#endif
547{
548    int j = cursor;
549
550    if (j == (int)command_len)
551        return;
552
553#if ENABLE_FEATURE_EDITING_VI
554    if (save) {
555        if (newdelflag) {
556            delptr = delbuf;
557            newdelflag = 0;
558        }
559        if ((delptr - delbuf) < DELBUFSIZ)
560            *delptr++ = command_ps[j];
561    }
562#endif
563
564    memmove(command_ps + j, command_ps + j + 1,
565            /* (command_len + 1 [because of NUL]) - (j + 1)
566             * simplified into (command_len - j) */
567            (command_len - j) * sizeof(command_ps[0]));
568    command_len--;
569    put_till_end_and_adv_cursor();
570    /* Last char is still visible, erase it (and more) */
571    printf(SEQ_CLEAR_TILL_END_OF_SCREEN);
572    input_backward(cursor - j);     /* back to old pos cursor */
573}
574
575#if ENABLE_FEATURE_EDITING_VI
576static void put(void)
577{
578    int ocursor;
579    int j = delptr - delbuf;
580
581    if (j == 0)
582        return;
583    ocursor = cursor;
584    /* open hole and then fill it */
585    memmove(command_ps + cursor + j, command_ps + cursor,
586            (command_len - cursor + 1) * sizeof(command_ps[0]));
587    memcpy(command_ps + cursor, delbuf, j * sizeof(command_ps[0]));
588    command_len += j;
589    put_till_end_and_adv_cursor();
590    input_backward(cursor - ocursor - j + 1); /* at end of new text */
591}
592#endif
593
594/* Delete the char in back of the cursor */
595static void input_backspace(void)
596{
597    if (cursor > 0) {
598        input_backward(1);
599        input_delete(0);
600    }
601}
602
603/* Move forward one character */
604static void input_forward(void)
605{
606    if (cursor < command_len)
607        put_cur_glyph_and_inc_cursor();
608}
609
610#if ENABLE_FEATURE_TAB_COMPLETION
611
612//FIXME:
613//needs to be more clever: currently it thinks that "foo\ b<TAB>
614//matches the file named "foo bar", which is untrue.
615//Also, perhaps "foo b<TAB> needs to complete to "foo bar" <cursor>,
616//not "foo bar <cursor>...
617
618static void free_tab_completion_data(void)
619{
620    if (matches) {
621        while (num_matches)
622            free(matches[--num_matches]);
623        free(matches);
624        matches = NULL;
625    }
626}
627
628static void add_match(char *matched)
629{
630    matches = xrealloc_vector(matches, 4, num_matches);
631    matches[num_matches] = matched;
632    num_matches++;
633}
634
635# if ENABLE_FEATURE_USERNAME_COMPLETION
636/* Replace "~user/..." with "/homedir/...".
637 * The parameter is malloced, free it or return it
638 * unchanged if no user is matched.
639 */
640static char *username_path_completion(char *ud)
641{
642    struct passwd *entry;
643    char *tilde_name = ud;
644    char *home = NULL;
645
646    ud++; /* skip ~ */
647    if (*ud == '/') {       /* "~/..." */
648        home = home_pwd_buf;
649    } else {
650        /* "~user/..." */
651        ud = strchr(ud, '/');
652        *ud = '\0';           /* "~user" */
653        entry = getpwnam(tilde_name + 1);
654        *ud = '/';            /* restore "~user/..." */
655        if (entry)
656            home = entry->pw_dir;
657    }
658    if (home) {
659        ud = concat_path_file(home, ud);
660        free(tilde_name);
661        tilde_name = ud;
662    }
663    return tilde_name;
664}
665
666/* ~use<tab> - find all users with this prefix.
667 * Return the length of the prefix used for matching.
668 */
669static NOINLINE unsigned complete_username(const char *ud)
670{
671    /* Using _r function to avoid pulling in static buffers */
672    char line_buff[256];
673    struct passwd pwd;
674    struct passwd *result;
675    unsigned userlen;
676
677    ud++; /* skip ~ */
678    userlen = strlen(ud);
679
680    setpwent();
681    while (!getpwent_r(&pwd, line_buff, sizeof(line_buff), &result)) {
682        /* Null usernames should result in all users as possible completions. */
683        if (/*!userlen || */ strncmp(ud, pwd.pw_name, userlen) == 0) {
684            add_match(xasprintf("~%s/", pwd.pw_name));
685        }
686    }
687    endpwent();
688
689    return 1 + userlen;
690}
691# endif  /* FEATURE_USERNAME_COMPLETION */
692
693enum {
694    FIND_EXE_ONLY = 0,
695    FIND_DIR_ONLY = 1,
696    FIND_FILE_ONLY = 2,
697};
698
699static int path_parse(char ***p)
700{
701    int npth;
702    const char *pth;
703    char *tmp;
704    char **res;
705
706    if (state->flags & WITH_PATH_LOOKUP)
707        pth = state->path_lookup;
708    else
709        pth = getenv("PATH");
710
711    /* PATH="" or PATH=":"? */
712    if (!pth || !pth[0] || LONE_CHAR(pth, ':'))
713        return 1;
714
715    tmp = (char*)pth;
716    npth = 1; /* path component count */
717    while (1) {
718        tmp = strchr(tmp, ':');
719        if (!tmp)
720            break;
721        tmp++;
722        if (*tmp == '\0')
723            break;  /* :<empty> */
724        npth++;
725    }
726
727    *p = res = xmalloc(npth * sizeof(res[0]));
728    res[0] = tmp = xstrdup(pth);
729    npth = 1;
730    while (1) {
731        tmp = strchr(tmp, ':');
732        if (!tmp)
733            break;
734        *tmp++ = '\0'; /* ':' -> '\0' */
735        if (*tmp == '\0')
736            break; /* :<empty> */
737        res[npth++] = tmp;
738    }
739    return npth;
740}
741
742/* Complete command, directory or file name.
743 * Return the length of the prefix used for matching.
744 */
745static NOINLINE unsigned complete_cmd_dir_file(const char *command, int type)
746{
747    char *path1[1];
748    char **paths = path1;
749    int npaths;
750    int i;
751    unsigned pf_len;
752    const char *pfind;
753    char *dirbuf = NULL;
754
755    npaths = 1;
756    path1[0] = (char*)".";
757
758    pfind = strrchr(command, '/');
759    if (!pfind) {
760        if (type == FIND_EXE_ONLY)
761            npaths = path_parse(&paths);
762        pfind = command;
763    } else {
764        /* point to 'l' in "..../last_component" */
765        pfind++;
766        /* dirbuf = ".../.../.../" */
767        dirbuf = xstrndup(command, pfind - command);
768# if ENABLE_FEATURE_USERNAME_COMPLETION
769        if (dirbuf[0] == '~')   /* ~/... or ~user/... */
770            dirbuf = username_path_completion(dirbuf);
771# endif
772        path1[0] = dirbuf;
773    }
774    pf_len = strlen(pfind);
775
776    for (i = 0; i < npaths; i++) {
777        DIR *dir;
778        struct dirent *next;
779        struct stat st;
780        char *found;
781
782        dir = opendir(paths[i]);
783        if (!dir)
784            continue; /* don't print an error */
785
786        while ((next = readdir(dir)) != NULL) {
787            unsigned len;
788            const char *name_found = next->d_name;
789
790            /* .../<tab>: bash 3.2.0 shows dotfiles, but not . and .. */
791            if (!pfind[0] && DOT_OR_DOTDOT(name_found))
792                continue;
793            /* match? */
794            if (strncmp(name_found, pfind, pf_len) != 0)
795                continue; /* no */
796
797            found = concat_path_file(paths[i], name_found);
798            /* NB: stat() first so that we see is it a directory;
799             * but if that fails, use lstat() so that
800             * we still match dangling links */
801            if (stat(found, &st) && lstat(found, &st))
802                goto cont; /* hmm, remove in progress? */
803
804            /* Save only name */
805            len = strlen(name_found);
806            found = xrealloc(found, len + 2); /* +2: for slash and NUL */
807            strcpy(found, name_found);
808
809            if (S_ISDIR(st.st_mode)) {
810                /* name is a directory, add slash */
811                found[len] = '/';
812                found[len + 1] = '\0';
813            } else {
814                /* skip files if looking for dirs only (example: cd) */
815                if (type == FIND_DIR_ONLY)
816                    goto cont;
817            }
818            /* add it to the list */
819            add_match(found);
820            continue;
821 cont:
822            free(found);
823        }
824        closedir(dir);
825    } /* for every path */
826
827    if (paths != path1) {
828        free(paths[0]); /* allocated memory is only in first member */
829        free(paths);
830    }
831    free(dirbuf);
832
833    return pf_len;
834}
835
836/* build_match_prefix:
837 * On entry, match_buf contains everything up to cursor at the moment <tab>
838 * was pressed. This function looks at it, figures out what part of it
839 * constitutes the command/file/directory prefix to use for completion,
840 * and rewrites match_buf to contain only that part.
841 */
842#define dbg_bmp 0
843/* Helpers: */
844/* QUOT is used on elements of int_buf[], which are bytes,
845 * not Unicode chars. Therefore it works correctly even in Unicode mode.
846 */
847#define QUOT (UCHAR_MAX+1)
848static void remove_chunk(int16_t *int_buf, int beg, int end)
849{
850    /* beg must be <= end */
851    if (beg == end)
852        return;
853
854    while ((int_buf[beg] = int_buf[end]) != 0)
855        beg++, end++;
856
857    if (dbg_bmp) {
858        int i;
859        for (i = 0; int_buf[i]; i++)
860            bb_putchar((unsigned char)int_buf[i]);
861        bb_putchar('\n');
862    }
863}
864/* Caller ensures that match_buf points to a malloced buffer
865 * big enough to hold strlen(match_buf)*2 + 2
866 */
867static NOINLINE int build_match_prefix(char *match_buf)
868{
869    int i, j;
870    int command_mode;
871    int16_t *int_buf = (int16_t*)match_buf;
872
873    if (dbg_bmp) printf("\n%s\n", match_buf);
874
875    /* Copy in reverse order, since they overlap */
876    i = strlen(match_buf);
877    do {
878        int_buf[i] = (unsigned char)match_buf[i];
879        i--;
880    } while (i >= 0);
881
882    /* Mark every \c as "quoted c" */
883    for (i = 0; int_buf[i]; i++) {
884        if (int_buf[i] == '\\') {
885            remove_chunk(int_buf, i, i + 1);
886            int_buf[i] |= QUOT;
887        }
888    }
889    /* Quote-mark "chars" and 'chars', drop delimiters */
890    {
891        int in_quote = 0;
892        i = 0;
893        while (int_buf[i]) {
894            int cur = int_buf[i];
895            if (!cur)
896                break;
897            if (cur == '\'' || cur == '"') {
898                if (!in_quote || (cur == in_quote)) {
899                    in_quote ^= cur;
900                    remove_chunk(int_buf, i, i + 1);
901                    continue;
902                }
903            }
904            if (in_quote)
905                int_buf[i] = cur | QUOT;
906            i++;
907        }
908    }
909
910    /* Remove everything up to command delimiters:
911     * ';' ';;' '&' '|' '&&' '||',
912     * but careful with '>&' '<&' '>|'
913     */
914    for (i = 0; int_buf[i]; i++) {
915        int cur = int_buf[i];
916        if (cur == ';' || cur == '&' || cur == '|') {
917            int prev = i ? int_buf[i - 1] : 0;
918            if (cur == '&' && (prev == '>' || prev == '<')) {
919                continue;
920            } else if (cur == '|' && prev == '>') {
921                continue;
922            }
923            remove_chunk(int_buf, 0, i + 1 + (cur == int_buf[i + 1]));
924            i = -1;  /* back to square 1 */
925        }
926    }
927    /* Remove all `cmd` */
928    for (i = 0; int_buf[i]; i++) {
929        if (int_buf[i] == '`') {
930            for (j = i + 1; int_buf[j]; j++) {
931                if (int_buf[j] == '`') {
932                    /* `cmd` should count as a word:
933                     * `cmd` c<tab> should search for files c*,
934                     * not commands c*. Therefore we don't drop
935                     * `cmd` entirely, we replace it with single `.
936                     */
937                    remove_chunk(int_buf, i, j);
938                    goto next;
939                }
940            }
941            /* No closing ` - command mode, remove all up to ` */
942            remove_chunk(int_buf, 0, i + 1);
943            break;
944 next: ;
945        }
946    }
947
948    /* Remove "cmd (" and "cmd {"
949     * Example: "if { c<tab>"
950     * In this example, c should be matched as command pfx.
951     */
952    for (i = 0; int_buf[i]; i++) {
953        if (int_buf[i] == '(' || int_buf[i] == '{') {
954            remove_chunk(int_buf, 0, i + 1);
955            i = -1;  /* back to square 1 */
956        }
957    }
958
959    /* Remove leading unquoted spaces */
960    for (i = 0; int_buf[i]; i++)
961        if (int_buf[i] != ' ')
962            break;
963    remove_chunk(int_buf, 0, i);
964
965    /* Determine completion mode */
966    command_mode = FIND_EXE_ONLY;
967    for (i = 0; int_buf[i]; i++) {
968        if (int_buf[i] == ' ' || int_buf[i] == '<' || int_buf[i] == '>') {
969            if (int_buf[i] == ' '
970             && command_mode == FIND_EXE_ONLY
971             && (char)int_buf[0] == 'c'
972             && (char)int_buf[1] == 'd'
973             && i == 2 /* -> int_buf[2] == ' ' */
974            ) {
975                command_mode = FIND_DIR_ONLY;
976            } else {
977                command_mode = FIND_FILE_ONLY;
978                break;
979            }
980        }
981    }
982    if (dbg_bmp) printf("command_mode(0:exe/1:dir/2:file):%d\n", command_mode);
983
984    /* Remove everything except last word */
985    for (i = 0; int_buf[i]; i++) /* quasi-strlen(int_buf) */
986        continue;
987    for (--i; i >= 0; i--) {
988        int cur = int_buf[i];
989        if (cur == ' ' || cur == '<' || cur == '>' || cur == '|' || cur == '&') {
990            remove_chunk(int_buf, 0, i + 1);
991            break;
992        }
993    }
994
995    /* Convert back to string of _chars_ */
996    i = 0;
997    while ((match_buf[i] = int_buf[i]) != '\0')
998        i++;
999
1000    if (dbg_bmp) printf("final match_buf:'%s'\n", match_buf);
1001
1002    return command_mode;
1003}
1004
1005/*
1006 * Display by column (original idea from ls applet,
1007 * very optimized by me [Vladimir] :)
1008 */
1009static void showfiles(void)
1010{
1011    int ncols, row;
1012    int column_width = 0;
1013    int nfiles = num_matches;
1014    int nrows = nfiles;
1015    int l;
1016
1017    /* find the longest file name - use that as the column width */
1018    for (row = 0; row < nrows; row++) {
1019        l = unicode_strwidth(matches[row]);
1020        if (column_width < l)
1021            column_width = l;
1022    }
1023    column_width += 2;              /* min space for columns */
1024    ncols = cmdedit_termw / column_width;
1025
1026    if (ncols > 1) {
1027        nrows /= ncols;
1028        if (nfiles % ncols)
1029            nrows++;        /* round up fractionals */
1030    } else {
1031        ncols = 1;
1032    }
1033    for (row = 0; row < nrows; row++) {
1034        int n = row;
1035        int nc;
1036
1037        for (nc = 1; nc < ncols && n+nrows < nfiles; n += nrows, nc++) {
1038            printf("%s%-*s", matches[n],
1039                (int)(column_width - unicode_strwidth(matches[n])), ""
1040            );
1041        }
1042        if (ENABLE_UNICODE_SUPPORT)
1043            puts(printable_string(NULL, matches[n]));
1044        else
1045            puts(matches[n]);
1046    }
1047}
1048
1049static const char *is_special_char(char c)
1050{
1051    return strchr(" `\"#$%^&*()=+{}[]:;'|\\<>", c);
1052}
1053
1054static char *quote_special_chars(char *found)
1055{
1056    int l = 0;
1057    char *s = xzalloc((strlen(found) + 1) * 2);
1058
1059    while (*found) {
1060        if (is_special_char(*found))
1061            s[l++] = '\\';
1062        s[l++] = *found++;
1063    }
1064    /* s[l] = '\0'; - already is */
1065    return s;
1066}
1067
1068/* Do TAB completion */
1069static NOINLINE void input_tab(smallint *lastWasTab)
1070{
1071    char *chosen_match;
1072    char *match_buf;
1073    size_t len_found;
1074    /* Length of string used for matching */
1075    unsigned match_pfx_len = match_pfx_len;
1076    int find_type;
1077# if ENABLE_UNICODE_SUPPORT
1078    /* cursor pos in command converted to multibyte form */
1079    int cursor_mb;
1080# endif
1081    if (!(state->flags & TAB_COMPLETION))
1082        return;
1083
1084    if (*lastWasTab) {
1085        /* The last char was a TAB too.
1086         * Print a list of all the available choices.
1087         */
1088        if (num_matches > 0) {
1089            /* cursor will be changed by goto_new_line() */
1090            int sav_cursor = cursor;
1091            goto_new_line();
1092            showfiles();
1093            redraw(0, command_len - sav_cursor);
1094        }
1095        return;
1096    }
1097
1098    *lastWasTab = 1;
1099    chosen_match = NULL;
1100
1101    /* Make a local copy of the string up to the position of the cursor.
1102     * build_match_prefix will expand it into int16_t's, need to allocate
1103     * twice as much as the string_len+1.
1104     * (we then also (ab)use this extra space later - see (**))
1105     */
1106    match_buf = xmalloc(MAX_LINELEN * sizeof(int16_t));
1107# if !ENABLE_UNICODE_SUPPORT
1108    save_string(match_buf, cursor + 1); /* +1 for NUL */
1109# else
1110    {
1111        CHAR_T wc = command_ps[cursor];
1112        command_ps[cursor] = BB_NUL;
1113        save_string(match_buf, MAX_LINELEN);
1114        command_ps[cursor] = wc;
1115        cursor_mb = strlen(match_buf);
1116    }
1117# endif
1118    find_type = build_match_prefix(match_buf);
1119
1120    /* Free up any memory already allocated */
1121    free_tab_completion_data();
1122
1123# if ENABLE_FEATURE_USERNAME_COMPLETION
1124    /* If the word starts with ~ and there is no slash in the word,
1125     * then try completing this word as a username. */
1126    if (state->flags & USERNAME_COMPLETION)
1127        if (match_buf[0] == '~' && strchr(match_buf, '/') == NULL)
1128            match_pfx_len = complete_username(match_buf);
1129# endif
1130    /* If complete_username() did not match,
1131     * try to match a command in $PATH, or a directory, or a file */
1132    if (!matches)
1133        match_pfx_len = complete_cmd_dir_file(match_buf, find_type);
1134
1135    /* Account for backslashes which will be inserted
1136     * by quote_special_chars() later */
1137    {
1138        const char *e = match_buf + strlen(match_buf);
1139        const char *s = e - match_pfx_len;
1140        while (s < e)
1141            if (is_special_char(*s++))
1142                match_pfx_len++;
1143    }
1144
1145    /* Remove duplicates */
1146    if (matches) {
1147        unsigned i, n = 0;
1148        qsort_string_vector(matches, num_matches);
1149        for (i = 0; i < num_matches - 1; ++i) {
1150            //if (matches[i] && matches[i+1]) { /* paranoia */
1151                if (strcmp(matches[i], matches[i+1]) == 0) {
1152                    free(matches[i]);
1153                    //matches[i] = NULL; /* paranoia */
1154                } else {
1155                    matches[n++] = matches[i];
1156                }
1157            //}
1158        }
1159        matches[n++] = matches[i];
1160        num_matches = n;
1161    }
1162
1163    /* Did we find exactly one match? */
1164    if (num_matches != 1) { /* no */
1165        char *cp;
1166        beep();
1167        if (!matches)
1168            goto ret; /* no matches at all */
1169        /* Find common prefix */
1170        chosen_match = xstrdup(matches[0]);
1171        for (cp = chosen_match; *cp; cp++) {
1172            unsigned n;
1173            for (n = 1; n < num_matches; n++) {
1174                if (matches[n][cp - chosen_match] != *cp) {
1175                    goto stop;
1176                }
1177            }
1178        }
1179 stop:
1180        if (cp == chosen_match) { /* have unique prefix? */
1181            goto ret; /* no */
1182        }
1183        *cp = '\0';
1184        cp = quote_special_chars(chosen_match);
1185        free(chosen_match);
1186        chosen_match = cp;
1187        len_found = strlen(chosen_match);
1188    } else {                        /* exactly one match */
1189        /* Next <tab> is not a double-tab */
1190        *lastWasTab = 0;
1191
1192        chosen_match = quote_special_chars(matches[0]);
1193        len_found = strlen(chosen_match);
1194        if (chosen_match[len_found-1] != '/') {
1195            chosen_match[len_found] = ' ';
1196            chosen_match[++len_found] = '\0';
1197        }
1198    }
1199
1200# if !ENABLE_UNICODE_SUPPORT
1201    /* Have space to place the match? */
1202    /* The result consists of three parts with these lengths: */
1203    /* cursor + (len_found - match_pfx_len) + (command_len - cursor) */
1204    /* it simplifies into: */
1205    if ((int)(len_found - match_pfx_len + command_len) < S.maxsize) {
1206        int pos;
1207        /* save tail */
1208        strcpy(match_buf, &command_ps[cursor]);
1209        /* add match and tail */
1210        sprintf(&command_ps[cursor], "%s%s", chosen_match + match_pfx_len, match_buf);
1211        command_len = strlen(command_ps);
1212        /* new pos */
1213        pos = cursor + len_found - match_pfx_len;
1214        /* write out the matched command */
1215        redraw(cmdedit_y, command_len - pos);
1216    }
1217# else
1218    {
1219        /* Use 2nd half of match_buf as scratch space - see (**) */
1220        char *command = match_buf + MAX_LINELEN;
1221        int len = save_string(command, MAX_LINELEN);
1222        /* Have space to place the match? */
1223        /* cursor_mb + (len_found - match_pfx_len) + (len - cursor_mb) */
1224        if ((int)(len_found - match_pfx_len + len) < MAX_LINELEN) {
1225            int pos;
1226            /* save tail */
1227            strcpy(match_buf, &command[cursor_mb]);
1228            /* where do we want to have cursor after all? */
1229            strcpy(&command[cursor_mb], chosen_match + match_pfx_len);
1230            len = load_string(command);
1231            /* add match and tail */
1232            sprintf(&command[cursor_mb], "%s%s", chosen_match + match_pfx_len, match_buf);
1233            command_len = load_string(command);
1234            /* write out the matched command */
1235            /* paranoia: load_string can return 0 on conv error,
1236             * prevent passing pos = (0 - 12) to redraw */
1237            pos = command_len - len;
1238            redraw(cmdedit_y, pos >= 0 ? pos : 0);
1239        }
1240    }
1241# endif
1242 ret:
1243    free(chosen_match);
1244    free(match_buf);
1245}
1246
1247#endif  /* FEATURE_TAB_COMPLETION */
1248
1249
1250line_input_t* FAST_FUNC new_line_input_t(int flags)
1251{
1252    line_input_t *n = xzalloc(sizeof(*n));
1253    n->flags = flags;
1254    n->max_history = MAX_HISTORY;
1255    return n;
1256}
1257
1258
1259#if MAX_HISTORY > 0
1260
1261unsigned size_from_HISTFILESIZE(const char *hp)
1262{
1263    int size = MAX_HISTORY;
1264    if (hp) {
1265        size = atoi(hp);
1266        if (size <= 0)
1267            return 1;
1268        if (size > MAX_HISTORY)
1269            return MAX_HISTORY;
1270    }
1271    return size;
1272}
1273
1274static void save_command_ps_at_cur_history(void)
1275{
1276    if (command_ps[0] != BB_NUL) {
1277        int cur = state->cur_history;
1278        free(state->history[cur]);
1279
1280# if ENABLE_UNICODE_SUPPORT
1281        {
1282            char tbuf[MAX_LINELEN];
1283            save_string(tbuf, sizeof(tbuf));
1284            state->history[cur] = xstrdup(tbuf);
1285        }
1286# else
1287        state->history[cur] = xstrdup(command_ps);
1288# endif
1289    }
1290}
1291
1292/* state->flags is already checked to be nonzero */
1293static int get_previous_history(void)
1294{
1295    if ((state->flags & DO_HISTORY) && state->cur_history) {
1296        save_command_ps_at_cur_history();
1297        state->cur_history--;
1298        return 1;
1299    }
1300    beep();
1301    return 0;
1302}
1303
1304static int get_next_history(void)
1305{
1306    if (state->flags & DO_HISTORY) {
1307        if (state->cur_history < state->cnt_history) {
1308            save_command_ps_at_cur_history(); /* save the current history line */
1309            return ++state->cur_history;
1310        }
1311    }
1312    beep();
1313    return 0;
1314}
1315
1316# if ENABLE_FEATURE_EDITING_SAVEHISTORY
1317/* We try to ensure that concurrent additions to the history
1318 * do not overwrite each other.
1319 * Otherwise shell users get unhappy.
1320 *
1321 * History file is trimmed lazily, when it grows several times longer
1322 * than configured MAX_HISTORY lines.
1323 */
1324
1325static void free_line_input_t(line_input_t *n)
1326{
1327    int i = n->cnt_history;
1328    while (i > 0)
1329        free(n->history[--i]);
1330    free(n);
1331}
1332
1333/* state->flags is already checked to be nonzero */
1334static void load_history(line_input_t *st_parm)
1335{
1336    char *temp_h[MAX_HISTORY];
1337    char *line;
1338    FILE *fp;
1339    unsigned idx, i, line_len;
1340
1341    /* NB: do not trash old history if file can't be opened */
1342
1343    fp = fopen_for_read(st_parm->hist_file);
1344    if (fp) {
1345        /* clean up old history */
1346        for (idx = st_parm->cnt_history; idx > 0;) {
1347            idx--;
1348            free(st_parm->history[idx]);
1349            st_parm->history[idx] = NULL;
1350        }
1351
1352        /* fill temp_h[], retaining only last MAX_HISTORY lines */
1353        memset(temp_h, 0, sizeof(temp_h));
1354        idx = 0;
1355        st_parm->cnt_history_in_file = 0;
1356        while ((line = xmalloc_fgetline(fp)) != NULL) {
1357            if (line[0] == '\0') {
1358                free(line);
1359                continue;
1360            }
1361            free(temp_h[idx]);
1362            temp_h[idx] = line;
1363            st_parm->cnt_history_in_file++;
1364            idx++;
1365            if (idx == st_parm->max_history)
1366                idx = 0;
1367        }
1368        fclose(fp);
1369
1370        /* find first non-NULL temp_h[], if any */
1371        if (st_parm->cnt_history_in_file) {
1372            while (temp_h[idx] == NULL) {
1373                idx++;
1374                if (idx == st_parm->max_history)
1375                    idx = 0;
1376            }
1377        }
1378
1379        /* copy temp_h[] to st_parm->history[] */
1380        for (i = 0; i < st_parm->max_history;) {
1381            line = temp_h[idx];
1382            if (!line)
1383                break;
1384            idx++;
1385            if (idx == st_parm->max_history)
1386                idx = 0;
1387            line_len = strlen(line);
1388            if (line_len >= MAX_LINELEN)
1389                line[MAX_LINELEN-1] = '\0';
1390            st_parm->history[i++] = line;
1391        }
1392        st_parm->cnt_history = i;
1393        if (ENABLE_FEATURE_EDITING_SAVE_ON_EXIT)
1394            st_parm->cnt_history_in_file = i;
1395    }
1396}
1397
1398#  if ENABLE_FEATURE_EDITING_SAVE_ON_EXIT
1399void save_history(line_input_t *st)
1400{
1401    FILE *fp;
1402
1403    if (!st->hist_file)
1404        return;
1405    if (st->cnt_history <= st->cnt_history_in_file)
1406        return;
1407
1408    fp = fopen(st->hist_file, "a");
1409    if (fp) {
1410        int i, fd;
1411        char *new_name;
1412        line_input_t *st_temp;
1413
1414        for (i = st->cnt_history_in_file; i < st->cnt_history; i++)
1415            fprintf(fp, "%s\n", st->history[i]);
1416        fclose(fp);
1417
1418        /* we may have concurrently written entries from others.
1419         * load them */
1420        st_temp = new_line_input_t(st->flags);
1421        st_temp->hist_file = st->hist_file;
1422        st_temp->max_history = st->max_history;
1423        load_history(st_temp);
1424
1425        /* write out temp file and replace hist_file atomically */
1426        new_name = xasprintf("%s.%u.new", st->hist_file, (int) getpid());
1427        fd = open(new_name, O_WRONLY | O_CREAT | O_TRUNC, 0600);
1428        if (fd >= 0) {
1429            fp = xfdopen_for_write(fd);
1430            for (i = 0; i < st_temp->cnt_history; i++)
1431                fprintf(fp, "%s\n", st_temp->history[i]);
1432            fclose(fp);
1433            if (rename(new_name, st->hist_file) == 0)
1434                st->cnt_history_in_file = st_temp->cnt_history;
1435        }
1436        free(new_name);
1437        free_line_input_t(st_temp);
1438    }
1439}
1440#  else
1441static void save_history(char *str)
1442{
1443    int fd;
1444    int len, len2;
1445
1446    if (!state->hist_file)
1447        return;
1448
1449    fd = open(state->hist_file, O_WRONLY | O_CREAT | O_APPEND, 0600);
1450    if (fd < 0)
1451        return;
1452    xlseek(fd, 0, SEEK_END); /* paranoia */
1453    len = strlen(str);
1454    str[len] = '\n'; /* we (try to) do atomic write */
1455    len2 = full_write(fd, str, len + 1);
1456    str[len] = '\0';
1457    close(fd);
1458    if (len2 != len + 1)
1459        return; /* "wtf?" */
1460
1461    /* did we write so much that history file needs trimming? */
1462    state->cnt_history_in_file++;
1463    if (state->cnt_history_in_file > state->max_history * 4) {
1464        char *new_name;
1465        line_input_t *st_temp;
1466
1467        /* we may have concurrently written entries from others.
1468         * load them */
1469        st_temp = new_line_input_t(state->flags);
1470        st_temp->hist_file = state->hist_file;
1471        st_temp->max_history = state->max_history;
1472        load_history(st_temp);
1473
1474        /* write out temp file and replace hist_file atomically */
1475        new_name = xasprintf("%s.%u.new", state->hist_file, (int) getpid());
1476        fd = open(new_name, O_WRONLY | O_CREAT | O_TRUNC, 0600);
1477        if (fd >= 0) {
1478            FILE *fp;
1479            int i;
1480
1481            fp = xfdopen_for_write(fd);
1482            for (i = 0; i < st_temp->cnt_history; i++)
1483                fprintf(fp, "%s\n", st_temp->history[i]);
1484            fclose(fp);
1485            if (rename(new_name, state->hist_file) == 0)
1486                state->cnt_history_in_file = st_temp->cnt_history;
1487        }
1488        free(new_name);
1489        free_line_input_t(st_temp);
1490    }
1491}
1492#  endif
1493# else
1494#  define load_history(a) ((void)0)
1495#  define save_history(a) ((void)0)
1496# endif /* FEATURE_COMMAND_SAVEHISTORY */
1497
1498static void remember_in_history(char *str)
1499{
1500    int i;
1501
1502    if (!(state->flags & DO_HISTORY))
1503        return;
1504    if (str[0] == '\0')
1505        return;
1506    i = state->cnt_history;
1507    /* Don't save dupes */
1508    if (i && strcmp(state->history[i-1], str) == 0)
1509        return;
1510
1511    free(state->history[state->max_history]); /* redundant, paranoia */
1512    state->history[state->max_history] = NULL; /* redundant, paranoia */
1513
1514    /* If history[] is full, remove the oldest command */
1515    /* we need to keep history[state->max_history] empty, hence >=, not > */
1516    if (i >= state->max_history) {
1517        free(state->history[0]);
1518        for (i = 0; i < state->max_history-1; i++)
1519            state->history[i] = state->history[i+1];
1520        /* i == state->max_history-1 */
1521# if ENABLE_FEATURE_EDITING_SAVE_ON_EXIT
1522        if (state->cnt_history_in_file)
1523            state->cnt_history_in_file--;
1524# endif
1525    }
1526    /* i <= state->max_history-1 */
1527    state->history[i++] = xstrdup(str);
1528    /* i <= state->max_history */
1529    state->cur_history = i;
1530    state->cnt_history = i;
1531# if ENABLE_FEATURE_EDITING_SAVEHISTORY && !ENABLE_FEATURE_EDITING_SAVE_ON_EXIT
1532    save_history(str);
1533# endif
1534    IF_FEATURE_EDITING_FANCY_PROMPT(num_ok_lines++;)
1535}
1536
1537#else /* MAX_HISTORY == 0 */
1538# define remember_in_history(a) ((void)0)
1539#endif /* MAX_HISTORY */
1540
1541
1542#if ENABLE_FEATURE_EDITING_VI
1543/*
1544 * vi mode implemented 2005 by Paul Fox <pgf@foxharp.boston.ma.us>
1545 */
1546static void
1547vi_Word_motion(int eat)
1548{
1549    CHAR_T *command = command_ps;
1550
1551    while (cursor < command_len && !BB_isspace(command[cursor]))
1552        input_forward();
1553    if (eat) while (cursor < command_len && BB_isspace(command[cursor]))
1554        input_forward();
1555}
1556
1557static void
1558vi_word_motion(int eat)
1559{
1560    CHAR_T *command = command_ps;
1561
1562    if (BB_isalnum(command[cursor]) || command[cursor] == '_') {
1563        while (cursor < command_len
1564         && (BB_isalnum(command[cursor+1]) || command[cursor+1] == '_')
1565        ) {
1566            input_forward();
1567        }
1568    } else if (BB_ispunct(command[cursor])) {
1569        while (cursor < command_len && BB_ispunct(command[cursor+1]))
1570            input_forward();
1571    }
1572
1573    if (cursor < command_len)
1574        input_forward();
1575
1576    if (eat) {
1577        while (cursor < command_len && BB_isspace(command[cursor]))
1578            input_forward();
1579    }
1580}
1581
1582static void
1583vi_End_motion(void)
1584{
1585    CHAR_T *command = command_ps;
1586
1587    input_forward();
1588    while (cursor < command_len && BB_isspace(command[cursor]))
1589        input_forward();
1590    while (cursor < command_len-1 && !BB_isspace(command[cursor+1]))
1591        input_forward();
1592}
1593
1594static void
1595vi_end_motion(void)
1596{
1597    CHAR_T *command = command_ps;
1598
1599    if (cursor >= command_len-1)
1600        return;
1601    input_forward();
1602    while (cursor < command_len-1 && BB_isspace(command[cursor]))
1603        input_forward();
1604    if (cursor >= command_len-1)
1605        return;
1606    if (BB_isalnum(command[cursor]) || command[cursor] == '_') {
1607        while (cursor < command_len-1
1608         && (BB_isalnum(command[cursor+1]) || command[cursor+1] == '_')
1609        ) {
1610            input_forward();
1611        }
1612    } else if (BB_ispunct(command[cursor])) {
1613        while (cursor < command_len-1 && BB_ispunct(command[cursor+1]))
1614            input_forward();
1615    }
1616}
1617
1618static void
1619vi_Back_motion(void)
1620{
1621    CHAR_T *command = command_ps;
1622
1623    while (cursor > 0 && BB_isspace(command[cursor-1]))
1624        input_backward(1);
1625    while (cursor > 0 && !BB_isspace(command[cursor-1]))
1626        input_backward(1);
1627}
1628
1629static void
1630vi_back_motion(void)
1631{
1632    CHAR_T *command = command_ps;
1633
1634    if (cursor <= 0)
1635        return;
1636    input_backward(1);
1637    while (cursor > 0 && BB_isspace(command[cursor]))
1638        input_backward(1);
1639    if (cursor <= 0)
1640        return;
1641    if (BB_isalnum(command[cursor]) || command[cursor] == '_') {
1642        while (cursor > 0
1643         && (BB_isalnum(command[cursor-1]) || command[cursor-1] == '_')
1644        ) {
1645            input_backward(1);
1646        }
1647    } else if (BB_ispunct(command[cursor])) {
1648        while (cursor > 0 && BB_ispunct(command[cursor-1]))
1649            input_backward(1);
1650    }
1651}
1652#endif
1653
1654/* Modelled after bash 4.0 behavior of Ctrl-<arrow> */
1655static void ctrl_left(void)
1656{
1657    CHAR_T *command = command_ps;
1658
1659    while (1) {
1660        CHAR_T c;
1661
1662        input_backward(1);
1663        if (cursor == 0)
1664            break;
1665        c = command[cursor];
1666        if (c != ' ' && !BB_ispunct(c)) {
1667            /* we reached a "word" delimited by spaces/punct.
1668             * go to its beginning */
1669            while (1) {
1670                c = command[cursor - 1];
1671                if (c == ' ' || BB_ispunct(c))
1672                    break;
1673                input_backward(1);
1674                if (cursor == 0)
1675                    break;
1676            }
1677            break;
1678        }
1679    }
1680}
1681static void ctrl_right(void)
1682{
1683    CHAR_T *command = command_ps;
1684
1685    while (1) {
1686        CHAR_T c;
1687
1688        c = command[cursor];
1689        if (c == BB_NUL)
1690            break;
1691        if (c != ' ' && !BB_ispunct(c)) {
1692            /* we reached a "word" delimited by spaces/punct.
1693             * go to its end + 1 */
1694            while (1) {
1695                input_forward();
1696                c = command[cursor];
1697                if (c == BB_NUL || c == ' ' || BB_ispunct(c))
1698                    break;
1699            }
1700            break;
1701        }
1702        input_forward();
1703    }
1704}
1705
1706
1707/*
1708 * read_line_input and its helpers
1709 */
1710
1711#if ENABLE_FEATURE_EDITING_ASK_TERMINAL
1712static void ask_terminal(void)
1713{
1714    /* Ask terminal where is the cursor now.
1715     * lineedit_read_key handles response and corrects
1716     * our idea of current cursor position.
1717     * Testcase: run "echo -n long_line_long_line_long_line",
1718     * then type in a long, wrapping command and try to
1719     * delete it using backspace key.
1720     * Note: we print it _after_ prompt, because
1721     * prompt may contain CR. Example: PS1='\[\r\n\]\w '
1722     */
1723    /* Problem: if there is buffered input on stdin,
1724     * the response will be delivered later,
1725     * possibly to an unsuspecting application.
1726     * Testcase: "sleep 1; busybox ash" + press and hold [Enter].
1727     * Result:
1728     * ~/srcdevel/bbox/fix/busybox.t4 #
1729     * ~/srcdevel/bbox/fix/busybox.t4 #
1730     * ^[[59;34~/srcdevel/bbox/fix/busybox.t4 #  <-- garbage
1731     * ~/srcdevel/bbox/fix/busybox.t4 #
1732     *
1733     * Checking for input with poll only makes the race narrower,
1734     * I still can trigger it. Strace:
1735     *
1736     * write(1, "~/srcdevel/bbox/fix/busybox.t4 # ", 33) = 33
1737     * poll([{fd=0, events=POLLIN}], 1, 0) = 0 (Timeout)  <-- no input exists
1738     * write(1, "\33[6n", 4) = 4  <-- send the ESC sequence, quick!
1739     * poll([{fd=0, events=POLLIN}], 1, -1) = 1 ([{fd=0, revents=POLLIN}])
1740     * read(0, "\n", 1)      = 1  <-- oh crap, user's input got in first
1741     */
1742    struct pollfd pfd;
1743
1744    pfd.fd = STDIN_FILENO;
1745    pfd.events = POLLIN;
1746    if (safe_poll(&pfd, 1, 0) == 0) {
1747        S.sent_ESC_br6n = 1;
1748        fputs(ESC"[6n", stdout);
1749        fflush_all(); /* make terminal see it ASAP! */
1750    }
1751}
1752#else
1753#define ask_terminal() ((void)0)
1754#endif
1755
1756#if !ENABLE_FEATURE_EDITING_FANCY_PROMPT
1757static void parse_and_put_prompt(const char *prmt_ptr)
1758{
1759    cmdedit_prompt = prmt_ptr;
1760    cmdedit_prmt_len = strlen(prmt_ptr);
1761    put_prompt();
1762}
1763#else
1764static void parse_and_put_prompt(const char *prmt_ptr)
1765{
1766    int prmt_len = 0;
1767    size_t cur_prmt_len = 0;
1768    char flg_not_length = '[';
1769    char *prmt_mem_ptr = xzalloc(1);
1770    char *cwd_buf = xrealloc_getcwd_or_warn(NULL);
1771    char cbuf[2];
1772    char c;
1773    char *pbuf;
1774
1775    cmdedit_prmt_len = 0;
1776
1777    if (!cwd_buf) {
1778        cwd_buf = (char *)bb_msg_unknown;
1779    }
1780
1781    cbuf[1] = '\0'; /* never changes */
1782
1783    while (*prmt_ptr) {
1784        char *free_me = NULL;
1785
1786        pbuf = cbuf;
1787        c = *prmt_ptr++;
1788        if (c == '\\') {
1789            const char *cp = prmt_ptr;
1790            int l;
1791
1792            c = bb_process_escape_sequence(&prmt_ptr);
1793            if (prmt_ptr == cp) {
1794                if (*cp == '\0')
1795                    break;
1796                c = *prmt_ptr++;
1797
1798                switch (c) {
1799# if ENABLE_USERNAME_OR_HOMEDIR
1800                case 'u':
1801                    pbuf = user_buf ? user_buf : (char*)"";
1802                    break;
1803# endif
1804                case 'h':
1805                    pbuf = free_me = safe_gethostname();
1806                    *strchrnul(pbuf, '.') = '\0';
1807                    break;
1808                case '$':
1809                    c = (geteuid() == 0 ? '#' : '$');
1810                    break;
1811# if ENABLE_USERNAME_OR_HOMEDIR
1812                case 'w':
1813                    /* /home/user[/something] -> ~[/something] */
1814                    pbuf = cwd_buf;
1815                    l = strlen(home_pwd_buf);
1816                    if (l != 0
1817                     && strncmp(home_pwd_buf, cwd_buf, l) == 0
1818                     && (cwd_buf[l]=='/' || cwd_buf[l]=='\0')
1819                     && strlen(cwd_buf + l) < PATH_MAX
1820                    ) {
1821                        pbuf = free_me = xasprintf("~%s", cwd_buf + l);
1822                    }
1823                    break;
1824# endif
1825                case 'W':
1826                    pbuf = cwd_buf;
1827                    cp = strrchr(pbuf, '/');
1828                    if (cp != NULL && cp != pbuf)
1829                        pbuf += (cp-pbuf) + 1;
1830                    break;
1831                case '!':
1832                    pbuf = free_me = xasprintf("%d", num_ok_lines);
1833                    break;
1834                case 'e': case 'E':     /* \e \E = \033 */
1835                    c = '\033';
1836                    break;
1837                case 'x': case 'X': {
1838                    char buf2[4];
1839                    for (l = 0; l < 3;) {
1840                        unsigned h;
1841                        buf2[l++] = *prmt_ptr;
1842                        buf2[l] = '\0';
1843                        h = strtoul(buf2, &pbuf, 16);
1844                        if (h > UCHAR_MAX || (pbuf - buf2) < l) {
1845                            buf2[--l] = '\0';
1846                            break;
1847                        }
1848                        prmt_ptr++;
1849                    }
1850                    c = (char)strtoul(buf2, NULL, 16);
1851                    if (c == 0)
1852                        c = '?';
1853                    pbuf = cbuf;
1854                    break;
1855                }
1856                case '[': case ']':
1857                    if (c == flg_not_length) {
1858                        flg_not_length = (flg_not_length == '[' ? ']' : '[');
1859                        continue;
1860                    }
1861                    break;
1862                } /* switch */
1863            } /* if */
1864        } /* if */
1865        cbuf[0] = c;
1866        cur_prmt_len = strlen(pbuf);
1867        prmt_len += cur_prmt_len;
1868        if (flg_not_length != ']')
1869            cmdedit_prmt_len += cur_prmt_len;
1870        prmt_mem_ptr = strcat(xrealloc(prmt_mem_ptr, prmt_len+1), pbuf);
1871        free(free_me);
1872    } /* while */
1873
1874    if (cwd_buf != (char *)bb_msg_unknown)
1875        free(cwd_buf);
1876    cmdedit_prompt = prmt_mem_ptr;
1877    put_prompt();
1878}
1879#endif
1880
1881static void cmdedit_setwidth(unsigned w, int redraw_flg)
1882{
1883    cmdedit_termw = w;
1884    if (redraw_flg) {
1885        /* new y for current cursor */
1886        int new_y = (cursor + cmdedit_prmt_len) / w;
1887        /* redraw */
1888        redraw((new_y >= cmdedit_y ? new_y : cmdedit_y), command_len - cursor);
1889        fflush_all();
1890    }
1891}
1892
1893static void win_changed(int nsig)
1894{
1895    int sv_errno = errno;
1896    unsigned width;
1897
1898    get_terminal_width_height(0, &width, NULL);
1899//FIXME: cmdedit_setwidth() -> redraw() -> printf() -> KABOOM! (we are in signal handler!)
1900    cmdedit_setwidth(width, /*redraw_flg:*/ nsig);
1901
1902    errno = sv_errno;
1903}
1904
1905static int lineedit_read_key(char *read_key_buffer, int timeout)
1906{
1907    int64_t ic;
1908#if ENABLE_UNICODE_SUPPORT
1909    char unicode_buf[MB_CUR_MAX + 1];
1910    int unicode_idx = 0;
1911#endif
1912
1913    while (1) {
1914        /* Wait for input. TIMEOUT = -1 makes read_key wait even
1915         * on nonblocking stdin, TIMEOUT = 50 makes sure we won't
1916         * insist on full MB_CUR_MAX buffer to declare input like
1917         * "\xff\n",pause,"ls\n" invalid and thus won't lose "ls".
1918         *
1919         * Note: read_key sets errno to 0 on success.
1920         */
1921        ic = read_key(STDIN_FILENO, read_key_buffer, timeout);
1922        if (errno) {
1923#if ENABLE_UNICODE_SUPPORT
1924            if (errno == EAGAIN && unicode_idx != 0)
1925                goto pushback;
1926#endif
1927            break;
1928        }
1929
1930#if ENABLE_FEATURE_EDITING_ASK_TERMINAL
1931        if ((int32_t)ic == KEYCODE_CURSOR_POS
1932         && S.sent_ESC_br6n
1933        ) {
1934            S.sent_ESC_br6n = 0;
1935            if (cursor == 0) { /* otherwise it may be bogus */
1936                int col = ((ic >> 32) & 0x7fff) - 1;
1937                if (col > cmdedit_prmt_len) {
1938                    cmdedit_x += (col - cmdedit_prmt_len);
1939                    while (cmdedit_x >= cmdedit_termw) {
1940                        cmdedit_x -= cmdedit_termw;
1941                        cmdedit_y++;
1942                    }
1943                }
1944            }
1945            continue;
1946        }
1947#endif
1948
1949#if ENABLE_UNICODE_SUPPORT
1950        if (unicode_status == UNICODE_ON) {
1951            wchar_t wc;
1952
1953            if ((int32_t)ic < 0) /* KEYCODE_xxx */
1954                break;
1955            // TODO: imagine sequence like: 0xff,<left-arrow>: we are currently losing 0xff...
1956
1957            unicode_buf[unicode_idx++] = ic;
1958            unicode_buf[unicode_idx] = '\0';
1959            if (mbstowcs(&wc, unicode_buf, 1) != 1) {
1960                /* Not (yet?) a valid unicode char */
1961                if (unicode_idx < MB_CUR_MAX) {
1962                    timeout = 50;
1963                    continue;
1964                }
1965 pushback:
1966                /* Invalid sequence. Save all "bad bytes" except first */
1967                read_key_ungets(read_key_buffer, unicode_buf + 1, unicode_idx - 1);
1968# if !ENABLE_UNICODE_PRESERVE_BROKEN
1969                ic = CONFIG_SUBST_WCHAR;
1970# else
1971                ic = unicode_mark_raw_byte(unicode_buf[0]);
1972# endif
1973            } else {
1974                /* Valid unicode char, return its code */
1975                ic = wc;
1976            }
1977        }
1978#endif
1979        break;
1980    }
1981
1982    return ic;
1983}
1984
1985#if ENABLE_UNICODE_BIDI_SUPPORT
1986static int isrtl_str(void)
1987{
1988    int idx = cursor;
1989
1990    while (idx < command_len && unicode_bidi_is_neutral_wchar(command_ps[idx]))
1991        idx++;
1992    return unicode_bidi_isrtl(command_ps[idx]);
1993}
1994#else
1995# define isrtl_str() 0
1996#endif
1997
1998/* leave out the "vi-mode"-only case labels if vi editing isn't
1999 * configured. */
2000#define vi_case(caselabel) IF_FEATURE_EDITING_VI(case caselabel)
2001
2002/* convert uppercase ascii to equivalent control char, for readability */
2003#undef CTRL
2004#define CTRL(a) ((a) & ~0x40)
2005
2006enum {
2007    VI_CMDMODE_BIT = 0x40000000,
2008    /* 0x80000000 bit flags KEYCODE_xxx */
2009};
2010
2011#if ENABLE_FEATURE_REVERSE_SEARCH
2012/* Mimic readline Ctrl-R reverse history search.
2013 * When invoked, it shows the following prompt:
2014 * (reverse-i-search)'': user_input [cursor pos unchanged by Ctrl-R]
2015 * and typing results in search being performed:
2016 * (reverse-i-search)'tmp': cd /tmp [cursor under t in /tmp]
2017 * Search is performed by looking at progressively older lines in history.
2018 * Ctrl-R again searches for the next match in history.
2019 * Backspace deletes last matched char.
2020 * Control keys exit search and return to normal editing (at current history line).
2021 */
2022static int32_t reverse_i_search(void)
2023{
2024    char match_buf[128]; /* for user input */
2025    char read_key_buffer[KEYCODE_BUFFER_SIZE];
2026    const char *matched_history_line;
2027    const char *saved_prompt;
2028    int32_t ic;
2029
2030    matched_history_line = NULL;
2031    read_key_buffer[0] = 0;
2032    match_buf[0] = '\0';
2033
2034    /* Save and replace the prompt */
2035    saved_prompt = cmdedit_prompt;
2036    goto set_prompt;
2037
2038    while (1) {
2039        int h;
2040        unsigned match_buf_len = strlen(match_buf);
2041
2042        fflush_all();
2043//FIXME: correct timeout?
2044        ic = lineedit_read_key(read_key_buffer, -1);
2045
2046        switch (ic) {
2047        case CTRL('R'): /* searching for the next match */
2048            break;
2049
2050        case '\b':
2051        case '\x7f':
2052            /* Backspace */
2053            if (unicode_status == UNICODE_ON) {
2054                while (match_buf_len != 0) {
2055                    uint8_t c = match_buf[--match_buf_len];
2056                    if ((c & 0xc0) != 0x80) /* start of UTF-8 char? */
2057                        break; /* yes */
2058                }
2059            } else {
2060                if (match_buf_len != 0)
2061                    match_buf_len--;
2062            }
2063            match_buf[match_buf_len] = '\0';
2064            break;
2065
2066        default:
2067            if (ic < ' '
2068             || (!ENABLE_UNICODE_SUPPORT && ic >= 256)
2069             || (ENABLE_UNICODE_SUPPORT && ic >= VI_CMDMODE_BIT)
2070            ) {
2071                goto ret;
2072            }
2073
2074            /* Append this char */
2075#if ENABLE_UNICODE_SUPPORT
2076            if (unicode_status == UNICODE_ON) {
2077                mbstate_t mbstate = { 0 };
2078                char buf[MB_CUR_MAX + 1];
2079                int len = wcrtomb(buf, ic, &mbstate);
2080                if (len > 0) {
2081                    buf[len] = '\0';
2082                    if (match_buf_len + len < sizeof(match_buf))
2083                        strcpy(match_buf + match_buf_len, buf);
2084                }
2085            } else
2086#endif
2087            if (match_buf_len < sizeof(match_buf) - 1) {
2088                match_buf[match_buf_len] = ic;
2089                match_buf[match_buf_len + 1] = '\0';
2090            }
2091            break;
2092        } /* switch (ic) */
2093
2094        /* Search in history for match_buf */
2095        h = state->cur_history;
2096        if (ic == CTRL('R'))
2097            h--;
2098        while (h >= 0) {
2099            if (state->history[h]) {
2100                char *match = strstr(state->history[h], match_buf);
2101                if (match) {
2102                    state->cur_history = h;
2103                    matched_history_line = state->history[h];
2104                    command_len = load_string(matched_history_line);
2105                    cursor = match - matched_history_line;
2106//FIXME: cursor position for Unicode case
2107
2108                    free((char*)cmdedit_prompt);
2109 set_prompt:
2110                    cmdedit_prompt = xasprintf("(reverse-i-search)'%s': ", match_buf);
2111                    cmdedit_prmt_len = strlen(cmdedit_prompt);
2112                    goto do_redraw;
2113                }
2114            }
2115            h--;
2116        }
2117
2118        /* Not found */
2119        match_buf[match_buf_len] = '\0';
2120        beep();
2121        continue;
2122
2123 do_redraw:
2124        redraw(cmdedit_y, command_len - cursor);
2125    } /* while (1) */
2126
2127 ret:
2128    if (matched_history_line)
2129        command_len = load_string(matched_history_line);
2130
2131    free((char*)cmdedit_prompt);
2132    cmdedit_prompt = saved_prompt;
2133    cmdedit_prmt_len = strlen(cmdedit_prompt);
2134    redraw(cmdedit_y, command_len - cursor);
2135
2136    return ic;
2137}
2138#endif
2139
2140/* maxsize must be >= 2.
2141 * Returns:
2142 * -1 on read errors or EOF, or on bare Ctrl-D,
2143 * 0  on ctrl-C (the line entered is still returned in 'command'),
2144 * >0 length of input string, including terminating '\n'
2145 */
2146int FAST_FUNC read_line_input(line_input_t *st, const char *prompt, char *command, int maxsize, int timeout)
2147{
2148    int len;
2149#if ENABLE_FEATURE_TAB_COMPLETION
2150    smallint lastWasTab = 0;
2151#endif
2152    smallint break_out = 0;
2153#if ENABLE_FEATURE_EDITING_VI
2154    smallint vi_cmdmode = 0;
2155#endif
2156    struct termios initial_settings;
2157    struct termios new_settings;
2158    char read_key_buffer[KEYCODE_BUFFER_SIZE];
2159
2160    INIT_S();
2161
2162    if (tcgetattr(STDIN_FILENO, &initial_settings) < 0
2163     || !(initial_settings.c_lflag & ECHO)
2164    ) {
2165        /* Happens when e.g. stty -echo was run before */
2166        parse_and_put_prompt(prompt);
2167        /* fflush_all(); - done by parse_and_put_prompt */
2168        if (fgets(command, maxsize, stdin) == NULL)
2169            len = -1; /* EOF or error */
2170        else
2171            len = strlen(command);
2172        DEINIT_S();
2173        return len;
2174    }
2175
2176    init_unicode();
2177
2178// FIXME: audit & improve this
2179    if (maxsize > MAX_LINELEN)
2180        maxsize = MAX_LINELEN;
2181    S.maxsize = maxsize;
2182
2183    /* With zero flags, no other fields are ever used */
2184    state = st ? st : (line_input_t*) &const_int_0;
2185#if MAX_HISTORY > 0
2186# if ENABLE_FEATURE_EDITING_SAVEHISTORY
2187    if (state->hist_file)
2188        if (state->cnt_history == 0)
2189            load_history(state);
2190# endif
2191    if (state->flags & DO_HISTORY)
2192        state->cur_history = state->cnt_history;
2193#endif
2194
2195    /* prepare before init handlers */
2196    cmdedit_y = 0;  /* quasireal y, not true if line > xt*yt */
2197    command_len = 0;
2198#if ENABLE_UNICODE_SUPPORT
2199    command_ps = xzalloc(maxsize * sizeof(command_ps[0]));
2200#else
2201    command_ps = command;
2202    command[0] = '\0';
2203#endif
2204#define command command_must_not_be_used
2205
2206    new_settings = initial_settings;
2207    /* ~ICANON: unbuffered input (most c_cc[] are disabled, VMIN/VTIME are enabled) */
2208    /* ~ECHO, ~ECHONL: turn off echoing, including newline echoing */
2209    /* ~ISIG: turn off INTR (ctrl-C), QUIT, SUSP */
2210    new_settings.c_lflag &= ~(ICANON | ECHO | ECHONL | ISIG);
2211    /* reads would block only if < 1 char is available */
2212    new_settings.c_cc[VMIN] = 1;
2213    /* no timeout (reads block forever) */
2214    new_settings.c_cc[VTIME] = 0;
2215    /* Should be not needed if ISIG is off: */
2216    /* Turn off CTRL-C */
2217    /* new_settings.c_cc[VINTR] = _POSIX_VDISABLE; */
2218    tcsetattr_stdin_TCSANOW(&new_settings);
2219
2220#if ENABLE_USERNAME_OR_HOMEDIR
2221    {
2222        struct passwd *entry;
2223
2224        entry = getpwuid(geteuid());
2225        if (entry) {
2226            user_buf = xstrdup(entry->pw_name);
2227            home_pwd_buf = xstrdup(entry->pw_dir);
2228        }
2229    }
2230#endif
2231
2232#if 0
2233    for (i = 0; i <= state->max_history; i++)
2234        bb_error_msg("history[%d]:'%s'", i, state->history[i]);
2235    bb_error_msg("cur_history:%d cnt_history:%d", state->cur_history, state->cnt_history);
2236#endif
2237
2238    /* Print out the command prompt, optionally ask where cursor is */
2239    parse_and_put_prompt(prompt);
2240    ask_terminal();
2241
2242    /* Install window resize handler (NB: after *all* init is complete) */
2243//FIXME: save entire sigaction!
2244    previous_SIGWINCH_handler = signal(SIGWINCH, win_changed);
2245    win_changed(0); /* get initial window size */
2246
2247    read_key_buffer[0] = 0;
2248    while (1) {
2249        /*
2250         * The emacs and vi modes share much of the code in the big
2251         * command loop.  Commands entered when in vi's command mode
2252         * (aka "escape mode") get an extra bit added to distinguish
2253         * them - this keeps them from being self-inserted. This
2254         * clutters the big switch a bit, but keeps all the code
2255         * in one place.
2256         */
2257        int32_t ic, ic_raw;
2258
2259        fflush_all();
2260        ic = ic_raw = lineedit_read_key(read_key_buffer, timeout);
2261
2262#if ENABLE_FEATURE_REVERSE_SEARCH
2263 again:
2264#endif
2265#if ENABLE_FEATURE_EDITING_VI
2266        newdelflag = 1;
2267        if (vi_cmdmode) {
2268            /* btw, since KEYCODE_xxx are all < 0, this doesn't
2269             * change ic if it contains one of them: */
2270            ic |= VI_CMDMODE_BIT;
2271        }
2272#endif
2273
2274        switch (ic) {
2275        case '\n':
2276        case '\r':
2277        vi_case('\n'|VI_CMDMODE_BIT:)
2278        vi_case('\r'|VI_CMDMODE_BIT:)
2279            /* Enter */
2280            goto_new_line();
2281            break_out = 1;
2282            break;
2283        case CTRL('A'):
2284        vi_case('0'|VI_CMDMODE_BIT:)
2285            /* Control-a -- Beginning of line */
2286            input_backward(cursor);
2287            break;
2288        case CTRL('B'):
2289        vi_case('h'|VI_CMDMODE_BIT:)
2290        vi_case('\b'|VI_CMDMODE_BIT:) /* ^H */
2291        vi_case('\x7f'|VI_CMDMODE_BIT:) /* DEL */
2292            input_backward(1); /* Move back one character */
2293            break;
2294        case CTRL('E'):
2295        vi_case('$'|VI_CMDMODE_BIT:)
2296            /* Control-e -- End of line */
2297            put_till_end_and_adv_cursor();
2298            break;
2299        case CTRL('F'):
2300        vi_case('l'|VI_CMDMODE_BIT:)
2301        vi_case(' '|VI_CMDMODE_BIT:)
2302            input_forward(); /* Move forward one character */
2303            break;
2304        case '\b':   /* ^H */
2305        case '\x7f': /* DEL */
2306            if (!isrtl_str())
2307                input_backspace();
2308            else
2309                input_delete(0);
2310            break;
2311        case KEYCODE_DELETE:
2312            if (!isrtl_str())
2313                input_delete(0);
2314            else
2315                input_backspace();
2316            break;
2317#if ENABLE_FEATURE_TAB_COMPLETION
2318        case '\t':
2319            input_tab(&lastWasTab);
2320            break;
2321#endif
2322        case CTRL('K'):
2323            /* Control-k -- clear to end of line */
2324            command_ps[cursor] = BB_NUL;
2325            command_len = cursor;
2326            printf(SEQ_CLEAR_TILL_END_OF_SCREEN);
2327            break;
2328        case CTRL('L'):
2329        vi_case(CTRL('L')|VI_CMDMODE_BIT:)
2330            /* Control-l -- clear screen */
2331            printf(ESC"[H"); /* cursor to top,left */
2332            redraw(0, command_len - cursor);
2333            break;
2334#if MAX_HISTORY > 0
2335        case CTRL('N'):
2336        vi_case(CTRL('N')|VI_CMDMODE_BIT:)
2337        vi_case('j'|VI_CMDMODE_BIT:)
2338            /* Control-n -- Get next command in history */
2339            if (get_next_history())
2340                goto rewrite_line;
2341            break;
2342        case CTRL('P'):
2343        vi_case(CTRL('P')|VI_CMDMODE_BIT:)
2344        vi_case('k'|VI_CMDMODE_BIT:)
2345            /* Control-p -- Get previous command from history */
2346            if (get_previous_history())
2347                goto rewrite_line;
2348            break;
2349#endif
2350        case CTRL('U'):
2351        vi_case(CTRL('U')|VI_CMDMODE_BIT:)
2352            /* Control-U -- Clear line before cursor */
2353            if (cursor) {
2354                command_len -= cursor;
2355                memmove(command_ps, command_ps + cursor,
2356                    (command_len + 1) * sizeof(command_ps[0]));
2357                redraw(cmdedit_y, command_len);
2358            }
2359            break;
2360        case CTRL('W'):
2361        vi_case(CTRL('W')|VI_CMDMODE_BIT:)
2362            /* Control-W -- Remove the last word */
2363            while (cursor > 0 && BB_isspace(command_ps[cursor-1]))
2364                input_backspace();
2365            while (cursor > 0 && !BB_isspace(command_ps[cursor-1]))
2366                input_backspace();
2367            break;
2368#if ENABLE_FEATURE_REVERSE_SEARCH
2369        case CTRL('R'):
2370            ic = ic_raw = reverse_i_search();
2371            goto again;
2372#endif
2373
2374#if ENABLE_FEATURE_EDITING_VI
2375        case 'i'|VI_CMDMODE_BIT:
2376            vi_cmdmode = 0;
2377            break;
2378        case 'I'|VI_CMDMODE_BIT:
2379            input_backward(cursor);
2380            vi_cmdmode = 0;
2381            break;
2382        case 'a'|VI_CMDMODE_BIT:
2383            input_forward();
2384            vi_cmdmode = 0;
2385            break;
2386        case 'A'|VI_CMDMODE_BIT:
2387            put_till_end_and_adv_cursor();
2388            vi_cmdmode = 0;
2389            break;
2390        case 'x'|VI_CMDMODE_BIT:
2391            input_delete(1);
2392            break;
2393        case 'X'|VI_CMDMODE_BIT:
2394            if (cursor > 0) {
2395                input_backward(1);
2396                input_delete(1);
2397            }
2398            break;
2399        case 'W'|VI_CMDMODE_BIT:
2400            vi_Word_motion(1);
2401            break;
2402        case 'w'|VI_CMDMODE_BIT:
2403            vi_word_motion(1);
2404            break;
2405        case 'E'|VI_CMDMODE_BIT:
2406            vi_End_motion();
2407            break;
2408        case 'e'|VI_CMDMODE_BIT:
2409            vi_end_motion();
2410            break;
2411        case 'B'|VI_CMDMODE_BIT:
2412            vi_Back_motion();
2413            break;
2414        case 'b'|VI_CMDMODE_BIT:
2415            vi_back_motion();
2416            break;
2417        case 'C'|VI_CMDMODE_BIT:
2418            vi_cmdmode = 0;
2419            /* fall through */
2420        case 'D'|VI_CMDMODE_BIT:
2421            goto clear_to_eol;
2422
2423        case 'c'|VI_CMDMODE_BIT:
2424            vi_cmdmode = 0;
2425            /* fall through */
2426        case 'd'|VI_CMDMODE_BIT: {
2427            int nc, sc;
2428
2429            ic = lineedit_read_key(read_key_buffer, timeout);
2430            if (errno) /* error */
2431                goto return_error_indicator;
2432            if (ic == ic_raw) { /* "cc", "dd" */
2433                input_backward(cursor);
2434                goto clear_to_eol;
2435                break;
2436            }
2437
2438            sc = cursor;
2439            switch (ic) {
2440            case 'w':
2441            case 'W':
2442            case 'e':
2443            case 'E':
2444                switch (ic) {
2445                case 'w':   /* "dw", "cw" */
2446                    vi_word_motion(vi_cmdmode);
2447                    break;
2448                case 'W':   /* 'dW', 'cW' */
2449                    vi_Word_motion(vi_cmdmode);
2450                    break;
2451                case 'e':   /* 'de', 'ce' */
2452                    vi_end_motion();
2453                    input_forward();
2454                    break;
2455                case 'E':   /* 'dE', 'cE' */
2456                    vi_End_motion();
2457                    input_forward();
2458                    break;
2459                }
2460                nc = cursor;
2461                input_backward(cursor - sc);
2462                while (nc-- > cursor)
2463                    input_delete(1);
2464                break;
2465            case 'b':  /* "db", "cb" */
2466            case 'B':  /* implemented as B */
2467                if (ic == 'b')
2468                    vi_back_motion();
2469                else
2470                    vi_Back_motion();
2471                while (sc-- > cursor)
2472                    input_delete(1);
2473                break;
2474            case ' ':  /* "d ", "c " */
2475                input_delete(1);
2476                break;
2477            case '$':  /* "d$", "c$" */
2478 clear_to_eol:
2479                while (cursor < command_len)
2480                    input_delete(1);
2481                break;
2482            }
2483            break;
2484        }
2485        case 'p'|VI_CMDMODE_BIT:
2486            input_forward();
2487            /* fallthrough */
2488        case 'P'|VI_CMDMODE_BIT:
2489            put();
2490            break;
2491        case 'r'|VI_CMDMODE_BIT:
2492//FIXME: unicode case?
2493            ic = lineedit_read_key(read_key_buffer, timeout);
2494            if (errno) /* error */
2495                goto return_error_indicator;
2496            if (ic < ' ' || ic > 255) {
2497                beep();
2498            } else {
2499                command_ps[cursor] = ic;
2500                bb_putchar(ic);
2501                bb_putchar('\b');
2502            }
2503            break;
2504        case '\x1b': /* ESC */
2505            if (state->flags & VI_MODE) {
2506                /* insert mode --> command mode */
2507                vi_cmdmode = 1;
2508                input_backward(1);
2509            }
2510            /* Handle a few ESC-<key> combinations the same way
2511             * standard readline bindings (IOW: bash) do.
2512             * Often, Alt-<key> generates ESC-<key>.
2513             */
2514            ic = lineedit_read_key(read_key_buffer, timeout);
2515            switch (ic) {
2516                //case KEYCODE_LEFT: - bash doesn't do this
2517                case 'b':
2518                    ctrl_left();
2519                    break;
2520                //case KEYCODE_RIGHT: - bash doesn't do this
2521                case 'f':
2522                    ctrl_right();
2523                    break;
2524                //case KEYCODE_DELETE: - bash doesn't do this
2525                case 'd':  /* Alt-D */
2526                {
2527                    /* Delete word forward */
2528                    int nc, sc = cursor;
2529                    ctrl_right();
2530                    nc = cursor - sc;
2531                    input_backward(nc);
2532                    while (--nc >= 0)
2533                        input_delete(1);
2534                    break;
2535                }
2536                case '\b':   /* Alt-Backspace(?) */
2537                case '\x7f': /* Alt-Backspace(?) */
2538                //case 'w': - bash doesn't do this
2539                {
2540                    /* Delete word backward */
2541                    int sc = cursor;
2542                    ctrl_left();
2543                    while (sc-- > cursor)
2544                        input_delete(1);
2545                    break;
2546                }
2547            }
2548            break;
2549#endif /* FEATURE_COMMAND_EDITING_VI */
2550
2551#if MAX_HISTORY > 0
2552        case KEYCODE_UP:
2553            if (get_previous_history())
2554                goto rewrite_line;
2555            beep();
2556            break;
2557        case KEYCODE_DOWN:
2558            if (!get_next_history())
2559                break;
2560 rewrite_line:
2561            /* Rewrite the line with the selected history item */
2562            /* change command */
2563            command_len = load_string(state->history[state->cur_history] ?
2564                    state->history[state->cur_history] : "");
2565            /* redraw and go to eol (bol, in vi) */
2566            redraw(cmdedit_y, (state->flags & VI_MODE) ? 9999 : 0);
2567            break;
2568#endif
2569        case KEYCODE_RIGHT:
2570            input_forward();
2571            break;
2572        case KEYCODE_LEFT:
2573            input_backward(1);
2574            break;
2575        case KEYCODE_CTRL_LEFT:
2576        case KEYCODE_ALT_LEFT: /* bash doesn't do it */
2577            ctrl_left();
2578            break;
2579        case KEYCODE_CTRL_RIGHT:
2580        case KEYCODE_ALT_RIGHT: /* bash doesn't do it */
2581            ctrl_right();
2582            break;
2583        case KEYCODE_HOME:
2584            input_backward(cursor);
2585            break;
2586        case KEYCODE_END:
2587            put_till_end_and_adv_cursor();
2588            break;
2589
2590        default:
2591            if (initial_settings.c_cc[VINTR] != 0
2592             && ic_raw == initial_settings.c_cc[VINTR]
2593            ) {
2594                /* Ctrl-C (usually) - stop gathering input */
2595                goto_new_line();
2596                command_len = 0;
2597                break_out = -1; /* "do not append '\n'" */
2598                break;
2599            }
2600            if (initial_settings.c_cc[VEOF] != 0
2601             && ic_raw == initial_settings.c_cc[VEOF]
2602            ) {
2603                /* Ctrl-D (usually) - delete one character,
2604                 * or exit if len=0 and no chars to delete */
2605                if (command_len == 0) {
2606                    errno = 0;
2607
2608        case -1: /* error (e.g. EIO when tty is destroyed) */
2609 IF_FEATURE_EDITING_VI(return_error_indicator:)
2610                    break_out = command_len = -1;
2611                    break;
2612                }
2613                input_delete(0);
2614                break;
2615            }
2616//          /* Control-V -- force insert of next char */
2617//          if (c == CTRL('V')) {
2618//              if (safe_read(STDIN_FILENO, &c, 1) < 1)
2619//                  goto return_error_indicator;
2620//              if (c == 0) {
2621//                  beep();
2622//                  break;
2623//              }
2624//          }
2625            if (ic < ' '
2626             || (!ENABLE_UNICODE_SUPPORT && ic >= 256)
2627             || (ENABLE_UNICODE_SUPPORT && ic >= VI_CMDMODE_BIT)
2628            ) {
2629                /* If VI_CMDMODE_BIT is set, ic is >= 256
2630                 * and vi mode ignores unexpected chars.
2631                 * Otherwise, we are here if ic is a
2632                 * control char or an unhandled ESC sequence,
2633                 * which is also ignored.
2634                 */
2635                break;
2636            }
2637            if ((int)command_len >= (maxsize - 2)) {
2638                /* Not enough space for the char and EOL */
2639                break;
2640            }
2641
2642            command_len++;
2643            if (cursor == (command_len - 1)) {
2644                /* We are at the end, append */
2645                command_ps[cursor] = ic;
2646                command_ps[cursor + 1] = BB_NUL;
2647                put_cur_glyph_and_inc_cursor();
2648                if (unicode_bidi_isrtl(ic))
2649                    input_backward(1);
2650            } else {
2651                /* In the middle, insert */
2652                int sc = cursor;
2653
2654                memmove(command_ps + sc + 1, command_ps + sc,
2655                    (command_len - sc) * sizeof(command_ps[0]));
2656                command_ps[sc] = ic;
2657                /* is right-to-left char, or neutral one (e.g. comma) was just added to rtl text? */
2658                if (!isrtl_str())
2659                    sc++; /* no */
2660                put_till_end_and_adv_cursor();
2661                /* to prev x pos + 1 */
2662                input_backward(cursor - sc);
2663            }
2664            break;
2665        } /* switch (ic) */
2666
2667        if (break_out)
2668            break;
2669
2670#if ENABLE_FEATURE_TAB_COMPLETION
2671        if (ic_raw != '\t')
2672            lastWasTab = 0;
2673#endif
2674    } /* while (1) */
2675
2676#if ENABLE_FEATURE_EDITING_ASK_TERMINAL
2677    if (S.sent_ESC_br6n) {
2678        /* "sleep 1; busybox ash" + hold [Enter] to trigger.
2679         * We sent "ESC [ 6 n", but got '\n' first, and
2680         * KEYCODE_CURSOR_POS response is now buffered from terminal.
2681         * It's bad already and not much can be done with it
2682         * (it _will_ be visible for the next process to read stdin),
2683         * but without this delay it even shows up on the screen
2684         * as garbage because we restore echo settings with tcsetattr
2685         * before it comes in. UGLY!
2686         */
2687        usleep(20*1000);
2688    }
2689#endif
2690
2691/* End of bug-catching "command_must_not_be_used" trick */
2692#undef command
2693
2694#if ENABLE_UNICODE_SUPPORT
2695    command[0] = '\0';
2696    if (command_len > 0)
2697        command_len = save_string(command, maxsize - 1);
2698    free(command_ps);
2699#endif
2700
2701    if (command_len > 0)
2702        remember_in_history(command);
2703
2704    if (break_out > 0) {
2705        command[command_len++] = '\n';
2706        command[command_len] = '\0';
2707    }
2708
2709#if ENABLE_FEATURE_TAB_COMPLETION
2710    free_tab_completion_data();
2711#endif
2712
2713    /* restore initial_settings */
2714    tcsetattr_stdin_TCSANOW(&initial_settings);
2715    /* restore SIGWINCH handler */
2716    signal(SIGWINCH, previous_SIGWINCH_handler);
2717    fflush_all();
2718
2719    len = command_len;
2720    DEINIT_S();
2721
2722    return len; /* can't return command_len, DEINIT_S() destroys it */
2723}
2724
2725#else  /* !FEATURE_EDITING */
2726
2727#undef read_line_input
2728int FAST_FUNC read_line_input(const char* prompt, char* command, int maxsize)
2729{
2730    fputs(prompt, stdout);
2731    fflush_all();
2732    if (!fgets(command, maxsize, stdin))
2733        return -1;
2734    return strlen(command);
2735}
2736
2737#endif  /* !FEATURE_EDITING */
2738
2739
2740/*
2741 * Testing
2742 */
2743
2744#ifdef TEST
2745
2746#include <locale.h>
2747
2748const char *applet_name = "debug stuff usage";
2749
2750int main(int argc, char **argv)
2751{
2752    char buff[MAX_LINELEN];
2753    char *prompt =
2754#if ENABLE_FEATURE_EDITING_FANCY_PROMPT
2755        "\\[\\033[32;1m\\]\\u@\\[\\x1b[33;1m\\]\\h:"
2756        "\\[\\033[34;1m\\]\\w\\[\\033[35;1m\\] "
2757        "\\!\\[\\e[36;1m\\]\\$ \\[\\E[0m\\]";
2758#else
2759        "% ";
2760#endif
2761
2762    while (1) {
2763        int l;
2764        l = read_line_input(prompt, buff);
2765        if (l <= 0 || buff[l-1] != '\n')
2766            break;
2767        buff[l-1] = '\0';
2768        printf("*** read_line_input() returned line =%s=\n", buff);
2769    }
2770    printf("*** read_line_input() detect ^D\n");
2771    return 0;
2772}
2773
2774#endif  /* TEST */
Note: See TracBrowser for help on using the repository browser.