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