Ignore:
Timestamp:
Feb 25, 2011, 9:26:54 PM (13 years ago)
Author:
Bruno Cornec
Message:
  • Update mindi-busybox to 1.18.3 to avoid problems with the tar command which is now failing on recent versions with busybox 1.7.3
File:
1 edited

Legend:

Unmodified
Added
Removed
  • branches/2.2.9/mindi-busybox/libbb/lineedit.c

    r1765 r2725  
    11/* vi: set sw=4 ts=4: */
    22/*
    3  * Termios command line History and Editing.
     3 * Command line editing.
    44 *
    55 * Copyright (c) 1986-2003 may safely be consumed by a BSD or GPL license.
     
    1616
    1717/*
    18    Usage and known bugs:
    19    Terminal key codes are not extensive, and more will probably
    20    need to be added. This version was created on Debian GNU/Linux 2.x.
    21    Delete, Backspace, Home, End, and the arrow keys were tested
    22    to work in an Xterm and console. Ctrl-A also works as Home.
    23    Ctrl-E also works as End.
    24 
    25    Small bugs (simple effect):
    26    - not true viewing if terminal size (x*y symbols) less
    27      size (prompt + editor's line + 2 symbols)
    28    - not true viewing if length prompt less terminal width
     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\] '
    2941 */
    30 
    3142#include "libbb.h"
    32 
    33 
    34 /* FIXME: obsolete CONFIG item? */
    35 #define ENABLE_FEATURE_NONPRINTABLE_INVERSE_PUT 0
    36 
     43#include "unicode.h"
    3744
    3845#ifdef TEST
    39 
    40 #define ENABLE_FEATURE_EDITING 0
    41 #define ENABLE_FEATURE_TAB_COMPLETION 0
    42 #define ENABLE_FEATURE_USERNAME_COMPLETION 0
    43 #define ENABLE_FEATURE_NONPRINTABLE_INVERSE_PUT 0
    44 #define ENABLE_FEATURE_CLEAN_UP 0
    45 
    46 #endif  /* TEST */
     46# define ENABLE_FEATURE_EDITING 0
     47# define ENABLE_FEATURE_TAB_COMPLETION 0
     48# define ENABLE_FEATURE_USERNAME_COMPLETION 0
     49#endif
    4750
    4851
     
    5053#if ENABLE_FEATURE_EDITING
    5154
    52 #if ENABLE_LOCALE_SUPPORT
    53 #define Isprint(c) isprint(c)
     55
     56#define ENABLE_USERNAME_OR_HOMEDIR \
     57    (ENABLE_FEATURE_USERNAME_COMPLETION || ENABLE_FEATURE_EDITING_FANCY_PROMPT)
     58#define IF_USERNAME_OR_HOMEDIR(...)
     59#if ENABLE_USERNAME_OR_HOMEDIR
     60# undef IF_USERNAME_OR_HOMEDIR
     61# define IF_USERNAME_OR_HOMEDIR(...) __VA_ARGS__
     62#endif
     63
     64
     65#undef CHAR_T
     66#if ENABLE_UNICODE_SUPPORT
     67# define BB_NUL ((wchar_t)0)
     68# define CHAR_T wchar_t
     69static bool BB_isspace(CHAR_T c) { return ((unsigned)c < 256 && isspace(c)); }
     70# if ENABLE_FEATURE_EDITING_VI
     71static bool BB_isalnum(CHAR_T c) { return ((unsigned)c < 256 && isalnum(c)); }
     72# endif
     73static bool BB_ispunct(CHAR_T c) { return ((unsigned)c < 256 && ispunct(c)); }
     74# undef isspace
     75# undef isalnum
     76# undef ispunct
     77# undef isprint
     78# define isspace isspace_must_not_be_used
     79# define isalnum isalnum_must_not_be_used
     80# define ispunct ispunct_must_not_be_used
     81# define isprint isprint_must_not_be_used
    5482#else
    55 #define Isprint(c) ((c) >= ' ' && (c) != ((unsigned char)'\233'))
    56 #endif
    57 
    58 #define ENABLE_FEATURE_GETUSERNAME_AND_HOMEDIR \
    59 (ENABLE_FEATURE_USERNAME_COMPLETION || ENABLE_FEATURE_EDITING_FANCY_PROMPT)
    60 
    61 enum { MAX_LINELEN = CONFIG_FEATURE_EDITING_MAX_LEN };
    62 
    63 static line_input_t *state;
    64 
    65 static struct termios initial_settings, new_settings;
    66 
    67 static volatile unsigned cmdedit_termw = 80;        /* actual terminal width */
    68 
    69 static int cmdedit_x;           /* real x terminal position */
    70 static int cmdedit_y;           /* pseudoreal y terminal position */
    71 static int cmdedit_prmt_len;    /* length of prompt (without colors etc) */
    72 
    73 static unsigned cursor;
    74 static unsigned command_len;
    75 static char *command_ps;
    76 static const char *cmdedit_prompt;
    77 
     83# define BB_NUL '\0'
     84# define CHAR_T char
     85# define BB_isspace(c) isspace(c)
     86# define BB_isalnum(c) isalnum(c)
     87# define BB_ispunct(c) ispunct(c)
     88#endif
     89#if ENABLE_UNICODE_PRESERVE_BROKEN
     90# define unicode_mark_raw_byte(wc)   ((wc) | 0x20000000)
     91# define unicode_is_raw_byte(wc)     ((wc) & 0x20000000)
     92#else
     93# define unicode_is_raw_byte(wc)     0
     94#endif
     95
     96
     97#define ESC "\033"
     98
     99#define SEQ_CLEAR_TILL_END_OF_SCREEN  ESC"[J"
     100//#define SEQ_CLEAR_TILL_END_OF_LINE  ESC"[K"
     101
     102
     103enum {
     104    MAX_LINELEN = CONFIG_FEATURE_EDITING_MAX_LEN < 0x7ff0
     105                  ? CONFIG_FEATURE_EDITING_MAX_LEN
     106                  : 0x7ff0
     107};
     108
     109#if ENABLE_USERNAME_OR_HOMEDIR
     110static const char null_str[] ALIGN1 = "";
     111#endif
     112
     113/* We try to minimize both static and stack usage. */
     114struct lineedit_statics {
     115    line_input_t *state;
     116
     117    volatile unsigned cmdedit_termw; /* = 80; */ /* actual terminal width */
     118    sighandler_t previous_SIGWINCH_handler;
     119
     120    unsigned cmdedit_x;        /* real x (col) terminal position */
     121    unsigned cmdedit_y;        /* pseudoreal y (row) terminal position */
     122    unsigned cmdedit_prmt_len; /* length of prompt (without colors etc) */
     123
     124    unsigned cursor;
     125    int command_len; /* must be signed */
     126    /* signed maxsize: we want x in "if (x > S.maxsize)"
     127     * to _not_ be promoted to unsigned */
     128    int maxsize;
     129    CHAR_T *command_ps;
     130
     131    const char *cmdedit_prompt;
    78132#if ENABLE_FEATURE_EDITING_FANCY_PROMPT
    79 static char *hostname_buf;
    80 static int num_ok_lines = 1;
    81 #endif
    82 
    83 #if ENABLE_FEATURE_GETUSERNAME_AND_HOMEDIR
    84 static const char null_str[] = "";
    85 static char *user_buf;
    86 static char *home_pwd_buf = (char*)null_str;
    87 #endif
     133    int num_ok_lines; /* = 1; */
     134#endif
     135
     136#if ENABLE_USERNAME_OR_HOMEDIR
     137    char *user_buf;
     138    char *home_pwd_buf; /* = (char*)null_str; */
     139#endif
     140
     141#if ENABLE_FEATURE_TAB_COMPLETION
     142    char **matches;
     143    unsigned num_matches;
     144#endif
     145
     146#if ENABLE_FEATURE_EDITING_VI
     147# define DELBUFSIZ 128
     148    CHAR_T *delptr;
     149    smallint newdelflag;     /* whether delbuf should be reused yet */
     150    CHAR_T delbuf[DELBUFSIZ];  /* a place to store deleted characters */
     151#endif
     152#if ENABLE_FEATURE_EDITING_ASK_TERMINAL
     153    smallint sent_ESC_br6n;
     154#endif
     155};
     156
     157/* See lineedit_ptr_hack.c */
     158extern struct lineedit_statics *const lineedit_ptr_to_statics;
     159
     160#define S (*lineedit_ptr_to_statics)
     161#define state            (S.state           )
     162#define cmdedit_termw    (S.cmdedit_termw   )
     163#define previous_SIGWINCH_handler (S.previous_SIGWINCH_handler)
     164#define cmdedit_x        (S.cmdedit_x       )
     165#define cmdedit_y        (S.cmdedit_y       )
     166#define cmdedit_prmt_len (S.cmdedit_prmt_len)
     167#define cursor           (S.cursor          )
     168#define command_len      (S.command_len     )
     169#define command_ps       (S.command_ps      )
     170#define cmdedit_prompt   (S.cmdedit_prompt  )
     171#define num_ok_lines     (S.num_ok_lines    )
     172#define user_buf         (S.user_buf        )
     173#define home_pwd_buf     (S.home_pwd_buf    )
     174#define matches          (S.matches         )
     175#define num_matches      (S.num_matches     )
     176#define delptr           (S.delptr          )
     177#define newdelflag       (S.newdelflag      )
     178#define delbuf           (S.delbuf          )
     179
     180#define INIT_S() do { \
     181    (*(struct lineedit_statics**)&lineedit_ptr_to_statics) = xzalloc(sizeof(S)); \
     182    barrier(); \
     183    cmdedit_termw = 80; \
     184    IF_FEATURE_EDITING_FANCY_PROMPT(num_ok_lines = 1;) \
     185    IF_USERNAME_OR_HOMEDIR(home_pwd_buf = (char*)null_str;) \
     186} while (0)
     187static void deinit_S(void)
     188{
     189#if ENABLE_FEATURE_EDITING_FANCY_PROMPT
     190    /* This one is allocated only if FANCY_PROMPT is on
     191     * (otherwise it points to verbatim prompt (NOT malloced)) */
     192    free((char*)cmdedit_prompt);
     193#endif
     194#if ENABLE_USERNAME_OR_HOMEDIR
     195    free(user_buf);
     196    if (home_pwd_buf != null_str)
     197        free(home_pwd_buf);
     198#endif
     199    free(lineedit_ptr_to_statics);
     200}
     201#define DEINIT_S() deinit_S()
     202
     203
     204#if ENABLE_UNICODE_SUPPORT
     205static size_t load_string(const char *src, int maxsize)
     206{
     207    ssize_t len = mbstowcs(command_ps, src, maxsize - 1);
     208    if (len < 0)
     209        len = 0;
     210    command_ps[len] = BB_NUL;
     211    return len;
     212}
     213static unsigned save_string(char *dst, unsigned maxsize)
     214{
     215# if !ENABLE_UNICODE_PRESERVE_BROKEN
     216    ssize_t len = wcstombs(dst, command_ps, maxsize - 1);
     217    if (len < 0)
     218        len = 0;
     219    dst[len] = '\0';
     220    return len;
     221# else
     222    unsigned dstpos = 0;
     223    unsigned srcpos = 0;
     224
     225    maxsize--;
     226    while (dstpos < maxsize) {
     227        wchar_t wc;
     228        int n = srcpos;
     229
     230        /* Convert up to 1st invalid byte (or up to end) */
     231        while ((wc = command_ps[srcpos]) != BB_NUL
     232            && !unicode_is_raw_byte(wc)
     233        ) {
     234            srcpos++;
     235        }
     236        command_ps[srcpos] = BB_NUL;
     237        n = wcstombs(dst + dstpos, command_ps + n, maxsize - dstpos);
     238        if (n < 0) /* should not happen */
     239            break;
     240        dstpos += n;
     241        if (wc == BB_NUL) /* usually is */
     242            break;
     243
     244        /* We do have invalid byte here! */
     245        command_ps[srcpos] = wc; /* restore it */
     246        srcpos++;
     247        if (dstpos == maxsize)
     248            break;
     249        dst[dstpos++] = (char) wc;
     250    }
     251    dst[dstpos] = '\0';
     252    return dstpos;
     253# endif
     254}
     255/* I thought just fputwc(c, stdout) would work. But no... */
     256static void BB_PUTCHAR(wchar_t c)
     257{
     258    char buf[MB_CUR_MAX + 1];
     259    mbstate_t mbst = { 0 };
     260    ssize_t len;
     261
     262    len = wcrtomb(buf, c, &mbst);
     263    if (len > 0) {
     264        buf[len] = '\0';
     265        fputs(buf, stdout);
     266    }
     267}
     268# if ENABLE_UNICODE_COMBINING_WCHARS || ENABLE_UNICODE_WIDE_WCHARS
     269static wchar_t adjust_width_and_validate_wc(unsigned *width_adj, wchar_t wc)
     270# else
     271static wchar_t adjust_width_and_validate_wc(wchar_t wc)
     272#  define adjust_width_and_validate_wc(width_adj, wc) \
     273    ((*(width_adj))++, adjust_width_and_validate_wc(wc))
     274# endif
     275{
     276    int w = 1;
     277
     278    if (unicode_status == UNICODE_ON) {
     279        if (wc > CONFIG_LAST_SUPPORTED_WCHAR) {
     280            /* note: also true for unicode_is_raw_byte(wc) */
     281            goto subst;
     282        }
     283        w = wcwidth(wc);
     284        if ((ENABLE_UNICODE_COMBINING_WCHARS && w < 0)
     285         || (!ENABLE_UNICODE_COMBINING_WCHARS && w <= 0)
     286         || (!ENABLE_UNICODE_WIDE_WCHARS && w > 1)
     287        ) {
     288 subst:
     289            w = 1;
     290            wc = CONFIG_SUBST_WCHAR;
     291        }
     292    }
     293
     294# if ENABLE_UNICODE_COMBINING_WCHARS || ENABLE_UNICODE_WIDE_WCHARS
     295    *width_adj += w;
     296#endif
     297    return wc;
     298}
     299#else /* !UNICODE */
     300static size_t load_string(const char *src, int maxsize)
     301{
     302    safe_strncpy(command_ps, src, maxsize);
     303    return strlen(command_ps);
     304}
     305# if ENABLE_FEATURE_TAB_COMPLETION
     306static void save_string(char *dst, unsigned maxsize)
     307{
     308    safe_strncpy(dst, command_ps, maxsize);
     309}
     310# endif
     311# define BB_PUTCHAR(c) bb_putchar(c)
     312/* Should never be called: */
     313int adjust_width_and_validate_wc(unsigned *width_adj, int wc);
     314#endif
     315
    88316
    89317/* Put 'command_ps[cursor]', cursor++.
    90318 * Advance cursor on screen. If we reached right margin, scroll text up
    91319 * and remove terminal margin effect by printing 'next_char' */
    92 static void cmdedit_set_out_char(int next_char)
    93 {
    94     int c = (unsigned char)command_ps[cursor];
    95 
    96     if (c == '\0') {
     320#define HACK_FOR_WRONG_WIDTH 1
     321static void put_cur_glyph_and_inc_cursor(void)
     322{
     323    CHAR_T c = command_ps[cursor];
     324    unsigned width = 0;
     325    int ofs_to_right;
     326
     327    if (c == BB_NUL) {
    97328        /* erase character after end of input string */
    98329        c = ' ';
    99     }
    100 #if ENABLE_FEATURE_NONPRINTABLE_INVERSE_PUT
    101     /* Display non-printable characters in reverse */
    102     if (!Isprint(c)) {
    103         if (c >= 128)
    104             c -= 128;
    105         if (c < ' ')
    106             c += '@';
    107         if (c == 127)
    108             c = '?';
    109         printf("\033[7m%c\033[0m", c);
    110     } else
    111 #endif
    112     {
    113         if (initial_settings.c_lflag & ECHO)
    114             putchar(c);
    115     }
    116     if (++cmdedit_x >= cmdedit_termw) {
    117         /* terminal is scrolled down */
     330    } else {
     331        /* advance cursor only if we aren't at the end yet */
     332        cursor++;
     333        if (unicode_status == UNICODE_ON) {
     334            IF_UNICODE_WIDE_WCHARS(width = cmdedit_x;)
     335            c = adjust_width_and_validate_wc(&cmdedit_x, c);
     336            IF_UNICODE_WIDE_WCHARS(width = cmdedit_x - width;)
     337        } else {
     338            cmdedit_x++;
     339        }
     340    }
     341
     342    ofs_to_right = cmdedit_x - cmdedit_termw;
     343    if (!ENABLE_UNICODE_WIDE_WCHARS || ofs_to_right <= 0) {
     344        /* c fits on this line */
     345        BB_PUTCHAR(c);
     346    }
     347
     348    if (ofs_to_right >= 0) {
     349        /* we go to the next line */
     350#if HACK_FOR_WRONG_WIDTH
     351        /* This works better if our idea of term width is wrong
     352         * and it is actually wider (often happens on serial lines).
     353         * Printing CR,LF *forces* cursor to next line.
     354         * OTOH if terminal width is correct AND terminal does NOT
     355         * have automargin (IOW: it is moving cursor to next line
     356         * by itself (which is wrong for VT-10x terminals)),
     357         * this will break things: there will be one extra empty line */
     358        puts("\r"); /* + implicit '\n' */
     359#else
     360        /* VT-10x terminals don't wrap cursor to next line when last char
     361         * on the line is printed - cursor stays "over" this char.
     362         * Need to print _next_ char too (first one to appear on next line)
     363         * to make cursor move down to next line.
     364         */
     365        /* Works ok only if cmdedit_termw is correct. */
     366        c = command_ps[cursor];
     367        if (c == BB_NUL)
     368            c = ' ';
     369        BB_PUTCHAR(c);
     370        bb_putchar('\b');
     371#endif
    118372        cmdedit_y++;
    119         cmdedit_x = 0;
    120         /* destroy "(auto)margin" */
    121         putchar(next_char);
    122         putchar('\b');
    123     }
    124 // Huh? What if command_ps[cursor] == '\0' (we are at the end already?)
    125     cursor++;
     373        if (!ENABLE_UNICODE_WIDE_WCHARS || ofs_to_right == 0) {
     374            width = 0;
     375        } else { /* ofs_to_right > 0 */
     376            /* wide char c didn't fit on prev line */
     377            BB_PUTCHAR(c);
     378        }
     379        cmdedit_x = width;
     380    }
    126381}
    127382
    128383/* Move to end of line (by printing all chars till the end) */
    129 static void input_end(void)
     384static void put_till_end_and_adv_cursor(void)
    130385{
    131386    while (cursor < command_len)
    132         cmdedit_set_out_char(' ');
     387        put_cur_glyph_and_inc_cursor();
    133388}
    134389
     
    136391static void goto_new_line(void)
    137392{
    138     input_end();
    139     if (cmdedit_x)
    140         putchar('\n');
    141 }
    142 
    143 
    144 static void out1str(const char *s)
    145 {
    146     if (s)
    147         fputs(s, stdout);
     393    put_till_end_and_adv_cursor();
     394    if (cmdedit_x != 0)
     395        bb_putchar('\n');
    148396}
    149397
    150398static void beep(void)
    151399{
    152     putchar('\007');
     400    bb_putchar('\007');
     401}
     402
     403static void put_prompt(void)
     404{
     405    unsigned w;
     406
     407    fputs(cmdedit_prompt, stdout);
     408    fflush_all();
     409    cursor = 0;
     410    w = cmdedit_termw; /* read volatile var once */
     411    cmdedit_y = cmdedit_prmt_len / w; /* new quasireal y */
     412    cmdedit_x = cmdedit_prmt_len % w;
    153413}
    154414
     
    157417static void input_backward(unsigned num)
    158418{
    159     int count_y;
    160 
    161419    if (num > cursor)
    162420        num = cursor;
    163     if (!num)
     421    if (num == 0)
    164422        return;
    165423    cursor -= num;
     424
     425    if ((ENABLE_UNICODE_COMBINING_WCHARS || ENABLE_UNICODE_WIDE_WCHARS)
     426     && unicode_status == UNICODE_ON
     427    ) {
     428        /* correct NUM to be equal to _screen_ width */
     429        int n = num;
     430        num = 0;
     431        while (--n >= 0)
     432            adjust_width_and_validate_wc(&num, command_ps[cursor + n]);
     433        if (num == 0)
     434            return;
     435    }
    166436
    167437    if (cmdedit_x >= num) {
    168438        cmdedit_x -= num;
    169439        if (num <= 4) {
    170             printf("\b\b\b\b" + (4-num));
     440            /* This is longer by 5 bytes on x86.
     441             * Also gets miscompiled for ARM users
     442             * (busybox.net/bugs/view.php?id=2274).
     443             * printf(("\b\b\b\b" + 4) - num);
     444             * return;
     445             */
     446            do {
     447                bb_putchar('\b');
     448            } while (--num);
    171449            return;
    172450        }
    173         printf("\033[%uD", num);
     451        printf(ESC"[%uD", num);
    174452        return;
    175453    }
    176454
    177455    /* Need to go one or more lines up */
    178     num -= cmdedit_x;
    179     count_y = 1 + (num / cmdedit_termw);
    180     cmdedit_y -= count_y;
    181     cmdedit_x = cmdedit_termw * count_y - num;
    182     /* go to 1st column; go up; go to correct column */
    183     printf("\r" "\033[%dA" "\033[%dC", count_y, cmdedit_x);
    184 }
    185 
    186 static void put_prompt(void)
    187 {
    188     out1str(cmdedit_prompt);
    189     cmdedit_x = cmdedit_prmt_len;
    190     cursor = 0;
    191 // Huh? what if cmdedit_prmt_len >= width?
    192     cmdedit_y = 0;                  /* new quasireal y */
     456    if (ENABLE_UNICODE_WIDE_WCHARS) {
     457        /* With wide chars, it is hard to "backtrack"
     458         * and reliably figure out where to put cursor.
     459         * Example (<> is a wide char; # is an ordinary char, _ cursor):
     460         * |prompt: <><> |
     461         * |<><><><><><> |
     462         * |_            |
     463         * and user presses left arrow. num = 1, cmdedit_x = 0,
     464         * We need to go up one line, and then - how do we know that
     465         * we need to go *10* positions to the right? Because
     466         * |prompt: <>#<>|
     467         * |<><><>#<><><>|
     468         * |_            |
     469         * in this situation we need to go *11* positions to the right.
     470         *
     471         * A simpler thing to do is to redraw everything from the start
     472         * up to new cursor position (which is already known):
     473         */
     474        unsigned sv_cursor;
     475        /* go to 1st column; go up to first line */
     476        printf("\r" ESC"[%uA", cmdedit_y);
     477        cmdedit_y = 0;
     478        sv_cursor = cursor;
     479        put_prompt(); /* sets cursor to 0 */
     480        while (cursor < sv_cursor)
     481            put_cur_glyph_and_inc_cursor();
     482    } else {
     483        int lines_up;
     484        unsigned width;
     485        /* num = chars to go back from the beginning of current line: */
     486        num -= cmdedit_x;
     487        width = cmdedit_termw; /* read volatile var once */
     488        /* num=1...w: one line up, w+1...2w: two, etc: */
     489        lines_up = 1 + (num - 1) / width;
     490        cmdedit_x = (width * cmdedit_y - num) % width;
     491        cmdedit_y -= lines_up;
     492        /* go to 1st column; go up */
     493        printf("\r" ESC"[%uA", lines_up);
     494        /* go to correct column.
     495         * xterm, konsole, Linux VT interpret 0 as 1 below! wow.
     496         * need to *make sure* we skip it if cmdedit_x == 0 */
     497        if (cmdedit_x)
     498            printf(ESC"[%uC", cmdedit_x);
     499    }
    193500}
    194501
     
    196503static void redraw(int y, int back_cursor)
    197504{
    198     if (y > 0)                              /* up to start y */
    199         printf("\033[%dA", y);
    200     putchar('\r');
     505    if (y > 0) /* up y lines */
     506        printf(ESC"[%uA", y);
     507    bb_putchar('\r');
    201508    put_prompt();
    202     input_end();                            /* rewrite */
    203     printf("\033[J");                       /* erase after cursor */
     509    put_till_end_and_adv_cursor();
     510    printf(SEQ_CLEAR_TILL_END_OF_SCREEN);
    204511    input_backward(back_cursor);
    205512}
    206 
    207 #if ENABLE_FEATURE_EDITING_VI
    208 #define DELBUFSIZ 128
    209 static char *delbuf;  /* a (malloced) place to store deleted characters */
    210 static char *delp;
    211 static char newdelflag;      /* whether delbuf should be reused yet */
    212 #endif
    213513
    214514/* Delete the char in front of the cursor, optionally saving it
    215515 * for later putback */
     516#if !ENABLE_FEATURE_EDITING_VI
     517static void input_delete(void)
     518#define input_delete(save) input_delete()
     519#else
    216520static void input_delete(int save)
     521#endif
    217522{
    218523    int j = cursor;
    219524
    220     if (j == command_len)
     525    if (j == (int)command_len)
    221526        return;
    222527
     
    224529    if (save) {
    225530        if (newdelflag) {
    226             if (!delbuf)
    227                 delbuf = malloc(DELBUFSIZ);
    228             /* safe if malloc fails */
    229             delp = delbuf;
     531            delptr = delbuf;
    230532            newdelflag = 0;
    231533        }
    232         if (delbuf && (delp - delbuf < DELBUFSIZ))
    233             *delp++ = command_ps[j];
    234     }
    235 #endif
    236 
    237     strcpy(command_ps + j, command_ps + j + 1);
     534        if ((delptr - delbuf) < DELBUFSIZ)
     535            *delptr++ = command_ps[j];
     536    }
     537#endif
     538
     539    memmove(command_ps + j, command_ps + j + 1,
     540            /* (command_len + 1 [because of NUL]) - (j + 1)
     541             * simplified into (command_len - j) */
     542            (command_len - j) * sizeof(command_ps[0]));
    238543    command_len--;
    239     input_end();                    /* rewrite new line */
    240     cmdedit_set_out_char(' ');      /* erase char */
     544    put_till_end_and_adv_cursor();
     545    /* Last char is still visible, erase it (and more) */
     546    printf(SEQ_CLEAR_TILL_END_OF_SCREEN);
    241547    input_backward(cursor - j);     /* back to old pos cursor */
    242548}
     
    246552{
    247553    int ocursor;
    248     int j = delp - delbuf;
     554    int j = delptr - delbuf;
    249555
    250556    if (j == 0)
     
    252558    ocursor = cursor;
    253559    /* open hole and then fill it */
    254     memmove(command_ps + cursor + j, command_ps + cursor, command_len - cursor + 1);
    255     strncpy(command_ps + cursor, delbuf, j);
     560    memmove(command_ps + cursor + j, command_ps + cursor,
     561            (command_len - cursor + 1) * sizeof(command_ps[0]));
     562    memcpy(command_ps + cursor, delbuf, j * sizeof(command_ps[0]));
    256563    command_len += j;
    257     input_end();                    /* rewrite new line */
     564    put_till_end_and_adv_cursor();
    258565    input_backward(cursor - ocursor - j + 1); /* at end of new text */
    259566}
     
    273580{
    274581    if (cursor < command_len)
    275         cmdedit_set_out_char(command_ps[cursor + 1]);
    276 }
    277 
     582        put_cur_glyph_and_inc_cursor();
     583}
    278584
    279585#if ENABLE_FEATURE_TAB_COMPLETION
    280586
    281 static char **matches;
    282 static unsigned num_matches;
     587//FIXME:
     588//needs to be more clever: currently it thinks that "foo\ b<TAB>
     589//matches the file named "foo bar", which is untrue.
     590//Also, perhaps "foo b<TAB> needs to complete to "foo bar" <cursor>,
     591//not "foo bar <cursor>...
    283592
    284593static void free_tab_completion_data(void)
     
    294603static void add_match(char *matched)
    295604{
    296     int nm = num_matches;
    297     int nm1 = nm + 1;
    298 
    299     matches = xrealloc(matches, nm1 * sizeof(char *));
    300     matches[nm] = matched;
     605    matches = xrealloc_vector(matches, 4, num_matches);
     606    matches[num_matches] = matched;
    301607    num_matches++;
    302608}
    303609
    304 #if ENABLE_FEATURE_USERNAME_COMPLETION
    305 static void username_tab_completion(char *ud, char *with_shash_flg)
     610# if ENABLE_FEATURE_USERNAME_COMPLETION
     611/* Replace "~user/..." with "/homedir/...".
     612 * The parameter is malloced, free it or return it
     613 * unchanged if no user is matched.
     614 */
     615static char *username_path_completion(char *ud)
    306616{
    307617    struct passwd *entry;
    308     int userlen;
    309 
    310     ud++;                           /* ~user/... to user/... */
     618    char *tilde_name = ud;
     619    char *home = NULL;
     620
     621    ud++; /* skip ~ */
     622    if (*ud == '/') {       /* "~/..." */
     623        home = home_pwd_buf;
     624    } else {
     625        /* "~user/..." */
     626        ud = strchr(ud, '/');
     627        *ud = '\0';           /* "~user" */
     628        entry = getpwnam(tilde_name + 1);
     629        *ud = '/';            /* restore "~user/..." */
     630        if (entry)
     631            home = entry->pw_dir;
     632    }
     633    if (home) {
     634        ud = concat_path_file(home, ud);
     635        free(tilde_name);
     636        tilde_name = ud;
     637    }
     638    return tilde_name;
     639}
     640
     641/* ~use<tab> - find all users with this prefix.
     642 * Return the length of the prefix used for matching.
     643 */
     644static NOINLINE unsigned complete_username(const char *ud)
     645{
     646    /* Using _r function to avoid pulling in static buffers */
     647    char line_buff[256];
     648    struct passwd pwd;
     649    struct passwd *result;
     650    unsigned userlen;
     651
     652    ud++; /* skip ~ */
    311653    userlen = strlen(ud);
    312654
    313     if (with_shash_flg) {           /* "~/..." or "~user/..." */
    314         char *sav_ud = ud - 1;
    315         char *home = NULL;
    316         char *temp;
    317 
    318         if (*ud == '/') {       /* "~/..."     */
    319             home = home_pwd_buf;
    320         } else {
    321             /* "~user/..." */
    322             temp = strchr(ud, '/');
    323             *temp = 0;              /* ~user\0 */
    324             entry = getpwnam(ud);
    325             *temp = '/';            /* restore ~user/... */
    326             ud = temp;
    327             if (entry)
    328                 home = entry->pw_dir;
    329         }
    330         if (home) {
    331             if ((userlen + strlen(home) + 1) < MAX_LINELEN) {
    332                 char temp2[MAX_LINELEN];     /* argument size */
    333 
    334                 /* /home/user/... */
    335                 sprintf(temp2, "%s%s", home, ud);
    336                 strcpy(sav_ud, temp2);
    337             }
    338         }
    339     } else {
    340         /* "~[^/]*" */
    341         /* Using _r function to avoid pulling in static buffers */
    342         char line_buff[256];
    343         struct passwd pwd;
    344         struct passwd *result;
    345 
    346         setpwent();
    347         while (!getpwent_r(&pwd, line_buff, sizeof(line_buff), &result)) {
    348             /* Null usernames should result in all users as possible completions. */
    349             if (/*!userlen || */ strncmp(ud, pwd.pw_name, userlen) == 0) {
    350                 add_match(xasprintf("~%s/", pwd.pw_name));
    351             }
    352         }
    353         endpwent();
    354     }
    355 }
    356 #endif  /* FEATURE_COMMAND_USERNAME_COMPLETION */
     655    setpwent();
     656    while (!getpwent_r(&pwd, line_buff, sizeof(line_buff), &result)) {
     657        /* Null usernames should result in all users as possible completions. */
     658        if (/*!userlen || */ strncmp(ud, pwd.pw_name, userlen) == 0) {
     659            add_match(xasprintf("~%s/", pwd.pw_name));
     660        }
     661    }
     662    endpwent();
     663
     664    return 1 + userlen;
     665}
     666# endif  /* FEATURE_USERNAME_COMPLETION */
    357667
    358668enum {
     
    362672};
    363673
    364 static int path_parse(char ***p, int flags)
     674static int path_parse(char ***p)
    365675{
    366676    int npth;
     
    369679    char **res;
    370680
    371     /* if not setenv PATH variable, to search cur dir "." */
    372     if (flags != FIND_EXE_ONLY)
    373         return 1;
    374 
    375681    if (state->flags & WITH_PATH_LOOKUP)
    376682        pth = state->path_lookup;
    377683    else
    378684        pth = getenv("PATH");
    379     /* PATH=<empty> or PATH=:<empty> */
     685
     686    /* PATH="" or PATH=":"? */
    380687    if (!pth || !pth[0] || LONE_CHAR(pth, ':'))
    381688        return 1;
     
    387694        if (!tmp)
    388695            break;
    389         if (*++tmp == '\0')
     696        tmp++;
     697        if (*tmp == '\0')
    390698            break;  /* :<empty> */
    391699        npth++;
    392700    }
    393701
    394     res = xmalloc(npth * sizeof(char*));
     702    *p = res = xmalloc(npth * sizeof(res[0]));
    395703    res[0] = tmp = xstrdup(pth);
    396704    npth = 1;
     
    404712        res[npth++] = tmp;
    405713    }
    406     *p = res;
    407714    return npth;
    408715}
    409716
    410 static void exe_n_cwd_tab_completion(char *command, int type)
    411 {
    412     DIR *dir;
    413     struct dirent *next;
    414     char dirbuf[MAX_LINELEN];
    415     struct stat st;
     717/* Complete command, directory or file name.
     718 * Return the length of the prefix used for matching.
     719 */
     720static NOINLINE unsigned complete_cmd_dir_file(const char *command, int type)
     721{
    416722    char *path1[1];
    417723    char **paths = path1;
    418724    int npaths;
    419725    int i;
    420     char *found;
    421     char *pfind = strrchr(command, '/');
     726    unsigned pf_len;
     727    const char *pfind;
     728    char *dirbuf = NULL;
    422729
    423730    npaths = 1;
    424731    path1[0] = (char*)".";
    425732
    426     if (pfind == NULL) {
    427         /* no dir, if flags==EXE_ONLY - get paths, else "." */
    428         npaths = path_parse(&paths, type);
     733    pfind = strrchr(command, '/');
     734    if (!pfind) {
     735        if (type == FIND_EXE_ONLY)
     736            npaths = path_parse(&paths);
    429737        pfind = command;
    430738    } else {
    431         /* dirbuf = ".../.../.../" */
    432         safe_strncpy(dirbuf, command, (pfind - command) + 2);
    433 #if ENABLE_FEATURE_USERNAME_COMPLETION
    434         if (dirbuf[0] == '~')   /* ~/... or ~user/... */
    435             username_tab_completion(dirbuf, dirbuf);
    436 #endif
    437         paths[0] = dirbuf;
    438739        /* point to 'l' in "..../last_component" */
    439740        pfind++;
    440     }
     741        /* dirbuf = ".../.../.../" */
     742        dirbuf = xstrndup(command, pfind - command);
     743# if ENABLE_FEATURE_USERNAME_COMPLETION
     744        if (dirbuf[0] == '~')   /* ~/... or ~user/... */
     745            dirbuf = username_path_completion(dirbuf);
     746# endif
     747        path1[0] = dirbuf;
     748    }
     749    pf_len = strlen(pfind);
    441750
    442751    for (i = 0; i < npaths; i++) {
     752        DIR *dir;
     753        struct dirent *next;
     754        struct stat st;
     755        char *found;
     756
    443757        dir = opendir(paths[i]);
    444         if (!dir)                       /* Don't print an error */
    445             continue;
     758        if (!dir)
     759            continue; /* don't print an error */
    446760
    447761        while ((next = readdir(dir)) != NULL) {
    448             int len1;
    449             const char *str_found = next->d_name;
    450 
    451             /* matched? */
    452             if (strncmp(str_found, pfind, strlen(pfind)))
     762            unsigned len;
     763            const char *name_found = next->d_name;
     764
     765            /* .../<tab>: bash 3.2.0 shows dotfiles, but not . and .. */
     766            if (!pfind[0] && DOT_OR_DOTDOT(name_found))
    453767                continue;
    454             /* not see .name without .match */
    455             if (*str_found == '.' && *pfind == 0) {
    456                 if (NOT_LONE_CHAR(paths[i], '/') || str_found[1])
    457                     continue;
    458                 str_found = ""; /* only "/" */
    459             }
    460             found = concat_path_file(paths[i], str_found);
    461             /* hmm, remover in progress? */
    462             if (stat(found, &st) < 0)
    463                 goto cont;
    464             /* find with dirs? */
    465             if (paths[i] != dirbuf)
    466                 strcpy(found, next->d_name);    /* only name */
    467 
    468             len1 = strlen(found);
    469             found = xrealloc(found, len1 + 2);
    470             found[len1] = '\0';
    471             found[len1+1] = '\0';
     768            /* match? */
     769            if (strncmp(name_found, pfind, pf_len) != 0)
     770                continue; /* no */
     771
     772            found = concat_path_file(paths[i], name_found);
     773            /* NB: stat() first so that we see is it a directory;
     774             * but if that fails, use lstat() so that
     775             * we still match dangling links */
     776            if (stat(found, &st) && lstat(found, &st))
     777                goto cont; /* hmm, remove in progress? */
     778
     779            /* Save only name */
     780            len = strlen(name_found);
     781            found = xrealloc(found, len + 2); /* +2: for slash and NUL */
     782            strcpy(found, name_found);
    472783
    473784            if (S_ISDIR(st.st_mode)) {
    474                 /* name is directory      */
    475                 if (found[len1-1] != '/') {
    476                     found[len1] = '/';
    477                 }
     785                /* name is a directory, add slash */
     786                found[len] = '/';
     787                found[len + 1] = '\0';
    478788            } else {
    479                 /* not put found file if search only dirs for cd */
     789                /* skip files if looking for dirs only (example: cd) */
    480790                if (type == FIND_DIR_ONLY)
    481791                    goto cont;
    482792            }
    483             /* Add it to the list */
     793            /* add it to the list */
    484794            add_match(found);
    485795            continue;
     
    488798        }
    489799        closedir(dir);
    490     }
     800    } /* for every path */
     801
    491802    if (paths != path1) {
    492         free(paths[0]);                 /* allocated memory only in first member */
     803        free(paths[0]); /* allocated memory is only in first member */
    493804        free(paths);
    494805    }
    495 }
    496 
     806    free(dirbuf);
     807
     808    return pf_len;
     809}
     810
     811/* build_match_prefix:
     812 * On entry, match_buf contains everything up to cursor at the moment <tab>
     813 * was pressed. This function looks at it, figures out what part of it
     814 * constitutes the command/file/directory prefix to use for completion,
     815 * and rewrites match_buf to contain only that part.
     816 */
     817#define dbg_bmp 0
     818/* Helpers: */
     819/* QUOT is used on elements of int_buf[], which are bytes,
     820 * not Unicode chars. Therefore it works correctly even in Unicode mode.
     821 */
    497822#define QUOT (UCHAR_MAX+1)
    498 
    499 #define collapse_pos(is, in) { \
    500     memmove(int_buf+(is), int_buf+(in), (MAX_LINELEN+1-(is)-(in))*sizeof(int)); \
    501     memmove(pos_buf+(is), pos_buf+(in), (MAX_LINELEN+1-(is)-(in))*sizeof(int)); }
    502 
    503 static int find_match(char *matchBuf, int *len_with_quotes)
     823static void remove_chunk(int16_t *int_buf, int beg, int end)
     824{
     825    /* beg must be <= end */
     826    if (beg == end)
     827        return;
     828
     829    while ((int_buf[beg] = int_buf[end]) != 0)
     830        beg++, end++;
     831
     832    if (dbg_bmp) {
     833        int i;
     834        for (i = 0; int_buf[i]; i++)
     835            bb_putchar((unsigned char)int_buf[i]);
     836        bb_putchar('\n');
     837    }
     838}
     839/* Caller ensures that match_buf points to a malloced buffer
     840 * big enough to hold strlen(match_buf)*2 + 2
     841 */
     842static NOINLINE int build_match_prefix(char *match_buf)
    504843{
    505844    int i, j;
    506845    int command_mode;
    507     int c, c2;
    508     int int_buf[MAX_LINELEN + 1];
    509     int pos_buf[MAX_LINELEN + 1];
    510 
    511     /* set to integer dimension characters and own positions */
    512     for (i = 0;; i++) {
    513         int_buf[i] = (unsigned char)matchBuf[i];
    514         if (int_buf[i] == 0) {
    515             pos_buf[i] = -1;        /* indicator end line */
    516             break;
    517         }
    518         pos_buf[i] = i;
    519     }
    520 
    521     /* mask \+symbol and convert '\t' to ' ' */
    522     for (i = j = 0; matchBuf[i]; i++, j++)
    523         if (matchBuf[i] == '\\') {
    524             collapse_pos(j, j + 1);
    525             int_buf[j] |= QUOT;
     846    int16_t *int_buf = (int16_t*)match_buf;
     847
     848    if (dbg_bmp) printf("\n%s\n", match_buf);
     849
     850    /* Copy in reverse order, since they overlap */
     851    i = strlen(match_buf);
     852    do {
     853        int_buf[i] = (unsigned char)match_buf[i];
     854        i--;
     855    } while (i >= 0);
     856
     857    /* Mark every \c as "quoted c" */
     858    for (i = 0; int_buf[i]; i++) {
     859        if (int_buf[i] == '\\') {
     860            remove_chunk(int_buf, i, i + 1);
     861            int_buf[i] |= QUOT;
     862        }
     863    }
     864    /* Quote-mark "chars" and 'chars', drop delimiters */
     865    {
     866        int in_quote = 0;
     867        i = 0;
     868        while (int_buf[i]) {
     869            int cur = int_buf[i];
     870            if (!cur)
     871                break;
     872            if (cur == '\'' || cur == '"') {
     873                if (!in_quote || (cur == in_quote)) {
     874                    in_quote ^= cur;
     875                    remove_chunk(int_buf, i, i + 1);
     876                    continue;
     877                }
     878            }
     879            if (in_quote)
     880                int_buf[i] = cur | QUOT;
    526881            i++;
    527 #if ENABLE_FEATURE_NONPRINTABLE_INVERSE_PUT
    528             if (matchBuf[i] == '\t')        /* algorithm equivalent */
    529                 int_buf[j] = ' ' | QUOT;
    530 #endif
    531         }
    532 #if ENABLE_FEATURE_NONPRINTABLE_INVERSE_PUT
    533         else if (matchBuf[i] == '\t')
    534             int_buf[j] = ' ';
    535 #endif
    536 
    537     /* mask "symbols" or 'symbols' */
    538     c2 = 0;
     882        }
     883    }
     884
     885    /* Remove everything up to command delimiters:
     886     * ';' ';;' '&' '|' '&&' '||',
     887     * but careful with '>&' '<&' '>|'
     888     */
    539889    for (i = 0; int_buf[i]; i++) {
    540         c = int_buf[i];
    541         if (c == '\'' || c == '"') {
    542             if (c2 == 0)
    543                 c2 = c;
    544             else {
    545                 if (c == c2)
    546                     c2 = 0;
    547                 else
    548                     int_buf[i] |= QUOT;
     890        int cur = int_buf[i];
     891        if (cur == ';' || cur == '&' || cur == '|') {
     892            int prev = i ? int_buf[i - 1] : 0;
     893            if (cur == '&' && (prev == '>' || prev == '<')) {
     894                continue;
     895            } else if (cur == '|' && prev == '>') {
     896                continue;
    549897            }
    550         } else if (c2 != 0 && c != '$')
    551             int_buf[i] |= QUOT;
    552     }
    553 
    554     /* skip commands with arguments if line has commands delimiters */
    555     /* ';' ';;' '&' '|' '&&' '||' but `>&' `<&' `>|' */
     898            remove_chunk(int_buf, 0, i + 1 + (cur == int_buf[i + 1]));
     899            i = -1;  /* back to square 1 */
     900        }
     901    }
     902    /* Remove all `cmd` */
    556903    for (i = 0; int_buf[i]; i++) {
    557         c = int_buf[i];
    558         c2 = int_buf[i + 1];
    559         j = i ? int_buf[i - 1] : -1;
    560         command_mode = 0;
    561         if (c == ';' || c == '&' || c == '|') {
    562             command_mode = 1 + (c == c2);
    563             if (c == '&') {
    564                 if (j == '>' || j == '<')
    565                     command_mode = 0;
    566             } else if (c == '|' && j == '>')
    567                 command_mode = 0;
    568         }
    569         if (command_mode) {
    570             collapse_pos(0, i + command_mode);
    571             i = -1;                         /* hack incremet */
    572         }
    573     }
    574     /* collapse `command...` */
    575     for (i = 0; int_buf[i]; i++)
    576904        if (int_buf[i] == '`') {
    577             for (j = i + 1; int_buf[j]; j++)
     905            for (j = i + 1; int_buf[j]; j++) {
    578906                if (int_buf[j] == '`') {
    579                     collapse_pos(i, j + 1);
    580                     j = 0;
    581                     break;
     907                    /* `cmd` should count as a word:
     908                     * `cmd` c<tab> should search for files c*,
     909                     * not commands c*. Therefore we don't drop
     910                     * `cmd` entirely, we replace it with single `.
     911                     */
     912                    remove_chunk(int_buf, i, j);
     913                    goto next;
    582914                }
    583             if (j) {
    584                 /* not found close ` - command mode, collapse all previous */
    585                 collapse_pos(0, i + 1);
    586                 break;
    587             } else
    588                 i--;                    /* hack incremet */
    589         }
    590 
    591     /* collapse (command...(command...)...) or {command...{command...}...} */
    592     c = 0;                                          /* "recursive" level */
    593     c2 = 0;
    594     for (i = 0; int_buf[i]; i++)
     915            }
     916            /* No closing ` - command mode, remove all up to ` */
     917            remove_chunk(int_buf, 0, i + 1);
     918            break;
     919 next: ;
     920        }
     921    }
     922
     923    /* Remove "cmd (" and "cmd {"
     924     * Example: "if { c<tab>"
     925     * In this example, c should be matched as command pfx.
     926     */
     927    for (i = 0; int_buf[i]; i++) {
    595928        if (int_buf[i] == '(' || int_buf[i] == '{') {
    596             if (int_buf[i] == '(')
    597                 c++;
    598             else
    599                 c2++;
    600             collapse_pos(0, i + 1);
    601             i = -1;                         /* hack incremet */
    602         }
    603     for (i = 0; pos_buf[i] >= 0 && (c > 0 || c2 > 0); i++)
    604         if ((int_buf[i] == ')' && c > 0) || (int_buf[i] == '}' && c2 > 0)) {
    605             if (int_buf[i] == ')')
    606                 c--;
    607             else
    608                 c2--;
    609             collapse_pos(0, i + 1);
    610             i = -1;                         /* hack incremet */
    611         }
    612 
    613     /* skip first not quote space */
     929            remove_chunk(int_buf, 0, i + 1);
     930            i = -1;  /* back to square 1 */
     931        }
     932    }
     933
     934    /* Remove leading unquoted spaces */
    614935    for (i = 0; int_buf[i]; i++)
    615936        if (int_buf[i] != ' ')
    616937            break;
    617     if (i)
    618         collapse_pos(0, i);
    619 
    620     /* set find mode for completion */
     938    remove_chunk(int_buf, 0, i);
     939
     940    /* Determine completion mode */
    621941    command_mode = FIND_EXE_ONLY;
    622     for (i = 0; int_buf[i]; i++)
     942    for (i = 0; int_buf[i]; i++) {
    623943        if (int_buf[i] == ' ' || int_buf[i] == '<' || int_buf[i] == '>') {
    624             if (int_buf[i] == ' ' && command_mode == FIND_EXE_ONLY
    625              && matchBuf[pos_buf[0]]=='c'
    626              && matchBuf[pos_buf[1]]=='d'
     944            if (int_buf[i] == ' '
     945             && command_mode == FIND_EXE_ONLY
     946             && (char)int_buf[0] == 'c'
     947             && (char)int_buf[1] == 'd'
     948             && i == 2 /* -> int_buf[2] == ' ' */
    627949            ) {
    628950                command_mode = FIND_DIR_ONLY;
     
    632954            }
    633955        }
    634     for (i = 0; int_buf[i]; i++)
    635         /* "strlen" */;
    636     /* find last word */
     956    }
     957    if (dbg_bmp) printf("command_mode(0:exe/1:dir/2:file):%d\n", command_mode);
     958
     959    /* Remove everything except last word */
     960    for (i = 0; int_buf[i]; i++) /* quasi-strlen(int_buf) */
     961        continue;
    637962    for (--i; i >= 0; i--) {
    638         c = int_buf[i];
    639         if (c == ' ' || c == '<' || c == '>' || c == '|' || c == '&') {
    640             collapse_pos(0, i + 1);
    641             break;
    642         }
    643     }
    644     /* skip first not quoted '\'' or '"' */
    645     for (i = 0; int_buf[i] == '\'' || int_buf[i] == '"'; i++)
    646         /*skip*/;
    647     /* collapse quote or unquote // or /~ */
    648     while ((int_buf[i] & ~QUOT) == '/'
    649      && ((int_buf[i+1] & ~QUOT) == '/' || (int_buf[i+1] & ~QUOT) == '~')
    650     ) {
     963        int cur = int_buf[i];
     964        if (cur == ' ' || cur == '<' || cur == '>' || cur == '|' || cur == '&') {
     965            remove_chunk(int_buf, 0, i + 1);
     966            break;
     967        }
     968    }
     969
     970    /* Convert back to string of _chars_ */
     971    i = 0;
     972    while ((match_buf[i] = int_buf[i]) != '\0')
    651973        i++;
    652     }
    653 
    654     /* set only match and destroy quotes */
    655     j = 0;
    656     for (c = 0; pos_buf[i] >= 0; i++) {
    657         matchBuf[c++] = matchBuf[pos_buf[i]];
    658         j = pos_buf[i] + 1;
    659     }
    660     matchBuf[c] = 0;
    661     /* old lenght matchBuf with quotes symbols */
    662     *len_with_quotes = j ? j - pos_buf[0] : 0;
     974
     975    if (dbg_bmp) printf("final match_buf:'%s'\n", match_buf);
    663976
    664977    return command_mode;
     
    666979
    667980/*
    668  * display by column (original idea from ls applet,
    669  * very optimized by me :)
     981 * Display by column (original idea from ls applet,
     982 * very optimized by me [Vladimir] :)
    670983 */
    671984static void showfiles(void)
     
    677990    int l;
    678991
    679     /* find the longest file name- use that as the column width */
     992    /* find the longest file name - use that as the column width */
    680993    for (row = 0; row < nrows; row++) {
    681         l = strlen(matches[row]);
     994        l = unicode_strwidth(matches[row]);
    682995        if (column_width < l)
    683996            column_width = l;
     
    6991012        for (nc = 1; nc < ncols && n+nrows < nfiles; n += nrows, nc++) {
    7001013            printf("%s%-*s", matches[n],
    701                 (int)(column_width - strlen(matches[n])), "");
    702         }
    703         printf("%s\n", matches[n]);
    704     }
    705 }
    706 
    707 static char *add_quote_for_spec_chars(char *found)
     1014                (int)(column_width - unicode_strwidth(matches[n])), ""
     1015            );
     1016        }
     1017        if (ENABLE_UNICODE_SUPPORT)
     1018            puts(printable_string(NULL, matches[n]));
     1019        else
     1020            puts(matches[n]);
     1021    }
     1022}
     1023
     1024static const char *is_special_char(char c)
     1025{
     1026    return strchr(" `\"#$%^&*()=+{}[]:;'|\\<>", c);
     1027}
     1028
     1029static char *quote_special_chars(char *found)
    7081030{
    7091031    int l = 0;
    710     char *s = xmalloc((strlen(found) + 1) * 2);
     1032    char *s = xzalloc((strlen(found) + 1) * 2);
    7111033
    7121034    while (*found) {
    713         if (strchr(" `\"#$%^&*()=+{}[]:;\'|\\<>", *found))
     1035        if (is_special_char(*found))
    7141036            s[l++] = '\\';
    7151037        s[l++] = *found++;
    7161038    }
    717     s[l] = 0;
     1039    /* s[l] = '\0'; - already is */
    7181040    return s;
    7191041}
    7201042
    721 static int match_compare(const void *a, const void *b)
    722 {
    723     return strcmp(*(char**)a, *(char**)b);
    724 }
    725 
    7261043/* Do TAB completion */
    727 static void input_tab(int *lastWasTab)
    728 {
     1044static NOINLINE void input_tab(smallint *lastWasTab)
     1045{
     1046    char *chosen_match;
     1047    char *match_buf;
     1048    size_t len_found;
     1049    /* Length of string used for matching */
     1050    unsigned match_pfx_len = match_pfx_len;
     1051    int find_type;
     1052# if ENABLE_UNICODE_SUPPORT
     1053    /* cursor pos in command converted to multibyte form */
     1054    int cursor_mb;
     1055# endif
    7291056    if (!(state->flags & TAB_COMPLETION))
    7301057        return;
    7311058
    732     if (!*lastWasTab) {
    733         char *tmp, *tmp1;
    734         int len_found;
    735         char matchBuf[MAX_LINELEN];
    736         int find_type;
    737         int recalc_pos;
    738 
    739         *lastWasTab = TRUE;             /* flop trigger */
    740 
    741         /* Make a local copy of the string -- up
    742          * to the position of the cursor */
    743         tmp = strncpy(matchBuf, command_ps, cursor);
    744         tmp[cursor] = '\0';
    745 
    746         find_type = find_match(matchBuf, &recalc_pos);
    747 
    748         /* Free up any memory already allocated */
    749         free_tab_completion_data();
    750 
    751 #if ENABLE_FEATURE_USERNAME_COMPLETION
    752         /* If the word starts with `~' and there is no slash in the word,
    753          * then try completing this word as a username. */
    754         if (state->flags & USERNAME_COMPLETION)
    755             if (matchBuf[0] == '~' && strchr(matchBuf, '/') == 0)
    756                 username_tab_completion(matchBuf, NULL);
    757 #endif
    758         /* Try to match any executable in our path and everything
    759          * in the current working directory */
    760         if (!matches)
    761             exe_n_cwd_tab_completion(matchBuf, find_type);
    762         /* Sort, then remove any duplicates found */
    763         if (matches) {
    764             int i, n = 0;
    765             qsort(matches, num_matches, sizeof(char*), match_compare);
    766             for (i = 0; i < num_matches - 1; ++i) {
    767                 if (matches[i] && matches[i+1]) { /* paranoia */
    768                     if (strcmp(matches[i], matches[i+1]) == 0) {
    769                         free(matches[i]);
    770                         matches[i] = NULL; /* paranoia */
    771                     } else {
    772                         matches[n++] = matches[i];
    773                     }
    774                 }
    775             }
    776             matches[n] = matches[i];
    777             num_matches = n + 1;
    778         }
    779         /* Did we find exactly one match? */
    780         if (!matches || num_matches > 1) {
    781             beep();
    782             if (!matches)
    783                 return;         /* not found */
    784             /* find minimal match */
    785         // ash: yet another failure in trying to achieve "we don't die on OOM"
    786             tmp1 = xstrdup(matches[0]);
    787             for (tmp = tmp1; *tmp; tmp++)
    788                 for (len_found = 1; len_found < num_matches; len_found++)
    789                     if (matches[len_found][(tmp - tmp1)] != *tmp) {
    790                         *tmp = '\0';
    791                         break;
    792                     }
    793             if (*tmp1 == '\0') {        /* have unique */
    794                 free(tmp1);
    795                 return;
    796             }
    797             tmp = add_quote_for_spec_chars(tmp1);
    798             free(tmp1);
    799         } else {                        /* one match */
    800             tmp = add_quote_for_spec_chars(matches[0]);
    801             /* for next completion current found */
    802             *lastWasTab = FALSE;
    803 
    804             len_found = strlen(tmp);
    805             if (tmp[len_found-1] != '/') {
    806                 tmp[len_found] = ' ';
    807                 tmp[len_found+1] = '\0';
    808             }
    809         }
    810         len_found = strlen(tmp);
    811         /* have space to placed match? */
    812         if ((len_found - strlen(matchBuf) + command_len) < MAX_LINELEN) {
    813             /* before word for match   */
    814             command_ps[cursor - recalc_pos] = 0;
    815             /* save   tail line        */
    816             strcpy(matchBuf, command_ps + cursor);
    817             /* add    match            */
    818             strcat(command_ps, tmp);
    819             /* add    tail             */
    820             strcat(command_ps, matchBuf);
    821             /* back to begin word for match    */
    822             input_backward(recalc_pos);
    823             /* new pos                         */
    824             recalc_pos = cursor + len_found;
    825             /* new len                         */
    826             command_len = strlen(command_ps);
    827             /* write out the matched command   */
    828             redraw(cmdedit_y, command_len - recalc_pos);
    829         }
    830         free(tmp);
    831     } else {
    832         /* Ok -- the last char was a TAB.  Since they
    833          * just hit TAB again, print a list of all the
    834          * available choices... */
    835         if (matches && num_matches > 0) {
    836             int sav_cursor = cursor;        /* change goto_new_line() */
    837 
    838             /* Go to the next line */
     1059    if (*lastWasTab) {
     1060        /* The last char was a TAB too.
     1061         * Print a list of all the available choices.
     1062         */
     1063        if (num_matches > 0) {
     1064            /* cursor will be changed by goto_new_line() */
     1065            int sav_cursor = cursor;
    8391066            goto_new_line();
    8401067            showfiles();
    8411068            redraw(0, command_len - sav_cursor);
    8421069        }
    843     }
    844 }
    845 
    846 #else
    847 #define input_tab(a) ((void)0)
    848 #endif  /* FEATURE_COMMAND_TAB_COMPLETION */
     1070        return;
     1071    }
     1072
     1073    *lastWasTab = 1;
     1074    chosen_match = NULL;
     1075
     1076    /* Make a local copy of the string up to the position of the cursor.
     1077     * build_match_prefix will expand it into int16_t's, need to allocate
     1078     * twice as much as the string_len+1.
     1079     * (we then also (ab)use this extra space later - see (**))
     1080     */
     1081    match_buf = xmalloc(MAX_LINELEN * sizeof(int16_t));
     1082# if !ENABLE_UNICODE_SUPPORT
     1083    save_string(match_buf, cursor + 1); /* +1 for NUL */
     1084# else
     1085    {
     1086        CHAR_T wc = command_ps[cursor];
     1087        command_ps[cursor] = BB_NUL;
     1088        save_string(match_buf, MAX_LINELEN);
     1089        command_ps[cursor] = wc;
     1090        cursor_mb = strlen(match_buf);
     1091    }
     1092# endif
     1093    find_type = build_match_prefix(match_buf);
     1094
     1095    /* Free up any memory already allocated */
     1096    free_tab_completion_data();
     1097
     1098# if ENABLE_FEATURE_USERNAME_COMPLETION
     1099    /* If the word starts with ~ and there is no slash in the word,
     1100     * then try completing this word as a username. */
     1101    if (state->flags & USERNAME_COMPLETION)
     1102        if (match_buf[0] == '~' && strchr(match_buf, '/') == NULL)
     1103            match_pfx_len = complete_username(match_buf);
     1104# endif
     1105    /* If complete_username() did not match,
     1106     * try to match a command in $PATH, or a directory, or a file */
     1107    if (!matches)
     1108        match_pfx_len = complete_cmd_dir_file(match_buf, find_type);
     1109
     1110    /* Account for backslashes which will be inserted
     1111     * by quote_special_chars() later */
     1112    {
     1113        const char *e = match_buf + strlen(match_buf);
     1114        const char *s = e - match_pfx_len;
     1115        while (s < e)
     1116            if (is_special_char(*s++))
     1117                match_pfx_len++;
     1118    }
     1119
     1120    /* Remove duplicates */
     1121    if (matches) {
     1122        unsigned i, n = 0;
     1123        qsort_string_vector(matches, num_matches);
     1124        for (i = 0; i < num_matches - 1; ++i) {
     1125            //if (matches[i] && matches[i+1]) { /* paranoia */
     1126                if (strcmp(matches[i], matches[i+1]) == 0) {
     1127                    free(matches[i]);
     1128                    //matches[i] = NULL; /* paranoia */
     1129                } else {
     1130                    matches[n++] = matches[i];
     1131                }
     1132            //}
     1133        }
     1134        matches[n++] = matches[i];
     1135        num_matches = n;
     1136    }
     1137
     1138    /* Did we find exactly one match? */
     1139    if (num_matches != 1) { /* no */
     1140        char *cp;
     1141        beep();
     1142        if (!matches)
     1143            goto ret; /* no matches at all */
     1144        /* Find common prefix */
     1145        chosen_match = xstrdup(matches[0]);
     1146        for (cp = chosen_match; *cp; cp++) {
     1147            unsigned n;
     1148            for (n = 1; n < num_matches; n++) {
     1149                if (matches[n][cp - chosen_match] != *cp) {
     1150                    goto stop;
     1151                }
     1152            }
     1153        }
     1154 stop:
     1155        if (cp == chosen_match) { /* have unique prefix? */
     1156            goto ret; /* no */
     1157        }
     1158        *cp = '\0';
     1159        cp = quote_special_chars(chosen_match);
     1160        free(chosen_match);
     1161        chosen_match = cp;
     1162        len_found = strlen(chosen_match);
     1163    } else {                        /* exactly one match */
     1164        /* Next <tab> is not a double-tab */
     1165        *lastWasTab = 0;
     1166
     1167        chosen_match = quote_special_chars(matches[0]);
     1168        len_found = strlen(chosen_match);
     1169        if (chosen_match[len_found-1] != '/') {
     1170            chosen_match[len_found] = ' ';
     1171            chosen_match[++len_found] = '\0';
     1172        }
     1173    }
     1174
     1175# if !ENABLE_UNICODE_SUPPORT
     1176    /* Have space to place the match? */
     1177    /* The result consists of three parts with these lengths: */
     1178    /* cursor + (len_found - match_pfx_len) + (command_len - cursor) */
     1179    /* it simplifies into: */
     1180    if ((int)(len_found - match_pfx_len + command_len) < S.maxsize) {
     1181        int pos;
     1182        /* save tail */
     1183        strcpy(match_buf, &command_ps[cursor]);
     1184        /* add match and tail */
     1185        sprintf(&command_ps[cursor], "%s%s", chosen_match + match_pfx_len, match_buf);
     1186        command_len = strlen(command_ps);
     1187        /* new pos */
     1188        pos = cursor + len_found - match_pfx_len;
     1189        /* write out the matched command */
     1190        redraw(cmdedit_y, command_len - pos);
     1191    }
     1192# else
     1193    {
     1194        /* Use 2nd half of match_buf as scratch space - see (**) */
     1195        char *command = match_buf + MAX_LINELEN;
     1196        int len = save_string(command, MAX_LINELEN);
     1197        /* Have space to place the match? */
     1198        /* cursor_mb + (len_found - match_pfx_len) + (len - cursor_mb) */
     1199        if ((int)(len_found - match_pfx_len + len) < MAX_LINELEN) {
     1200            int pos;
     1201            /* save tail */
     1202            strcpy(match_buf, &command[cursor_mb]);
     1203            /* where do we want to have cursor after all? */
     1204            strcpy(&command[cursor_mb], chosen_match + match_pfx_len);
     1205            len = load_string(command, S.maxsize);
     1206            /* add match and tail */
     1207            sprintf(&command[cursor_mb], "%s%s", chosen_match + match_pfx_len, match_buf);
     1208            command_len = load_string(command, S.maxsize);
     1209            /* write out the matched command */
     1210            /* paranoia: load_string can return 0 on conv error,
     1211             * prevent passing pos = (0 - 12) to redraw */
     1212            pos = command_len - len;
     1213            redraw(cmdedit_y, pos >= 0 ? pos : 0);
     1214        }
     1215    }
     1216# endif
     1217 ret:
     1218    free(chosen_match);
     1219    free(match_buf);
     1220}
     1221
     1222#endif  /* FEATURE_TAB_COMPLETION */
     1223
     1224
     1225line_input_t* FAST_FUNC new_line_input_t(int flags)
     1226{
     1227    line_input_t *n = xzalloc(sizeof(*n));
     1228    n->flags = flags;
     1229    return n;
     1230}
    8491231
    8501232
    8511233#if MAX_HISTORY > 0
    8521234
     1235static void save_command_ps_at_cur_history(void)
     1236{
     1237    if (command_ps[0] != BB_NUL) {
     1238        int cur = state->cur_history;
     1239        free(state->history[cur]);
     1240
     1241# if ENABLE_UNICODE_SUPPORT
     1242        {
     1243            char tbuf[MAX_LINELEN];
     1244            save_string(tbuf, sizeof(tbuf));
     1245            state->history[cur] = xstrdup(tbuf);
     1246        }
     1247# else
     1248        state->history[cur] = xstrdup(command_ps);
     1249# endif
     1250    }
     1251}
     1252
    8531253/* state->flags is already checked to be nonzero */
    854 static void get_previous_history(void)
    855 {
    856     if (command_ps[0] != '\0' || state->history[state->cur_history] == NULL) {
    857         free(state->history[state->cur_history]);
    858         state->history[state->cur_history] = xstrdup(command_ps);
    859     }
    860     state->cur_history--;
    861 }
    862 
    863 static int get_next_history(void)
    864 {
    865     if (state->flags & DO_HISTORY) {
    866         int ch = state->cur_history;
    867         if (ch < state->cnt_history) {
    868             get_previous_history(); /* save the current history line */
    869             state->cur_history = ch + 1;
    870             return state->cur_history;
    871         }
     1254static int get_previous_history(void)
     1255{
     1256    if ((state->flags & DO_HISTORY) && state->cur_history) {
     1257        save_command_ps_at_cur_history();
     1258        state->cur_history--;
     1259        return 1;
    8721260    }
    8731261    beep();
     
    8751263}
    8761264
    877 #if ENABLE_FEATURE_EDITING_SAVEHISTORY
     1265static int get_next_history(void)
     1266{
     1267    if (state->flags & DO_HISTORY) {
     1268        if (state->cur_history < state->cnt_history) {
     1269            save_command_ps_at_cur_history(); /* save the current history line */
     1270            return ++state->cur_history;
     1271        }
     1272    }
     1273    beep();
     1274    return 0;
     1275}
     1276
     1277# if ENABLE_FEATURE_EDITING_SAVEHISTORY
     1278/* We try to ensure that concurrent additions to the history
     1279 * do not overwrite each other.
     1280 * Otherwise shell users get unhappy.
     1281 *
     1282 * History file is trimmed lazily, when it grows several times longer
     1283 * than configured MAX_HISTORY lines.
     1284 */
     1285
     1286static void free_line_input_t(line_input_t *n)
     1287{
     1288    int i = n->cnt_history;
     1289    while (i > 0)
     1290        free(n->history[--i]);
     1291    free(n);
     1292}
     1293
    8781294/* state->flags is already checked to be nonzero */
    879 static void load_history(const char *fromfile)
    880 {
     1295static void load_history(line_input_t *st_parm)
     1296{
     1297    char *temp_h[MAX_HISTORY];
     1298    char *line;
    8811299    FILE *fp;
    882     int hi;
    883 
    884     /* cleanup old */
    885     for (hi = state->cnt_history; hi > 0;) {
    886         hi--;
    887         free(state->history[hi]);
    888     }
    889 
    890     fp = fopen(fromfile, "r");
     1300    unsigned idx, i, line_len;
     1301
     1302    /* NB: do not trash old history if file can't be opened */
     1303
     1304    fp = fopen_for_read(st_parm->hist_file);
    8911305    if (fp) {
    892         for (hi = 0; hi < MAX_HISTORY;) {
    893             char *hl = xmalloc_getline(fp);
    894             int l;
    895 
    896             if (!hl)
    897                 break;
    898             l = strlen(hl);
    899             if (l >= MAX_LINELEN)
    900                 hl[MAX_LINELEN-1] = '\0';
    901             if (l == 0 || hl[0] == ' ') {
    902                 free(hl);
     1306        /* clean up old history */
     1307        for (idx = st_parm->cnt_history; idx > 0;) {
     1308            idx--;
     1309            free(st_parm->history[idx]);
     1310            st_parm->history[idx] = NULL;
     1311        }
     1312
     1313        /* fill temp_h[], retaining only last MAX_HISTORY lines */
     1314        memset(temp_h, 0, sizeof(temp_h));
     1315        st_parm->cnt_history_in_file = idx = 0;
     1316        while ((line = xmalloc_fgetline(fp)) != NULL) {
     1317            if (line[0] == '\0') {
     1318                free(line);
    9031319                continue;
    9041320            }
    905             state->history[hi++] = hl;
     1321            free(temp_h[idx]);
     1322            temp_h[idx] = line;
     1323            st_parm->cnt_history_in_file++;
     1324            idx++;
     1325            if (idx == MAX_HISTORY)
     1326                idx = 0;
    9061327        }
    9071328        fclose(fp);
    908     }
    909     state->cur_history = state->cnt_history = hi;
     1329
     1330        /* find first non-NULL temp_h[], if any */
     1331        if (st_parm->cnt_history_in_file) {
     1332            while (temp_h[idx] == NULL) {
     1333                idx++;
     1334                if (idx == MAX_HISTORY)
     1335                    idx = 0;
     1336            }
     1337        }
     1338
     1339        /* copy temp_h[] to st_parm->history[] */
     1340        for (i = 0; i < MAX_HISTORY;) {
     1341            line = temp_h[idx];
     1342            if (!line)
     1343                break;
     1344            idx++;
     1345            if (idx == MAX_HISTORY)
     1346                idx = 0;
     1347            line_len = strlen(line);
     1348            if (line_len >= MAX_LINELEN)
     1349                line[MAX_LINELEN-1] = '\0';
     1350            st_parm->history[i++] = line;
     1351        }
     1352        st_parm->cnt_history = i;
     1353    }
    9101354}
    9111355
    9121356/* state->flags is already checked to be nonzero */
    913 static void save_history(const char *tofile)
    914 {
    915     FILE *fp;
    916 
    917     fp = fopen(tofile, "w");
    918     if (fp) {
    919         int i;
    920 
    921         for (i = 0; i < state->cnt_history; i++) {
    922             fprintf(fp, "%s\n", state->history[i]);
    923         }
    924         fclose(fp);
    925     }
    926 }
    927 #else
    928 #define load_history(a) ((void)0)
    929 #define save_history(a) ((void)0)
    930 #endif /* FEATURE_COMMAND_SAVEHISTORY */
    931 
    932 static void remember_in_history(const char *str)
     1357static void save_history(char *str)
     1358{
     1359    int fd;
     1360    int len, len2;
     1361
     1362    fd = open(state->hist_file, O_WRONLY | O_CREAT | O_APPEND, 0600);
     1363    if (fd < 0)
     1364        return;
     1365    xlseek(fd, 0, SEEK_END); /* paranoia */
     1366    len = strlen(str);
     1367    str[len] = '\n'; /* we (try to) do atomic write */
     1368    len2 = full_write(fd, str, len + 1);
     1369    str[len] = '\0';
     1370    close(fd);
     1371    if (len2 != len + 1)
     1372        return; /* "wtf?" */
     1373
     1374    /* did we write so much that history file needs trimming? */
     1375    state->cnt_history_in_file++;
     1376    if (state->cnt_history_in_file > MAX_HISTORY * 4) {
     1377        char *new_name;
     1378        line_input_t *st_temp;
     1379
     1380        /* we may have concurrently written entries from others.
     1381         * load them */
     1382        st_temp = new_line_input_t(state->flags);
     1383        st_temp->hist_file = state->hist_file;
     1384        load_history(st_temp);
     1385
     1386        /* write out temp file and replace hist_file atomically */
     1387        new_name = xasprintf("%s.%u.new", state->hist_file, (int) getpid());
     1388        fd = open(state->hist_file, O_WRONLY | O_CREAT | O_TRUNC, 0600);
     1389        if (fd >= 0) {
     1390            FILE *fp;
     1391            int i;
     1392
     1393            fp = xfdopen_for_write(fd);
     1394            for (i = 0; i < st_temp->cnt_history; i++)
     1395                fprintf(fp, "%s\n", st_temp->history[i]);
     1396            fclose(fp);
     1397            if (rename(new_name, state->hist_file) == 0)
     1398                state->cnt_history_in_file = st_temp->cnt_history;
     1399        }
     1400        free(new_name);
     1401        free_line_input_t(st_temp);
     1402    }
     1403}
     1404# else
     1405#  define load_history(a) ((void)0)
     1406#  define save_history(a) ((void)0)
     1407# endif /* FEATURE_COMMAND_SAVEHISTORY */
     1408
     1409static void remember_in_history(char *str)
    9331410{
    9341411    int i;
     
    9361413    if (!(state->flags & DO_HISTORY))
    9371414        return;
    938 
     1415    if (str[0] == '\0')
     1416        return;
    9391417    i = state->cnt_history;
    940     free(state->history[MAX_HISTORY]);
    941     state->history[MAX_HISTORY] = NULL;
    942     /* After max history, remove the oldest command */
     1418    /* Don't save dupes */
     1419    if (i && strcmp(state->history[i-1], str) == 0)
     1420        return;
     1421
     1422    free(state->history[MAX_HISTORY]); /* redundant, paranoia */
     1423    state->history[MAX_HISTORY] = NULL; /* redundant, paranoia */
     1424
     1425    /* If history[] is full, remove the oldest command */
     1426    /* we need to keep history[MAX_HISTORY] empty, hence >=, not > */
    9431427    if (i >= MAX_HISTORY) {
    9441428        free(state->history[0]);
    9451429        for (i = 0; i < MAX_HISTORY-1; i++)
    9461430            state->history[i] = state->history[i+1];
    947     }
    948 // Maybe "if (!i || strcmp(history[i-1], command) != 0) ..."
    949 // (i.e. do not save dups?)
     1431        /* i == MAX_HISTORY-1 */
     1432    }
     1433    /* i <= MAX_HISTORY-1 */
    9501434    state->history[i++] = xstrdup(str);
     1435    /* i <= MAX_HISTORY */
    9511436    state->cur_history = i;
    9521437    state->cnt_history = i;
    953 #if ENABLE_FEATURE_EDITING_SAVEHISTORY
     1438# if MAX_HISTORY > 0 && ENABLE_FEATURE_EDITING_SAVEHISTORY
    9541439    if ((state->flags & SAVE_HISTORY) && state->hist_file)
    955         save_history(state->hist_file);
    956 #endif
    957     USE_FEATURE_EDITING_FANCY_PROMPT(num_ok_lines++;)
     1440        save_history(str);
     1441# endif
     1442    IF_FEATURE_EDITING_FANCY_PROMPT(num_ok_lines++;)
    9581443}
    9591444
    9601445#else /* MAX_HISTORY == 0 */
    961 #define remember_in_history(a) ((void)0)
     1446# define remember_in_history(a) ((void)0)
    9621447#endif /* MAX_HISTORY */
    9631448
    9641449
     1450#if ENABLE_FEATURE_EDITING_VI
    9651451/*
    966  * This function is used to grab a character buffer
    967  * from the input file descriptor and allows you to
    968  * a string with full command editing (sort of like
    969  * a mini readline).
    970  *
    971  * The following standard commands are not implemented:
    972  * ESC-b -- Move back one word
    973  * ESC-f -- Move forward one word
    974  * ESC-d -- Delete back one word
    975  * ESC-h -- Delete forward one word
    976  * CTL-t -- Transpose two characters
    977  *
    978  * Minimalist vi-style command line editing available if configured.
    9791452 * vi mode implemented 2005 by Paul Fox <pgf@foxharp.boston.ma.us>
    9801453 */
    981 
    982 #if ENABLE_FEATURE_EDITING_VI
    9831454static void
    984 vi_Word_motion(char *command, int eat)
    985 {
    986     while (cursor < command_len && !isspace(command[cursor]))
     1455vi_Word_motion(int eat)
     1456{
     1457    CHAR_T *command = command_ps;
     1458
     1459    while (cursor < command_len && !BB_isspace(command[cursor]))
    9871460        input_forward();
    988     if (eat) while (cursor < command_len && isspace(command[cursor]))
     1461    if (eat) while (cursor < command_len && BB_isspace(command[cursor]))
    9891462        input_forward();
    9901463}
    9911464
    9921465static void
    993 vi_word_motion(char *command, int eat)
    994 {
    995     if (isalnum(command[cursor]) || command[cursor] == '_') {
     1466vi_word_motion(int eat)
     1467{
     1468    CHAR_T *command = command_ps;
     1469
     1470    if (BB_isalnum(command[cursor]) || command[cursor] == '_') {
    9961471        while (cursor < command_len
    997          && (isalnum(command[cursor+1]) || command[cursor+1] == '_'))
     1472         && (BB_isalnum(command[cursor+1]) || command[cursor+1] == '_')
     1473        ) {
    9981474            input_forward();
    999     } else if (ispunct(command[cursor])) {
    1000         while (cursor < command_len && ispunct(command[cursor+1]))
     1475        }
     1476    } else if (BB_ispunct(command[cursor])) {
     1477        while (cursor < command_len && BB_ispunct(command[cursor+1]))
    10011478            input_forward();
    10021479    }
     
    10051482        input_forward();
    10061483
    1007     if (eat && cursor < command_len && isspace(command[cursor]))
    1008         while (cursor < command_len && isspace(command[cursor]))
     1484    if (eat) {
     1485        while (cursor < command_len && BB_isspace(command[cursor]))
    10091486            input_forward();
     1487    }
    10101488}
    10111489
    10121490static void
    1013 vi_End_motion(char *command)
    1014 {
     1491vi_End_motion(void)
     1492{
     1493    CHAR_T *command = command_ps;
     1494
    10151495    input_forward();
    1016     while (cursor < command_len && isspace(command[cursor]))
     1496    while (cursor < command_len && BB_isspace(command[cursor]))
    10171497        input_forward();
    1018     while (cursor < command_len-1 && !isspace(command[cursor+1]))
     1498    while (cursor < command_len-1 && !BB_isspace(command[cursor+1]))
    10191499        input_forward();
    10201500}
    10211501
    10221502static void
    1023 vi_end_motion(char *command)
    1024 {
     1503vi_end_motion(void)
     1504{
     1505    CHAR_T *command = command_ps;
     1506
    10251507    if (cursor >= command_len-1)
    10261508        return;
    10271509    input_forward();
    1028     while (cursor < command_len-1 && isspace(command[cursor]))
     1510    while (cursor < command_len-1 && BB_isspace(command[cursor]))
    10291511        input_forward();
    10301512    if (cursor >= command_len-1)
    10311513        return;
    1032     if (isalnum(command[cursor]) || command[cursor] == '_') {
     1514    if (BB_isalnum(command[cursor]) || command[cursor] == '_') {
    10331515        while (cursor < command_len-1
    1034          && (isalnum(command[cursor+1]) || command[cursor+1] == '_')
     1516         && (BB_isalnum(command[cursor+1]) || command[cursor+1] == '_')
    10351517        ) {
    10361518            input_forward();
    10371519        }
    1038     } else if (ispunct(command[cursor])) {
    1039         while (cursor < command_len-1 && ispunct(command[cursor+1]))
     1520    } else if (BB_ispunct(command[cursor])) {
     1521        while (cursor < command_len-1 && BB_ispunct(command[cursor+1]))
    10401522            input_forward();
    10411523    }
     
    10431525
    10441526static void
    1045 vi_Back_motion(char *command)
    1046 {
    1047     while (cursor > 0 && isspace(command[cursor-1]))
     1527vi_Back_motion(void)
     1528{
     1529    CHAR_T *command = command_ps;
     1530
     1531    while (cursor > 0 && BB_isspace(command[cursor-1]))
    10481532        input_backward(1);
    1049     while (cursor > 0 && !isspace(command[cursor-1]))
     1533    while (cursor > 0 && !BB_isspace(command[cursor-1]))
    10501534        input_backward(1);
    10511535}
    10521536
    10531537static void
    1054 vi_back_motion(char *command)
    1055 {
     1538vi_back_motion(void)
     1539{
     1540    CHAR_T *command = command_ps;
     1541
    10561542    if (cursor <= 0)
    10571543        return;
    10581544    input_backward(1);
    1059     while (cursor > 0 && isspace(command[cursor]))
     1545    while (cursor > 0 && BB_isspace(command[cursor]))
    10601546        input_backward(1);
    10611547    if (cursor <= 0)
    10621548        return;
    1063     if (isalnum(command[cursor]) || command[cursor] == '_') {
     1549    if (BB_isalnum(command[cursor]) || command[cursor] == '_') {
    10641550        while (cursor > 0
    1065          && (isalnum(command[cursor-1]) || command[cursor-1] == '_')
     1551         && (BB_isalnum(command[cursor-1]) || command[cursor-1] == '_')
    10661552        ) {
    10671553            input_backward(1);
    10681554        }
    1069     } else if (ispunct(command[cursor])) {
    1070         while (cursor > 0 && ispunct(command[cursor-1]))
     1555    } else if (BB_ispunct(command[cursor])) {
     1556        while (cursor > 0 && BB_ispunct(command[cursor-1]))
    10711557            input_backward(1);
    10721558    }
    10731559}
    10741560#endif
     1561
     1562/* Modelled after bash 4.0 behavior of Ctrl-<arrow> */
     1563static void ctrl_left(void)
     1564{
     1565    CHAR_T *command = command_ps;
     1566
     1567    while (1) {
     1568        CHAR_T c;
     1569
     1570        input_backward(1);
     1571        if (cursor == 0)
     1572            break;
     1573        c = command[cursor];
     1574        if (c != ' ' && !BB_ispunct(c)) {
     1575            /* we reached a "word" delimited by spaces/punct.
     1576             * go to its beginning */
     1577            while (1) {
     1578                c = command[cursor - 1];
     1579                if (c == ' ' || BB_ispunct(c))
     1580                    break;
     1581                input_backward(1);
     1582                if (cursor == 0)
     1583                    break;
     1584            }
     1585            break;
     1586        }
     1587    }
     1588}
     1589static void ctrl_right(void)
     1590{
     1591    CHAR_T *command = command_ps;
     1592
     1593    while (1) {
     1594        CHAR_T c;
     1595
     1596        c = command[cursor];
     1597        if (c == BB_NUL)
     1598            break;
     1599        if (c != ' ' && !BB_ispunct(c)) {
     1600            /* we reached a "word" delimited by spaces/punct.
     1601             * go to its end + 1 */
     1602            while (1) {
     1603                input_forward();
     1604                c = command[cursor];
     1605                if (c == BB_NUL || c == ' ' || BB_ispunct(c))
     1606                    break;
     1607            }
     1608            break;
     1609        }
     1610        input_forward();
     1611    }
     1612}
    10751613
    10761614
     
    10791617 */
    10801618
     1619#if ENABLE_FEATURE_EDITING_ASK_TERMINAL
     1620static void ask_terminal(void)
     1621{
     1622    /* Ask terminal where is the cursor now.
     1623     * lineedit_read_key handles response and corrects
     1624     * our idea of current cursor position.
     1625     * Testcase: run "echo -n long_line_long_line_long_line",
     1626     * then type in a long, wrapping command and try to
     1627     * delete it using backspace key.
     1628     * Note: we print it _after_ prompt, because
     1629     * prompt may contain CR. Example: PS1='\[\r\n\]\w '
     1630     */
     1631    /* Problem: if there is buffered input on stdin,
     1632     * the response will be delivered later,
     1633     * possibly to an unsuspecting application.
     1634     * Testcase: "sleep 1; busybox ash" + press and hold [Enter].
     1635     * Result:
     1636     * ~/srcdevel/bbox/fix/busybox.t4 #
     1637     * ~/srcdevel/bbox/fix/busybox.t4 #
     1638     * ^[[59;34~/srcdevel/bbox/fix/busybox.t4 #  <-- garbage
     1639     * ~/srcdevel/bbox/fix/busybox.t4 #
     1640     *
     1641     * Checking for input with poll only makes the race narrower,
     1642     * I still can trigger it. Strace:
     1643     *
     1644     * write(1, "~/srcdevel/bbox/fix/busybox.t4 # ", 33) = 33
     1645     * poll([{fd=0, events=POLLIN}], 1, 0) = 0 (Timeout)  <-- no input exists
     1646     * write(1, "\33[6n", 4) = 4  <-- send the ESC sequence, quick!
     1647     * poll([{fd=0, events=POLLIN}], 1, 4294967295) = 1 ([{fd=0, revents=POLLIN}])
     1648     * read(0, "\n", 1)      = 1  <-- oh crap, user's input got in first
     1649     */
     1650    struct pollfd pfd;
     1651
     1652    pfd.fd = STDIN_FILENO;
     1653    pfd.events = POLLIN;
     1654    if (safe_poll(&pfd, 1, 0) == 0) {
     1655        S.sent_ESC_br6n = 1;
     1656        fputs(ESC"[6n", stdout);
     1657        fflush_all(); /* make terminal see it ASAP! */
     1658    }
     1659}
     1660#else
     1661#define ask_terminal() ((void)0)
     1662#endif
     1663
    10811664#if !ENABLE_FEATURE_EDITING_FANCY_PROMPT
    1082 static void parse_prompt(const char *prmt_ptr)
     1665static void parse_and_put_prompt(const char *prmt_ptr)
    10831666{
    10841667    cmdedit_prompt = prmt_ptr;
     
    10871670}
    10881671#else
    1089 static void parse_prompt(const char *prmt_ptr)
     1672static void parse_and_put_prompt(const char *prmt_ptr)
    10901673{
    10911674    int prmt_len = 0;
     
    10931676    char flg_not_length = '[';
    10941677    char *prmt_mem_ptr = xzalloc(1);
    1095     char *pwd_buf = xrealloc_getcwd_or_warn(NULL);
    1096     char buf2[PATH_MAX + 1];
    1097     char buf[2];
     1678    char *cwd_buf = xrealloc_getcwd_or_warn(NULL);
     1679    char cbuf[2];
    10981680    char c;
    10991681    char *pbuf;
     
    11011683    cmdedit_prmt_len = 0;
    11021684
    1103     if (!pwd_buf) {
    1104         pwd_buf = (char *)bb_msg_unknown;
    1105     }
     1685    if (!cwd_buf) {
     1686        cwd_buf = (char *)bb_msg_unknown;
     1687    }
     1688
     1689    cbuf[1] = '\0'; /* never changes */
    11061690
    11071691    while (*prmt_ptr) {
    1108         pbuf = buf;
    1109         pbuf[1] = 0;
     1692        char *free_me = NULL;
     1693
     1694        pbuf = cbuf;
    11101695        c = *prmt_ptr++;
    11111696        if (c == '\\') {
     
    11151700            c = bb_process_escape_sequence(&prmt_ptr);
    11161701            if (prmt_ptr == cp) {
    1117                 if (*cp == 0)
     1702                if (*cp == '\0')
    11181703                    break;
    11191704                c = *prmt_ptr++;
     1705
    11201706                switch (c) {
    1121 #if ENABLE_FEATURE_GETUSERNAME_AND_HOMEDIR
     1707# if ENABLE_USERNAME_OR_HOMEDIR
    11221708                case 'u':
    11231709                    pbuf = user_buf ? user_buf : (char*)"";
    11241710                    break;
    1125 #endif
     1711# endif
    11261712                case 'h':
    1127                     pbuf = hostname_buf;
    1128                     if (!pbuf) {
    1129                         pbuf = xzalloc(256);
    1130                         if (gethostname(pbuf, 255) < 0) {
    1131                             strcpy(pbuf, "?");
    1132                         } else {
    1133                             char *s = strchr(pbuf, '.');
    1134                             if (s)
    1135                                 *s = '\0';
    1136                         }
    1137                         hostname_buf = pbuf;
    1138                     }
     1713                    pbuf = free_me = safe_gethostname();
     1714                    *strchrnul(pbuf, '.') = '\0';
    11391715                    break;
    11401716                case '$':
    11411717                    c = (geteuid() == 0 ? '#' : '$');
    11421718                    break;
    1143 #if ENABLE_FEATURE_GETUSERNAME_AND_HOMEDIR
     1719# if ENABLE_USERNAME_OR_HOMEDIR
    11441720                case 'w':
    1145                     pbuf = pwd_buf;
     1721                    /* /home/user[/something] -> ~[/something] */
     1722                    pbuf = cwd_buf;
    11461723                    l = strlen(home_pwd_buf);
    11471724                    if (l != 0
    1148                      && strncmp(home_pwd_buf, pbuf, l) == 0
    1149                      && (pbuf[l]=='/' || pbuf[l]=='\0')
    1150                      && strlen(pwd_buf+l)<PATH_MAX
     1725                     && strncmp(home_pwd_buf, cwd_buf, l) == 0
     1726                     && (cwd_buf[l]=='/' || cwd_buf[l]=='\0')
     1727                     && strlen(cwd_buf + l) < PATH_MAX
    11511728                    ) {
    1152                         pbuf = buf2;
    1153                         *pbuf = '~';
    1154                         strcpy(pbuf+1, pwd_buf+l);
     1729                        pbuf = free_me = xasprintf("~%s", cwd_buf + l);
    11551730                    }
    11561731                    break;
    1157 #endif
     1732# endif
    11581733                case 'W':
    1159                     pbuf = pwd_buf;
     1734                    pbuf = cwd_buf;
    11601735                    cp = strrchr(pbuf, '/');
    11611736                    if (cp != NULL && cp != pbuf)
     
    11631738                    break;
    11641739                case '!':
    1165                     pbuf = buf2;
    1166                     snprintf(buf2, sizeof(buf2), "%d", num_ok_lines);
     1740                    pbuf = free_me = xasprintf("%d", num_ok_lines);
    11671741                    break;
    11681742                case 'e': case 'E':     /* \e \E = \033 */
    11691743                    c = '\033';
    11701744                    break;
    1171                 case 'x': case 'X':
     1745                case 'x': case 'X': {
     1746                    char buf2[4];
    11721747                    for (l = 0; l < 3;) {
    1173                         int h;
     1748                        unsigned h;
    11741749                        buf2[l++] = *prmt_ptr;
    1175                         buf2[l] = 0;
    1176                         h = strtol(buf2, &pbuf, 16);
     1750                        buf2[l] = '\0';
     1751                        h = strtoul(buf2, &pbuf, 16);
    11771752                        if (h > UCHAR_MAX || (pbuf - buf2) < l) {
    1178                             l--;
     1753                            buf2[--l] = '\0';
    11791754                            break;
    11801755                        }
    11811756                        prmt_ptr++;
    11821757                    }
    1183                     buf2[l] = 0;
    1184                     c = (char)strtol(buf2, NULL, 16);
     1758                    c = (char)strtoul(buf2, NULL, 16);
    11851759                    if (c == 0)
    11861760                        c = '?';
    1187                     pbuf = buf;
     1761                    pbuf = cbuf;
    11881762                    break;
     1763                }
    11891764                case '[': case ']':
    11901765                    if (c == flg_not_length) {
    1191                         flg_not_length = flg_not_length == '[' ? ']' : '[';
     1766                        flg_not_length = (flg_not_length == '[' ? ']' : '[');
    11921767                        continue;
    11931768                    }
    11941769                    break;
    1195                 }
    1196             }
    1197         }
    1198         if (pbuf == buf)
    1199             *pbuf = c;
     1770                } /* switch */
     1771            } /* if */
     1772        } /* if */
     1773        cbuf[0] = c;
    12001774        cur_prmt_len = strlen(pbuf);
    12011775        prmt_len += cur_prmt_len;
     
    12031777            cmdedit_prmt_len += cur_prmt_len;
    12041778        prmt_mem_ptr = strcat(xrealloc(prmt_mem_ptr, prmt_len+1), pbuf);
    1205     }
    1206     if (pwd_buf != (char *)bb_msg_unknown)
    1207         free(pwd_buf);
     1779        free(free_me);
     1780    } /* while */
     1781
     1782    if (cwd_buf != (char *)bb_msg_unknown)
     1783        free(cwd_buf);
    12081784    cmdedit_prompt = prmt_mem_ptr;
    12091785    put_prompt();
    12101786}
    12111787#endif
    1212 
    1213 #define setTermSettings(fd, argp) tcsetattr(fd, TCSANOW, argp)
    1214 #define getTermSettings(fd, argp) tcgetattr(fd, argp);
    1215 
    1216 static sighandler_t previous_SIGWINCH_handler;
    12171788
    12181789static void cmdedit_setwidth(unsigned w, int redraw_flg)
     
    12241795        /* redraw */
    12251796        redraw((new_y >= cmdedit_y ? new_y : cmdedit_y), command_len - cursor);
    1226         fflush(stdout);
     1797        fflush_all();
    12271798    }
    12281799}
     
    12301801static void win_changed(int nsig)
    12311802{
    1232     int width;
     1803    int sv_errno = errno;
     1804    unsigned width;
    12331805    get_terminal_width_height(0, &width, NULL);
    12341806    cmdedit_setwidth(width, nsig /* - just a yes/no flag */);
    12351807    if (nsig == SIGWINCH)
    12361808        signal(SIGWINCH, win_changed); /* rearm ourself */
    1237 }
    1238 
    1239 /*
    1240  * The emacs and vi modes share much of the code in the big
    1241  * command loop.  Commands entered when in vi's command mode (aka
    1242  * "escape mode") get an extra bit added to distinguish them --
    1243  * this keeps them from being self-inserted.  This clutters the
    1244  * big switch a bit, but keeps all the code in one place.
    1245  */
    1246 
    1247 #define vbit 0x100
     1809    errno = sv_errno;
     1810}
     1811
     1812static int lineedit_read_key(char *read_key_buffer)
     1813{
     1814    int64_t ic;
     1815    int timeout = -1;
     1816#if ENABLE_UNICODE_SUPPORT
     1817    char unicode_buf[MB_CUR_MAX + 1];
     1818    int unicode_idx = 0;
     1819#endif
     1820
     1821    while (1) {
     1822        /* Wait for input. TIMEOUT = -1 makes read_key wait even
     1823         * on nonblocking stdin, TIMEOUT = 50 makes sure we won't
     1824         * insist on full MB_CUR_MAX buffer to declare input like
     1825         * "\xff\n",pause,"ls\n" invalid and thus won't lose "ls".
     1826         *
     1827         * Note: read_key sets errno to 0 on success.
     1828         */
     1829        ic = read_key(STDIN_FILENO, read_key_buffer, timeout);
     1830        if (errno) {
     1831#if ENABLE_UNICODE_SUPPORT
     1832            if (errno == EAGAIN && unicode_idx != 0)
     1833                goto pushback;
     1834#endif
     1835            break;
     1836        }
     1837
     1838#if ENABLE_FEATURE_EDITING_ASK_TERMINAL
     1839        if ((int32_t)ic == KEYCODE_CURSOR_POS
     1840         && S.sent_ESC_br6n
     1841        ) {
     1842            S.sent_ESC_br6n = 0;
     1843            if (cursor == 0) { /* otherwise it may be bogus */
     1844                int col = ((ic >> 32) & 0x7fff) - 1;
     1845                if (col > cmdedit_prmt_len) {
     1846                    cmdedit_x += (col - cmdedit_prmt_len);
     1847                    while (cmdedit_x >= cmdedit_termw) {
     1848                        cmdedit_x -= cmdedit_termw;
     1849                        cmdedit_y++;
     1850                    }
     1851                }
     1852            }
     1853            continue;
     1854        }
     1855#endif
     1856
     1857#if ENABLE_UNICODE_SUPPORT
     1858        if (unicode_status == UNICODE_ON) {
     1859            wchar_t wc;
     1860
     1861            if ((int32_t)ic < 0) /* KEYCODE_xxx */
     1862                break;
     1863            // TODO: imagine sequence like: 0xff,<left-arrow>: we are currently losing 0xff...
     1864
     1865            unicode_buf[unicode_idx++] = ic;
     1866            unicode_buf[unicode_idx] = '\0';
     1867            if (mbstowcs(&wc, unicode_buf, 1) != 1) {
     1868                /* Not (yet?) a valid unicode char */
     1869                if (unicode_idx < MB_CUR_MAX) {
     1870                    timeout = 50;
     1871                    continue;
     1872                }
     1873 pushback:
     1874                /* Invalid sequence. Save all "bad bytes" except first */
     1875                read_key_ungets(read_key_buffer, unicode_buf + 1, unicode_idx - 1);
     1876# if !ENABLE_UNICODE_PRESERVE_BROKEN
     1877                ic = CONFIG_SUBST_WCHAR;
     1878# else
     1879                ic = unicode_mark_raw_byte(unicode_buf[0]);
     1880# endif
     1881            } else {
     1882                /* Valid unicode char, return its code */
     1883                ic = wc;
     1884            }
     1885        }
     1886#endif
     1887        break;
     1888    }
     1889
     1890    return ic;
     1891}
     1892
     1893#if ENABLE_UNICODE_BIDI_SUPPORT
     1894static int isrtl_str(void)
     1895{
     1896    int idx = cursor;
     1897
     1898    while (idx < command_len && unicode_bidi_is_neutral_wchar(command_ps[idx]))
     1899        idx++;
     1900    return unicode_bidi_isrtl(command_ps[idx]);
     1901}
     1902#else
     1903# define isrtl_str() 0
     1904#endif
    12481905
    12491906/* leave out the "vi-mode"-only case labels if vi editing isn't
    12501907 * configured. */
    1251 #define vi_case(caselabel) USE_FEATURE_EDITING(case caselabel)
     1908#define vi_case(caselabel) IF_FEATURE_EDITING_VI(case caselabel)
    12521909
    12531910/* convert uppercase ascii to equivalent control char, for readability */
     
    12551912#define CTRL(a) ((a) & ~0x40)
    12561913
    1257 /* Returns:
    1258  * -1 on read errors or EOF, or on bare Ctrl-D.
    1259  * 0  on ctrl-C,
     1914/* maxsize must be >= 2.
     1915 * Returns:
     1916 * -1 on read errors or EOF, or on bare Ctrl-D,
     1917 * 0  on ctrl-C (the line entered is still returned in 'command'),
    12601918 * >0 length of input string, including terminating '\n'
    12611919 */
    1262 int read_line_input(const char* prompt, char* command, int maxsize, line_input_t *st)
    1263 {
    1264     int lastWasTab = FALSE;
    1265     unsigned int ic;
    1266     unsigned char c;
     1920int FAST_FUNC read_line_input(const char *prompt, char *command, int maxsize, line_input_t *st)
     1921{
     1922    int len;
     1923#if ENABLE_FEATURE_TAB_COMPLETION
     1924    smallint lastWasTab = 0;
     1925#endif
    12671926    smallint break_out = 0;
    12681927#if ENABLE_FEATURE_EDITING_VI
    12691928    smallint vi_cmdmode = 0;
    1270     smalluint prevc;
    1271 #endif
     1929#endif
     1930    struct termios initial_settings;
     1931    struct termios new_settings;
     1932    char read_key_buffer[KEYCODE_BUFFER_SIZE];
     1933
     1934    INIT_S();
     1935
     1936    if (tcgetattr(STDIN_FILENO, &initial_settings) < 0
     1937     || !(initial_settings.c_lflag & ECHO)
     1938    ) {
     1939        /* Happens when e.g. stty -echo was run before */
     1940        parse_and_put_prompt(prompt);
     1941        /* fflush_all(); - done by parse_and_put_prompt */
     1942        if (fgets(command, maxsize, stdin) == NULL)
     1943            len = -1; /* EOF or error */
     1944        else
     1945            len = strlen(command);
     1946        DEINIT_S();
     1947        return len;
     1948    }
     1949
     1950    init_unicode();
    12721951
    12731952// FIXME: audit & improve this
    12741953    if (maxsize > MAX_LINELEN)
    12751954        maxsize = MAX_LINELEN;
     1955    S.maxsize = maxsize;
    12761956
    12771957    /* With null flags, no other fields are ever used */
    12781958    state = st ? st : (line_input_t*) &const_int_0;
    1279 #if ENABLE_FEATURE_EDITING_SAVEHISTORY
     1959#if MAX_HISTORY > 0
     1960# if ENABLE_FEATURE_EDITING_SAVEHISTORY
    12801961    if ((state->flags & SAVE_HISTORY) && state->hist_file)
    1281         load_history(state->hist_file);
     1962        if (state->cnt_history == 0)
     1963            load_history(state);
     1964# endif
     1965    if (state->flags & DO_HISTORY)
     1966        state->cur_history = state->cnt_history;
    12821967#endif
    12831968
     
    12851970    cmdedit_y = 0;  /* quasireal y, not true if line > xt*yt */
    12861971    command_len = 0;
     1972#if ENABLE_UNICODE_SUPPORT
     1973    command_ps = xzalloc(maxsize * sizeof(command_ps[0]));
     1974#else
    12871975    command_ps = command;
    12881976    command[0] = '\0';
    1289 
    1290     getTermSettings(0, (void *) &initial_settings);
    1291     memcpy(&new_settings, &initial_settings, sizeof(new_settings));
     1977#endif
     1978#define command command_must_not_be_used
     1979
     1980    new_settings = initial_settings;
    12921981    new_settings.c_lflag &= ~ICANON;        /* unbuffered input */
    12931982    /* Turn off echoing and CTRL-C, so we can trap it */
     
    12981987    /* Turn off CTRL-C, so we can trap it */
    12991988#ifndef _POSIX_VDISABLE
    1300 #define _POSIX_VDISABLE '\0'
     1989# define _POSIX_VDISABLE '\0'
    13011990#endif
    13021991    new_settings.c_cc[VINTR] = _POSIX_VDISABLE;
    1303     setTermSettings(0, (void *) &new_settings);
     1992    tcsetattr_stdin_TCSANOW(&new_settings);
    13041993
    13051994    /* Now initialize things */
    13061995    previous_SIGWINCH_handler = signal(SIGWINCH, win_changed);
    13071996    win_changed(0); /* do initial resizing */
    1308 #if ENABLE_FEATURE_GETUSERNAME_AND_HOMEDIR
     1997#if ENABLE_USERNAME_OR_HOMEDIR
    13091998    {
    13101999        struct passwd *entry;
     
    13122001        entry = getpwuid(geteuid());
    13132002        if (entry) {
    1314             /* If we enter read_line_input for the Nth time,
    1315              * they may be already allocated! Need to free. */
    1316             free(user_buf);
    1317             if (home_pwd_buf != null_str)
    1318                 free(home_pwd_buf);
    13192003            user_buf = xstrdup(entry->pw_name);
    13202004            home_pwd_buf = xstrdup(entry->pw_dir);
    1321             /* They are not freed on exit (too small to bother) */
    1322         }
    1323     }
    1324 #endif
    1325     /* Print out the command prompt */
    1326     parse_prompt(prompt);
    1327 
     2005        }
     2006    }
     2007#endif
     2008
     2009#if 0
     2010    for (i = 0; i <= MAX_HISTORY; i++)
     2011        bb_error_msg("history[%d]:'%s'", i, state->history[i]);
     2012    bb_error_msg("cur_history:%d cnt_history:%d", state->cur_history, state->cnt_history);
     2013#endif
     2014
     2015    /* Print out the command prompt, optionally ask where cursor is */
     2016    parse_and_put_prompt(prompt);
     2017    ask_terminal();
     2018
     2019    read_key_buffer[0] = 0;
    13282020    while (1) {
    1329         fflush(stdout);
    1330 
    1331         if (safe_read(0, &c, 1) < 1) {
    1332             /* if we can't read input then exit */
    1333             goto prepare_to_die;
    1334         }
    1335 
    1336         ic = c;
     2021        /*
     2022         * The emacs and vi modes share much of the code in the big
     2023         * command loop.  Commands entered when in vi's command mode
     2024         * (aka "escape mode") get an extra bit added to distinguish
     2025         * them - this keeps them from being self-inserted. This
     2026         * clutters the big switch a bit, but keeps all the code
     2027         * in one place.
     2028         */
     2029        enum {
     2030            VI_CMDMODE_BIT = 0x40000000,
     2031            /* 0x80000000 bit flags KEYCODE_xxx */
     2032        };
     2033        int32_t ic, ic_raw;
     2034
     2035        fflush_all();
     2036        ic = ic_raw = lineedit_read_key(read_key_buffer);
    13372037
    13382038#if ENABLE_FEATURE_EDITING_VI
    13392039        newdelflag = 1;
    1340         if (vi_cmdmode)
    1341             ic |= vbit;
    1342 #endif
     2040        if (vi_cmdmode) {
     2041            /* btw, since KEYCODE_xxx are all < 0, this doesn't
     2042             * change ic if it contains one of them: */
     2043            ic |= VI_CMDMODE_BIT;
     2044        }
     2045#endif
     2046
    13432047        switch (ic) {
    13442048        case '\n':
    13452049        case '\r':
    1346         vi_case('\n'|vbit:)
    1347         vi_case('\r'|vbit:)
     2050        vi_case('\n'|VI_CMDMODE_BIT:)
     2051        vi_case('\r'|VI_CMDMODE_BIT:)
    13482052            /* Enter */
    13492053            goto_new_line();
    13502054            break_out = 1;
    13512055            break;
    1352 #if ENABLE_FEATURE_EDITING_FANCY_KEYS
    13532056        case CTRL('A'):
    1354         vi_case('0'|vbit:)
     2057        vi_case('0'|VI_CMDMODE_BIT:)
    13552058            /* Control-a -- Beginning of line */
    13562059            input_backward(cursor);
    13572060            break;
    13582061        case CTRL('B'):
    1359         vi_case('h'|vbit:)
    1360         vi_case('\b'|vbit:)
    1361         vi_case('\x7f'|vbit:) /* DEL */
    1362             /* Control-b -- Move back one character */
    1363             input_backward(1);
    1364             break;
    1365 #endif
    1366         case CTRL('C'):
    1367         vi_case(CTRL('C')|vbit:)
    1368             /* Control-c -- stop gathering input */
    1369             goto_new_line();
    1370             command_len = 0;
    1371             break_out = -1; /* "do not append '\n'" */
    1372             break;
    1373         case CTRL('D'):
    1374             /* Control-d -- Delete one character, or exit
    1375              * if the len=0 and no chars to delete */
    1376             if (command_len == 0) {
    1377                 errno = 0;
    1378  prepare_to_die:
    1379                 /* to control stopped jobs */
    1380                 break_out = command_len = -1;
    1381                 break;
    1382             }
    1383             input_delete(0);
    1384             break;
    1385 
    1386 #if ENABLE_FEATURE_EDITING_FANCY_KEYS
     2062        vi_case('h'|VI_CMDMODE_BIT:)
     2063        vi_case('\b'|VI_CMDMODE_BIT:) /* ^H */
     2064        vi_case('\x7f'|VI_CMDMODE_BIT:) /* DEL */
     2065            input_backward(1); /* Move back one character */
     2066            break;
    13872067        case CTRL('E'):
    1388         vi_case('$'|vbit:)
     2068        vi_case('$'|VI_CMDMODE_BIT:)
    13892069            /* Control-e -- End of line */
    1390             input_end();
     2070            put_till_end_and_adv_cursor();
    13912071            break;
    13922072        case CTRL('F'):
    1393         vi_case('l'|vbit:)
    1394         vi_case(' '|vbit:)
    1395             /* Control-f -- Move forward one character */
    1396             input_forward();
    1397             break;
    1398 #endif
    1399 
    1400         case '\b':
     2073        vi_case('l'|VI_CMDMODE_BIT:)
     2074        vi_case(' '|VI_CMDMODE_BIT:)
     2075            input_forward(); /* Move forward one character */
     2076            break;
     2077        case '\b':   /* ^H */
    14012078        case '\x7f': /* DEL */
    1402             /* Control-h and DEL */
    1403             input_backspace();
    1404             break;
    1405 
     2079            if (!isrtl_str())
     2080                input_backspace();
     2081            else
     2082                input_delete(0);
     2083            break;
     2084        case KEYCODE_DELETE:
     2085            if (!isrtl_str())
     2086                input_delete(0);
     2087            else
     2088                input_backspace();
     2089            break;
     2090#if ENABLE_FEATURE_TAB_COMPLETION
    14062091        case '\t':
    14072092            input_tab(&lastWasTab);
    14082093            break;
    1409 
    1410 #if ENABLE_FEATURE_EDITING_FANCY_KEYS
     2094#endif
    14112095        case CTRL('K'):
    14122096            /* Control-k -- clear to end of line */
    1413             command[cursor] = 0;
     2097            command_ps[cursor] = BB_NUL;
    14142098            command_len = cursor;
    1415             printf("\033[J");
     2099            printf(SEQ_CLEAR_TILL_END_OF_SCREEN);
    14162100            break;
    14172101        case CTRL('L'):
    1418         vi_case(CTRL('L')|vbit:)
     2102        vi_case(CTRL('L')|VI_CMDMODE_BIT:)
    14192103            /* Control-l -- clear screen */
    1420             printf("\033[H");
     2104            printf(ESC"[H"); /* cursor to top,left */
    14212105            redraw(0, command_len - cursor);
    14222106            break;
    1423 #endif
    1424 
    14252107#if MAX_HISTORY > 0
    14262108        case CTRL('N'):
    1427         vi_case(CTRL('N')|vbit:)
    1428         vi_case('j'|vbit:)
     2109        vi_case(CTRL('N')|VI_CMDMODE_BIT:)
     2110        vi_case('j'|VI_CMDMODE_BIT:)
    14292111            /* Control-n -- Get next command in history */
    14302112            if (get_next_history())
     
    14322114            break;
    14332115        case CTRL('P'):
    1434         vi_case(CTRL('P')|vbit:)
    1435         vi_case('k'|vbit:)
     2116        vi_case(CTRL('P')|VI_CMDMODE_BIT:)
     2117        vi_case('k'|VI_CMDMODE_BIT:)
    14362118            /* Control-p -- Get previous command from history */
    1437             if ((state->flags & DO_HISTORY) && state->cur_history > 0) {
    1438                 get_previous_history();
     2119            if (get_previous_history())
    14392120                goto rewrite_line;
    1440             }
    1441             beep();
    1442             break;
    1443 #endif
    1444 
    1445 #if ENABLE_FEATURE_EDITING_FANCY_KEYS
     2121            break;
     2122#endif
    14462123        case CTRL('U'):
    1447         vi_case(CTRL('U')|vbit:)
     2124        vi_case(CTRL('U')|VI_CMDMODE_BIT:)
    14482125            /* Control-U -- Clear line before cursor */
    14492126            if (cursor) {
    1450                 strcpy(command, command + cursor);
    14512127                command_len -= cursor;
     2128                memmove(command_ps, command_ps + cursor,
     2129                    (command_len + 1) * sizeof(command_ps[0]));
    14522130                redraw(cmdedit_y, command_len);
    14532131            }
    14542132            break;
    1455 #endif
    14562133        case CTRL('W'):
    1457         vi_case(CTRL('W')|vbit:)
     2134        vi_case(CTRL('W')|VI_CMDMODE_BIT:)
    14582135            /* Control-W -- Remove the last word */
    1459             while (cursor > 0 && isspace(command[cursor-1]))
     2136            while (cursor > 0 && BB_isspace(command_ps[cursor-1]))
    14602137                input_backspace();
    1461             while (cursor > 0 && !isspace(command[cursor-1]))
     2138            while (cursor > 0 && !BB_isspace(command_ps[cursor-1]))
    14622139                input_backspace();
    14632140            break;
    14642141
    14652142#if ENABLE_FEATURE_EDITING_VI
    1466         case 'i'|vbit:
     2143        case 'i'|VI_CMDMODE_BIT:
    14672144            vi_cmdmode = 0;
    14682145            break;
    1469         case 'I'|vbit:
     2146        case 'I'|VI_CMDMODE_BIT:
    14702147            input_backward(cursor);
    14712148            vi_cmdmode = 0;
    14722149            break;
    1473         case 'a'|vbit:
     2150        case 'a'|VI_CMDMODE_BIT:
    14742151            input_forward();
    14752152            vi_cmdmode = 0;
    14762153            break;
    1477         case 'A'|vbit:
    1478             input_end();
     2154        case 'A'|VI_CMDMODE_BIT:
     2155            put_till_end_and_adv_cursor();
    14792156            vi_cmdmode = 0;
    14802157            break;
    1481         case 'x'|vbit:
     2158        case 'x'|VI_CMDMODE_BIT:
    14822159            input_delete(1);
    14832160            break;
    1484         case 'X'|vbit:
     2161        case 'X'|VI_CMDMODE_BIT:
    14852162            if (cursor > 0) {
    14862163                input_backward(1);
     
    14882165            }
    14892166            break;
    1490         case 'W'|vbit:
    1491             vi_Word_motion(command, 1);
    1492             break;
    1493         case 'w'|vbit:
    1494             vi_word_motion(command, 1);
    1495             break;
    1496         case 'E'|vbit:
    1497             vi_End_motion(command);
    1498             break;
    1499         case 'e'|vbit:
    1500             vi_end_motion(command);
    1501             break;
    1502         case 'B'|vbit:
    1503             vi_Back_motion(command);
    1504             break;
    1505         case 'b'|vbit:
    1506             vi_back_motion(command);
    1507             break;
    1508         case 'C'|vbit:
     2167        case 'W'|VI_CMDMODE_BIT:
     2168            vi_Word_motion(1);
     2169            break;
     2170        case 'w'|VI_CMDMODE_BIT:
     2171            vi_word_motion(1);
     2172            break;
     2173        case 'E'|VI_CMDMODE_BIT:
     2174            vi_End_motion();
     2175            break;
     2176        case 'e'|VI_CMDMODE_BIT:
     2177            vi_end_motion();
     2178            break;
     2179        case 'B'|VI_CMDMODE_BIT:
     2180            vi_Back_motion();
     2181            break;
     2182        case 'b'|VI_CMDMODE_BIT:
     2183            vi_back_motion();
     2184            break;
     2185        case 'C'|VI_CMDMODE_BIT:
    15092186            vi_cmdmode = 0;
    15102187            /* fall through */
    1511         case 'D'|vbit:
     2188        case 'D'|VI_CMDMODE_BIT:
    15122189            goto clear_to_eol;
    15132190
    1514         case 'c'|vbit:
     2191        case 'c'|VI_CMDMODE_BIT:
    15152192            vi_cmdmode = 0;
    15162193            /* fall through */
    1517         case 'd'|vbit: {
     2194        case 'd'|VI_CMDMODE_BIT: {
    15182195            int nc, sc;
    1519             sc = cursor;
    1520             prevc = ic;
    1521             if (safe_read(0, &c, 1) < 1)
    1522                 goto prepare_to_die;
    1523             if (c == (prevc & 0xff)) {
    1524                 /* "cc", "dd" */
     2196
     2197            ic = lineedit_read_key(read_key_buffer);
     2198            if (errno) /* error */
     2199                goto return_error_indicator;
     2200            if (ic == ic_raw) { /* "cc", "dd" */
    15252201                input_backward(cursor);
    15262202                goto clear_to_eol;
    15272203                break;
    15282204            }
    1529             switch (c) {
     2205
     2206            sc = cursor;
     2207            switch (ic) {
    15302208            case 'w':
    15312209            case 'W':
    15322210            case 'e':
    15332211            case 'E':
    1534                 switch (c) {
     2212                switch (ic) {
    15352213                case 'w':   /* "dw", "cw" */
    1536                     vi_word_motion(command, vi_cmdmode);
     2214                    vi_word_motion(vi_cmdmode);
    15372215                    break;
    15382216                case 'W':   /* 'dW', 'cW' */
    1539                     vi_Word_motion(command, vi_cmdmode);
     2217                    vi_Word_motion(vi_cmdmode);
    15402218                    break;
    15412219                case 'e':   /* 'de', 'ce' */
    1542                     vi_end_motion(command);
     2220                    vi_end_motion();
    15432221                    input_forward();
    15442222                    break;
    15452223                case 'E':   /* 'dE', 'cE' */
    1546                     vi_End_motion(command);
     2224                    vi_End_motion();
    15472225                    input_forward();
    15482226                    break;
     
    15552233            case 'b':  /* "db", "cb" */
    15562234            case 'B':  /* implemented as B */
    1557                 if (c == 'b')
    1558                     vi_back_motion(command);
     2235                if (ic == 'b')
     2236                    vi_back_motion();
    15592237                else
    1560                     vi_Back_motion(command);
     2238                    vi_Back_motion();
    15612239                while (sc-- > cursor)
    15622240                    input_delete(1);
     
    15662244                break;
    15672245            case '$':  /* "d$", "c$" */
    1568             clear_to_eol:
     2246 clear_to_eol:
    15692247                while (cursor < command_len)
    15702248                    input_delete(1);
     
    15732251            break;
    15742252        }
    1575         case 'p'|vbit:
     2253        case 'p'|VI_CMDMODE_BIT:
    15762254            input_forward();
    15772255            /* fallthrough */
    1578         case 'P'|vbit:
     2256        case 'P'|VI_CMDMODE_BIT:
    15792257            put();
    15802258            break;
    1581         case 'r'|vbit:
    1582             if (safe_read(0, &c, 1) < 1)
    1583                 goto prepare_to_die;
    1584             if (c == 0)
     2259        case 'r'|VI_CMDMODE_BIT:
     2260//FIXME: unicode case?
     2261            ic = lineedit_read_key(read_key_buffer);
     2262            if (errno) /* error */
     2263                goto return_error_indicator;
     2264            if (ic < ' ' || ic > 255) {
    15852265                beep();
    1586             else {
    1587                 *(command + cursor) = c;
    1588                 putchar(c);
    1589                 putchar('\b');
     2266            } else {
     2267                command_ps[cursor] = ic;
     2268                bb_putchar(ic);
     2269                bb_putchar('\b');
    15902270            }
    15912271            break;
    1592 #endif /* FEATURE_COMMAND_EDITING_VI */
    1593 
    15942272        case '\x1b': /* ESC */
    1595 
    1596 #if ENABLE_FEATURE_EDITING_VI
    15972273            if (state->flags & VI_MODE) {
    1598                 /* ESC: insert mode --> command mode */
     2274                /* insert mode --> command mode */
    15992275                vi_cmdmode = 1;
    16002276                input_backward(1);
     2277            }
     2278            break;
     2279#endif /* FEATURE_COMMAND_EDITING_VI */
     2280
     2281#if MAX_HISTORY > 0
     2282        case KEYCODE_UP:
     2283            if (get_previous_history())
     2284                goto rewrite_line;
     2285            beep();
     2286            break;
     2287        case KEYCODE_DOWN:
     2288            if (!get_next_history())
     2289                break;
     2290 rewrite_line:
     2291            /* Rewrite the line with the selected history item */
     2292            /* change command */
     2293            command_len = load_string(state->history[state->cur_history] ?
     2294                    state->history[state->cur_history] : "", maxsize);
     2295            /* redraw and go to eol (bol, in vi) */
     2296            redraw(cmdedit_y, (state->flags & VI_MODE) ? 9999 : 0);
     2297            break;
     2298#endif
     2299        case KEYCODE_RIGHT:
     2300            input_forward();
     2301            break;
     2302        case KEYCODE_LEFT:
     2303            input_backward(1);
     2304            break;
     2305        case KEYCODE_CTRL_LEFT:
     2306            ctrl_left();
     2307            break;
     2308        case KEYCODE_CTRL_RIGHT:
     2309            ctrl_right();
     2310            break;
     2311        case KEYCODE_HOME:
     2312            input_backward(cursor);
     2313            break;
     2314        case KEYCODE_END:
     2315            put_till_end_and_adv_cursor();
     2316            break;
     2317
     2318        default:
     2319            if (initial_settings.c_cc[VINTR] != 0
     2320             && ic_raw == initial_settings.c_cc[VINTR]
     2321            ) {
     2322                /* Ctrl-C (usually) - stop gathering input */
     2323                goto_new_line();
     2324                command_len = 0;
     2325                break_out = -1; /* "do not append '\n'" */
    16012326                break;
    16022327            }
    1603 #endif
    1604             /* escape sequence follows */
    1605             if (safe_read(0, &c, 1) < 1)
    1606                 goto prepare_to_die;
    1607             /* different vt100 emulations */
    1608             if (c == '[' || c == 'O') {
    1609         vi_case('['|vbit:)
    1610         vi_case('O'|vbit:)
    1611                 if (safe_read(0, &c, 1) < 1)
    1612                     goto prepare_to_die;
    1613             }
    1614             if (c >= '1' && c <= '9') {
    1615                 unsigned char dummy;
    1616 
    1617                 if (safe_read(0, &dummy, 1) < 1)
    1618                     goto prepare_to_die;
    1619                 if (dummy != '~')
    1620                     c = '\0';
    1621             }
    1622 
    1623             switch (c) {
    1624 #if ENABLE_FEATURE_TAB_COMPLETION
    1625             case '\t':                      /* Alt-Tab */
    1626                 input_tab(&lastWasTab);
    1627                 break;
    1628 #endif
    1629 #if MAX_HISTORY > 0
    1630             case 'A':
    1631                 /* Up Arrow -- Get previous command from history */
    1632                 if ((state->flags & DO_HISTORY) && state->cur_history > 0) {
    1633                     get_previous_history();
    1634                     goto rewrite_line;
     2328            if (initial_settings.c_cc[VEOF] != 0
     2329             && ic_raw == initial_settings.c_cc[VEOF]
     2330            ) {
     2331                /* Ctrl-D (usually) - delete one character,
     2332                 * or exit if len=0 and no chars to delete */
     2333                if (command_len == 0) {
     2334                    errno = 0;
     2335
     2336        case -1: /* error (e.g. EIO when tty is destroyed) */
     2337 IF_FEATURE_EDITING_VI(return_error_indicator:)
     2338                    break_out = command_len = -1;
     2339                    break;
    16352340                }
    1636                 beep();
    1637                 break;
    1638             case 'B':
    1639                 /* Down Arrow -- Get next command in history */
    1640                 if (!get_next_history())
    1641                     break;
    1642  rewrite_line:
    1643                 /* Rewrite the line with the selected history item */
    1644                 /* change command */
    1645                 command_len = strlen(strcpy(command, state->history[state->cur_history]));
    1646                 /* redraw and go to eol (bol, in vi */
    1647                 redraw(cmdedit_y, (state->flags & VI_MODE) ? 9999 : 0);
    1648                 break;
    1649 #endif
    1650             case 'C':
    1651                 /* Right Arrow -- Move forward one character */
    1652                 input_forward();
    1653                 break;
    1654             case 'D':
    1655                 /* Left Arrow -- Move back one character */
    1656                 input_backward(1);
    1657                 break;
    1658             case '3':
    1659                 /* Delete */
    16602341                input_delete(0);
    16612342                break;
    1662             case '1': // vt100? linux vt? or what?
    1663             case '7': // vt100? linux vt? or what?
    1664             case 'H': /* xterm's <Home> */
    1665                 input_backward(cursor);
     2343            }
     2344//          /* Control-V -- force insert of next char */
     2345//          if (c == CTRL('V')) {
     2346//              if (safe_read(STDIN_FILENO, &c, 1) < 1)
     2347//                  goto return_error_indicator;
     2348//              if (c == 0) {
     2349//                  beep();
     2350//                  break;
     2351//              }
     2352//          }
     2353            if (ic < ' '
     2354             || (!ENABLE_UNICODE_SUPPORT && ic >= 256)
     2355             || (ENABLE_UNICODE_SUPPORT && ic >= VI_CMDMODE_BIT)
     2356            ) {
     2357                /* If VI_CMDMODE_BIT is set, ic is >= 256
     2358                 * and vi mode ignores unexpected chars.
     2359                 * Otherwise, we are here if ic is a
     2360                 * control char or an unhandled ESC sequence,
     2361                 * which is also ignored.
     2362                 */
    16662363                break;
    1667             case '4': // vt100? linux vt? or what?
    1668             case '8': // vt100? linux vt? or what?
    1669             case 'F': /* xterm's <End> */
    1670                 input_end();
     2364            }
     2365            if ((int)command_len >= (maxsize - 2)) {
     2366                /* Not enough space for the char and EOL */
    16712367                break;
    1672             default:
    1673                 c = '\0';
    1674                 beep();
    16752368            }
    1676             break;
    1677 
    1678         default:        /* If it's regular input, do the normal thing */
    1679 #if ENABLE_FEATURE_NONPRINTABLE_INVERSE_PUT
    1680             /* Control-V -- Add non-printable symbol */
    1681             if (c == CTRL('V')) {
    1682                 if (safe_read(0, &c, 1) < 1)
    1683                     goto prepare_to_die;
    1684                 if (c == 0) {
    1685                     beep();
    1686                     break;
    1687                 }
    1688             } else
    1689 #endif
    1690 
    1691 #if ENABLE_FEATURE_EDITING_VI
    1692             if (vi_cmdmode)  /* Don't self-insert */
    1693                 break;
    1694 #endif
    1695             if (!Isprint(c)) /* Skip non-printable characters */
    1696                 break;
    1697 
    1698             if (command_len >= (maxsize - 2))        /* Need to leave space for enter */
    1699                 break;
    17002369
    17012370            command_len++;
    1702             if (cursor == (command_len - 1)) {      /* Append if at the end of the line */
    1703                 command[cursor] = c;
    1704                 command[cursor+1] = '\0';
    1705                 cmdedit_set_out_char(' ');
    1706             } else {                        /* Insert otherwise */
     2371            if (cursor == (command_len - 1)) {
     2372                /* We are at the end, append */
     2373                command_ps[cursor] = ic;
     2374                command_ps[cursor + 1] = BB_NUL;
     2375                put_cur_glyph_and_inc_cursor();
     2376                if (unicode_bidi_isrtl(ic))
     2377                    input_backward(1);
     2378            } else {
     2379                /* In the middle, insert */
    17072380                int sc = cursor;
    17082381
    1709                 memmove(command + sc + 1, command + sc, command_len - sc);
    1710                 command[sc] = c;
    1711                 sc++;
    1712                 /* rewrite from cursor */
    1713                 input_end();
     2382                memmove(command_ps + sc + 1, command_ps + sc,
     2383                    (command_len - sc) * sizeof(command_ps[0]));
     2384                command_ps[sc] = ic;
     2385                /* is right-to-left char, or neutral one (e.g. comma) was just added to rtl text? */
     2386                if (!isrtl_str())
     2387                    sc++; /* no */
     2388                put_till_end_and_adv_cursor();
    17142389                /* to prev x pos + 1 */
    17152390                input_backward(cursor - sc);
    17162391            }
    17172392            break;
    1718         }
    1719         if (break_out)                  /* Enter is the command terminator, no more input. */
    1720             break;
    1721 
    1722         if (c != '\t')
    1723             lastWasTab = FALSE;
    1724     }
     2393        } /* switch (ic) */
     2394
     2395        if (break_out)
     2396            break;
     2397
     2398#if ENABLE_FEATURE_TAB_COMPLETION
     2399        if (ic_raw != '\t')
     2400            lastWasTab = 0;
     2401#endif
     2402    } /* while (1) */
     2403
     2404#if ENABLE_FEATURE_EDITING_ASK_TERMINAL
     2405    if (S.sent_ESC_br6n) {
     2406        /* "sleep 1; busybox ash" + hold [Enter] to trigger.
     2407         * We sent "ESC [ 6 n", but got '\n' first, and
     2408         * KEYCODE_CURSOR_POS response is now buffered from terminal.
     2409         * It's bad already and not much can be done with it
     2410         * (it _will_ be visible for the next process to read stdin),
     2411         * but without this delay it even shows up on the screen
     2412         * as garbage because we restore echo settings with tcsetattr
     2413         * before it comes in. UGLY!
     2414         */
     2415        usleep(20*1000);
     2416    }
     2417#endif
     2418
     2419/* End of bug-catching "command_must_not_be_used" trick */
     2420#undef command
     2421
     2422#if ENABLE_UNICODE_SUPPORT
     2423    command[0] = '\0';
     2424    if (command_len > 0)
     2425        command_len = save_string(command, maxsize - 1);
     2426    free(command_ps);
     2427#endif
    17252428
    17262429    if (command_len > 0)
     
    17322435    }
    17332436
    1734 #if ENABLE_FEATURE_CLEAN_UP && ENABLE_FEATURE_TAB_COMPLETION
     2437#if ENABLE_FEATURE_TAB_COMPLETION
    17352438    free_tab_completion_data();
    17362439#endif
    17372440
    1738 #if ENABLE_FEATURE_EDITING_FANCY_PROMPT
    1739     free((char*)cmdedit_prompt);
    1740 #endif
    17412441    /* restore initial_settings */
    1742     setTermSettings(STDIN_FILENO, (void *) &initial_settings);
     2442    tcsetattr_stdin_TCSANOW(&initial_settings);
    17432443    /* restore SIGWINCH handler */
    17442444    signal(SIGWINCH, previous_SIGWINCH_handler);
    1745     fflush(stdout);
    1746     return command_len;
    1747 }
    1748 
    1749 line_input_t *new_line_input_t(int flags)
    1750 {
    1751     line_input_t *n = xzalloc(sizeof(*n));
    1752     n->flags = flags;
    1753     return n;
    1754 }
    1755 
    1756 #else
     2445    fflush_all();
     2446
     2447    len = command_len;
     2448    DEINIT_S();
     2449
     2450    return len; /* can't return command_len, DEINIT_S() destroys it */
     2451}
     2452
     2453#else  /* !FEATURE_EDITING */
    17572454
    17582455#undef read_line_input
    1759 int read_line_input(const char* prompt, char* command, int maxsize)
     2456int FAST_FUNC read_line_input(const char* prompt, char* command, int maxsize)
    17602457{
    17612458    fputs(prompt, stdout);
    1762     fflush(stdout);
     2459    fflush_all();
    17632460    fgets(command, maxsize, stdin);
    17642461    return strlen(command);
    17652462}
    17662463
    1767 #endif  /* FEATURE_COMMAND_EDITING */
     2464#endif  /* !FEATURE_EDITING */
    17682465
    17692466
     
    17902487#endif
    17912488
    1792 #if ENABLE_FEATURE_NONPRINTABLE_INVERSE_PUT
    1793     setlocale(LC_ALL, "");
    1794 #endif
    17952489    while (1) {
    17962490        int l;
     
    17982492        if (l <= 0 || buff[l-1] != '\n')
    17992493            break;
    1800         buff[l-1] = 0;
     2494        buff[l-1] = '\0';
    18012495        printf("*** read_line_input() returned line =%s=\n", buff);
    18022496    }
Note: See TracChangeset for help on using the changeset viewer.