Magellan Linux

Diff of /trunk/mkinitrd-magellan/busybox/libbb/lineedit.c

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

revision 983 by niro, Fri Apr 24 18:33:46 2009 UTC revision 984 by niro, Sun May 30 11:32:42 2010 UTC
# Line 16  Line 16 
16    
17  /*  /*
18   * Usage and known bugs:   * Usage and known bugs:
19   * Terminal key codes are not extensive, and more will probably   * Terminal key codes are not extensive, more needs to be added.
20   * need to be added. This version was created on Debian GNU/Linux 2.x.   * This version was created on Debian GNU/Linux 2.x.
21   * Delete, Backspace, Home, End, and the arrow keys were tested   * Delete, Backspace, Home, End, and the arrow keys were tested
22   * to work in an Xterm and console. Ctrl-A also works as Home.   * to work in an Xterm and console. Ctrl-A also works as Home.
23   * Ctrl-E also works as End.   * 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   * lineedit does not know that the terminal escape sequences do not
32   * take up space on the screen. The redisplay code assumes, unless   * take up space on the screen. The redisplay code assumes, unless
33   * told otherwise, that each character in the prompt is a printable   * told otherwise, that each character in the prompt is a printable
# Line 33  Line 39 
39   *   *
40   * PS1='\[\033[01;32m\]\u@\h\[\033[01;34m\] \w \$\[\033[00m\] '   * PS1='\[\033[01;32m\]\u@\h\[\033[01;34m\] \w \$\[\033[00m\] '
41   */   */
   
42  #include "libbb.h"  #include "libbb.h"
43    #include "unicode.h"
44    
45  /* FIXME: obsolete CONFIG item? */  /* FIXME: obsolete CONFIG item? */
46  #define ENABLE_FEATURE_NONPRINTABLE_INVERSE_PUT 0  #define ENABLE_FEATURE_NONPRINTABLE_INVERSE_PUT 0
47    
   
48  #ifdef TEST  #ifdef TEST
49    # define ENABLE_FEATURE_EDITING 0
50  #define ENABLE_FEATURE_EDITING 0  # define ENABLE_FEATURE_TAB_COMPLETION 0
51  #define ENABLE_FEATURE_TAB_COMPLETION 0  # define ENABLE_FEATURE_USERNAME_COMPLETION 0
52  #define ENABLE_FEATURE_USERNAME_COMPLETION 0  # define ENABLE_FEATURE_NONPRINTABLE_INVERSE_PUT 0
53  #define ENABLE_FEATURE_NONPRINTABLE_INVERSE_PUT 0  #endif
   
 #endif  /* TEST */  
54    
55    
56  /* Entire file (except TESTing part) sits inside this #if */  /* Entire file (except TESTing part) sits inside this #if */
57  #if ENABLE_FEATURE_EDITING  #if ENABLE_FEATURE_EDITING
58    
 #if ENABLE_LOCALE_SUPPORT  
 #define Isprint(c) isprint(c)  
 #else  
 #define Isprint(c) ((c) >= ' ' && (c) != ((unsigned char)'\233'))  
 #endif  
59    
60  #define ENABLE_FEATURE_GETUSERNAME_AND_HOMEDIR \  #define ENABLE_FEATURE_GETUSERNAME_AND_HOMEDIR \
61   (ENABLE_FEATURE_USERNAME_COMPLETION || ENABLE_FEATURE_EDITING_FANCY_PROMPT)   (ENABLE_FEATURE_USERNAME_COMPLETION || ENABLE_FEATURE_EDITING_FANCY_PROMPT)
62  #define USE_FEATURE_GETUSERNAME_AND_HOMEDIR(...)  #define IF_FEATURE_GETUSERNAME_AND_HOMEDIR(...)
63  #if ENABLE_FEATURE_GETUSERNAME_AND_HOMEDIR  #if ENABLE_FEATURE_GETUSERNAME_AND_HOMEDIR
64  #undef USE_FEATURE_GETUSERNAME_AND_HOMEDIR  #undef IF_FEATURE_GETUSERNAME_AND_HOMEDIR
65  #define USE_FEATURE_GETUSERNAME_AND_HOMEDIR(...) __VA_ARGS__  #define IF_FEATURE_GETUSERNAME_AND_HOMEDIR(...) __VA_ARGS__
66  #endif  #endif
67    
68    
69    #undef CHAR_T
70    #if ENABLE_FEATURE_ASSUME_UNICODE
71    # define BB_NUL L'\0'
72    # define CHAR_T wchar_t
73    static bool BB_isspace(CHAR_T c) { return ((unsigned)c < 256 && isspace(c)); }
74    # if ENABLE_FEATURE_EDITING_VI
75    static bool BB_isalnum(CHAR_T c) { return ((unsigned)c < 256 && isalnum(c)); }
76    # endif
77    static 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    
94    
95  enum {  enum {
96   /* We use int16_t for positions, need to limit line len */   /* We use int16_t for positions, need to limit line len */
97   MAX_LINELEN = CONFIG_FEATURE_EDITING_MAX_LEN < 0x7ff0   MAX_LINELEN = CONFIG_FEATURE_EDITING_MAX_LEN < 0x7ff0
# Line 86  struct lineedit_statics { Line 110  struct lineedit_statics {
110   volatile unsigned cmdedit_termw; /* = 80; */ /* actual terminal width */   volatile unsigned cmdedit_termw; /* = 80; */ /* actual terminal width */
111   sighandler_t previous_SIGWINCH_handler;   sighandler_t previous_SIGWINCH_handler;
112    
113   unsigned cmdedit_x;        /* real x terminal position */   unsigned cmdedit_x;        /* real x (col) terminal position */
114   unsigned cmdedit_y;        /* pseudoreal y terminal position */   unsigned cmdedit_y;        /* pseudoreal y (row) terminal position */
115   unsigned cmdedit_prmt_len; /* length of prompt (without colors etc) */   unsigned cmdedit_prmt_len; /* length of prompt (without colors etc) */
116    
117   unsigned cursor;   unsigned cursor;
118   unsigned command_len;   int command_len; /* must be signed */
119   char *command_ps;   /* signed maxsize: we want x in "if (x > S.maxsize)"
120     * to _not_ be promoted to unsigned */
121     int maxsize;
122     CHAR_T *command_ps;
123    
124   const char *cmdedit_prompt;   const char *cmdedit_prompt;
125  #if ENABLE_FEATURE_EDITING_FANCY_PROMPT  #if ENABLE_FEATURE_EDITING_FANCY_PROMPT
# Line 111  struct lineedit_statics { Line 138  struct lineedit_statics {
138    
139  #if ENABLE_FEATURE_EDITING_VI  #if ENABLE_FEATURE_EDITING_VI
140  #define DELBUFSIZ 128  #define DELBUFSIZ 128
141   char *delptr;   CHAR_T *delptr;
142   smallint newdelflag;     /* whether delbuf should be reused yet */   smallint newdelflag;     /* whether delbuf should be reused yet */
143   char delbuf[DELBUFSIZ];  /* a place to store deleted characters */   CHAR_T delbuf[DELBUFSIZ];  /* a place to store deleted characters */
144    #endif
145    #if ENABLE_FEATURE_EDITING_ASK_TERMINAL
146     smallint sent_ESC_br6n;
147  #endif  #endif
148    
149   /* Formerly these were big buffers on stack: */   /* Formerly these were big buffers on stack: */
# Line 152  extern struct lineedit_statics *const li Line 182  extern struct lineedit_statics *const li
182   (*(struct lineedit_statics**)&lineedit_ptr_to_statics) = xzalloc(sizeof(S)); \   (*(struct lineedit_statics**)&lineedit_ptr_to_statics) = xzalloc(sizeof(S)); \
183   barrier(); \   barrier(); \
184   cmdedit_termw = 80; \   cmdedit_termw = 80; \
185   USE_FEATURE_EDITING_FANCY_PROMPT(num_ok_lines = 1;) \   IF_FEATURE_EDITING_FANCY_PROMPT(num_ok_lines = 1;) \
186   USE_FEATURE_GETUSERNAME_AND_HOMEDIR(home_pwd_buf = (char*)null_str;) \   IF_FEATURE_GETUSERNAME_AND_HOMEDIR(home_pwd_buf = (char*)null_str;) \
187  } while (0)  } while (0)
188  static void deinit_S(void)  static void deinit_S(void)
189  {  {
# Line 172  static void deinit_S(void) Line 202  static void deinit_S(void)
202  #define DEINIT_S() deinit_S()  #define DEINIT_S() deinit_S()
203    
204    
205    #if ENABLE_FEATURE_ASSUME_UNICODE
206    static size_t load_string(const char *src, int maxsize)
207    {
208     ssize_t len = mbstowcs(command_ps, src, maxsize - 1);
209     if (len < 0)
210     len = 0;
211     command_ps[len] = L'\0';
212     return len;
213    }
214    static size_t save_string(char *dst, int maxsize)
215    {
216     ssize_t len = wcstombs(dst, command_ps, maxsize - 1);
217     if (len < 0)
218     len = 0;
219     dst[len] = '\0';
220     return len;
221    }
222    /* I thought just fputwc(c, stdout) would work. But no... */
223    static void BB_PUTCHAR(wchar_t c)
224    {
225     char buf[MB_CUR_MAX + 1];
226     mbstate_t mbst = { 0 };
227     ssize_t len = wcrtomb(buf, c, &mbst);
228    
229     if (len > 0) {
230     buf[len] = '\0';
231     fputs(buf, stdout);
232     }
233    }
234    #else
235    static size_t load_string(const char *src, int maxsize)
236    {
237     safe_strncpy(command_ps, src, maxsize);
238     return strlen(command_ps);
239    }
240    # if ENABLE_FEATURE_TAB_COMPLETION
241    static void save_string(char *dst, int maxsize)
242    {
243     safe_strncpy(dst, command_ps, maxsize);
244    }
245    # endif
246    # define BB_PUTCHAR(c) bb_putchar(c)
247    #endif
248    
249    
250  /* Put 'command_ps[cursor]', cursor++.  /* Put 'command_ps[cursor]', cursor++.
251   * Advance cursor on screen. If we reached right margin, scroll text up   * Advance cursor on screen. If we reached right margin, scroll text up
252   * and remove terminal margin effect by printing 'next_char' */   * and remove terminal margin effect by printing 'next_char' */
# Line 183  static void cmdedit_set_out_char(void) Line 258  static void cmdedit_set_out_char(void)
258  static void cmdedit_set_out_char(int next_char)  static void cmdedit_set_out_char(int next_char)
259  #endif  #endif
260  {  {
261   int c = (unsigned char)command_ps[cursor];   CHAR_T c = command_ps[cursor];
262    
263   if (c == '\0') {   if (c == BB_NUL) {
264   /* erase character after end of input string */   /* erase character after end of input string */
265   c = ' ';   c = ' ';
266   }   }
267  #if ENABLE_FEATURE_NONPRINTABLE_INVERSE_PUT  #if ENABLE_FEATURE_NONPRINTABLE_INVERSE_PUT
268   /* Display non-printable characters in reverse */   /* Display non-printable characters in reverse */
269   if (!Isprint(c)) {   if (!BB_isprint(c)) {
270   if (c >= 128)   if (c >= 128)
271   c -= 128;   c -= 128;
272   if (c < ' ')   if (c < ' ')
# Line 202  static void cmdedit_set_out_char(int nex Line 277  static void cmdedit_set_out_char(int nex
277   } else   } else
278  #endif  #endif
279   {   {
280   bb_putchar(c);   BB_PUTCHAR(c);
281   }   }
282   if (++cmdedit_x >= cmdedit_termw) {   if (++cmdedit_x >= cmdedit_termw) {
283   /* terminal is scrolled down */   /* terminal is scrolled down */
# Line 224  static void cmdedit_set_out_char(int nex Line 299  static void cmdedit_set_out_char(int nex
299   bb_putchar('\b');   bb_putchar('\b');
300  #endif  #endif
301   }   }
302  // Huh? What if command_ps[cursor] == '\0' (we are at the end already?)  // Huh? What if command_ps[cursor] == BB_NUL (we are at the end already?)
303   cursor++;   cursor++;
304  }  }
305    
# Line 299  static void input_backward(unsigned num) Line 374  static void input_backward(unsigned num)
374    
375  static void put_prompt(void)  static void put_prompt(void)
376  {  {
377     unsigned w;
378    
379   out1str(cmdedit_prompt);   out1str(cmdedit_prompt);
380     fflush_all();
381   cursor = 0;   cursor = 0;
382   {   w = cmdedit_termw; /* read volatile var once */
383   unsigned w = cmdedit_termw; /* volatile var */   cmdedit_y = cmdedit_prmt_len / w; /* new quasireal y */
384   cmdedit_y = cmdedit_prmt_len / w; /* new quasireal y */   cmdedit_x = cmdedit_prmt_len % w;
  cmdedit_x = cmdedit_prmt_len % w;  
  }  
385  }  }
386    
387  /* draw prompt, editor line, and clear tail */  /* draw prompt, editor line, and clear tail */
388  static void redraw(int y, int back_cursor)  static void redraw(int y, int back_cursor)
389  {  {
390   if (y > 0)                              /* up to start y */   if (y > 0)  /* up to start y */
391   printf("\033[%dA", y);   printf("\033[%uA", y);
392   bb_putchar('\r');   bb_putchar('\r');
393   put_prompt();   put_prompt();
394   input_end();                            /* rewrite */   input_end();      /* rewrite */
395   printf("\033[J");                       /* erase after cursor */   printf("\033[J"); /* erase after cursor */
396   input_backward(back_cursor);   input_backward(back_cursor);
397  }  }
398    
# Line 345  static void input_delete(int save) Line 421  static void input_delete(int save)
421   }   }
422  #endif  #endif
423    
424   overlapping_strcpy(command_ps + j, command_ps + j + 1);   memmove(command_ps + j, command_ps + j + 1,
425     /* (command_len + 1 [because of NUL]) - (j + 1)
426     * simplified into (command_len - j) */
427     (command_len - j) * sizeof(command_ps[0]));
428   command_len--;   command_len--;
429   input_end();                    /* rewrite new line */   input_end();                    /* rewrite new line */
430   cmdedit_set_out_char(' ');      /* erase char */   cmdedit_set_out_char(' ');      /* erase char */
# Line 362  static void put(void) Line 441  static void put(void)
441   return;   return;
442   ocursor = cursor;   ocursor = cursor;
443   /* open hole and then fill it */   /* open hole and then fill it */
444   memmove(command_ps + cursor + j, command_ps + cursor, command_len - cursor + 1);   memmove(command_ps + cursor + j, command_ps + cursor,
445   strncpy(command_ps + cursor, delbuf, j);   (command_len - cursor + 1) * sizeof(command_ps[0]));
446     memcpy(command_ps + cursor, delbuf, j * sizeof(command_ps[0]));
447   command_len += j;   command_len += j;
448   input_end();                    /* rewrite new line */   input_end();                    /* rewrite new line */
449   input_backward(cursor - ocursor - j + 1); /* at end of new text */   input_backward(cursor - ocursor - j + 1); /* at end of new text */
# Line 600  static void exe_n_cwd_tab_completion(cha Line 680  static void exe_n_cwd_tab_completion(cha
680  #undef dirbuf  #undef dirbuf
681  }  }
682    
683    /* QUOT is used on elements of int_buf[], which are bytes,
684     * not Unicode chars. Therefore it works correctly even in Unicode mode.
685     */
686  #define QUOT (UCHAR_MAX+1)  #define QUOT (UCHAR_MAX+1)
687    
688  #define collapse_pos(is, in) do { \  #define int_buf (S.find_match__int_buf)
689   memmove(int_buf+(is), int_buf+(in), (MAX_LINELEN+1-(is)-(in)) * sizeof(pos_buf[0])); \  #define pos_buf (S.find_match__pos_buf)
690   memmove(pos_buf+(is), pos_buf+(in), (MAX_LINELEN+1-(is)-(in)) * sizeof(pos_buf[0])); \  /* is must be <= in */
691  } while (0)  static void collapse_pos(int is, int in)
692    {
693  static int find_match(char *matchBuf, int *len_with_quotes)   memmove(int_buf+is, int_buf+in, (MAX_LINELEN+1-in)*sizeof(int_buf[0]));
694     memmove(pos_buf+is, pos_buf+in, (MAX_LINELEN+1-in)*sizeof(pos_buf[0]));
695    }
696    static NOINLINE int find_match(char *matchBuf, int *len_with_quotes)
697  {  {
698   int i, j;   int i, j;
699   int command_mode;   int command_mode;
700   int c, c2;   int c, c2;
701    /* Were local, but it uses too much stack */
702  /* int16_t int_buf[MAX_LINELEN + 1]; */  /* int16_t int_buf[MAX_LINELEN + 1]; */
703  /* int16_t pos_buf[MAX_LINELEN + 1]; */  /* int16_t pos_buf[MAX_LINELEN + 1]; */
 #define int_buf (S.find_match__int_buf)  
 #define pos_buf (S.find_match__pos_buf)  
704    
705   /* set to integer dimension characters and own positions */   /* set to integer dimension characters and own positions */
706   for (i = 0;; i++) {   for (i = 0;; i++) {
707   int_buf[i] = (unsigned char)matchBuf[i];   int_buf[i] = (unsigned char)matchBuf[i];
708   if (int_buf[i] == 0) {   if (int_buf[i] == 0) {
709   pos_buf[i] = -1;        /* indicator end line */   pos_buf[i] = -1; /* end-fo-line indicator */
710   break;   break;
711   }   }
712   pos_buf[i] = i;   pos_buf[i] = i;
# Line 634  static int find_match(char *matchBuf, in Line 719  static int find_match(char *matchBuf, in
719   int_buf[j] |= QUOT;   int_buf[j] |= QUOT;
720   i++;   i++;
721  #if ENABLE_FEATURE_NONPRINTABLE_INVERSE_PUT  #if ENABLE_FEATURE_NONPRINTABLE_INVERSE_PUT
722   if (matchBuf[i] == '\t')        /* algorithm equivalent */   if (matchBuf[i] == '\t')  /* algorithm equivalent */
723   int_buf[j] = ' ' | QUOT;   int_buf[j] = ' ' | QUOT;
724  #endif  #endif
725   }   }
# Line 677  static int find_match(char *matchBuf, in Line 762  static int find_match(char *matchBuf, in
762   }   }
763   if (command_mode) {   if (command_mode) {
764   collapse_pos(0, i + command_mode);   collapse_pos(0, i + command_mode);
765   i = -1;                         /* hack incremet */   i = -1;  /* hack incremet */
766   }   }
767   }   }
768   /* collapse `command...` */   /* collapse `command...` */
769   for (i = 0; int_buf[i]; i++)   for (i = 0; int_buf[i]; i++) {
770   if (int_buf[i] == '`') {   if (int_buf[i] == '`') {
771   for (j = i + 1; int_buf[j]; j++)   for (j = i + 1; int_buf[j]; j++)
772   if (int_buf[j] == '`') {   if (int_buf[j] == '`') {
# Line 690  static int find_match(char *matchBuf, in Line 775  static int find_match(char *matchBuf, in
775   break;   break;
776   }   }
777   if (j) {   if (j) {
778   /* not found close ` - command mode, collapse all previous */   /* not found closing ` - command mode, collapse all previous */
779   collapse_pos(0, i + 1);   collapse_pos(0, i + 1);
780   break;   break;
781   } else   } else
782   i--;                    /* hack incremet */   i--;  /* hack incremet */
783   }   }
784     }
785    
786   /* collapse (command...(command...)...) or {command...{command...}...} */   /* collapse (command...(command...)...) or {command...{command...}...} */
787   c = 0;                                          /* "recursive" level */   c = 0;  /* "recursive" level */
788   c2 = 0;   c2 = 0;
789   for (i = 0; int_buf[i]; i++)   for (i = 0; int_buf[i]; i++) {
790   if (int_buf[i] == '(' || int_buf[i] == '{') {   if (int_buf[i] == '(' || int_buf[i] == '{') {
791   if (int_buf[i] == '(')   if (int_buf[i] == '(')
792   c++;   c++;
793   else   else
794   c2++;   c2++;
795   collapse_pos(0, i + 1);   collapse_pos(0, i + 1);
796   i = -1;                         /* hack incremet */   i = -1;  /* hack incremet */
797   }   }
798   for (i = 0; pos_buf[i] >= 0 && (c > 0 || c2 > 0); i++)   }
799     for (i = 0; pos_buf[i] >= 0 && (c > 0 || c2 > 0); i++) {
800   if ((int_buf[i] == ')' && c > 0) || (int_buf[i] == '}' && c2 > 0)) {   if ((int_buf[i] == ')' && c > 0) || (int_buf[i] == '}' && c2 > 0)) {
801   if (int_buf[i] == ')')   if (int_buf[i] == ')')
802   c--;   c--;
803   else   else
804   c2--;   c2--;
805   collapse_pos(0, i + 1);   collapse_pos(0, i + 1);
806   i = -1;                         /* hack incremet */   i = -1;  /* hack incremet */
807   }   }
808     }
809    
810   /* skip first not quote space */   /* skip first not quote space */
811   for (i = 0; int_buf[i]; i++)   for (i = 0; int_buf[i]; i++)
# Line 728  static int find_match(char *matchBuf, in Line 816  static int find_match(char *matchBuf, in
816    
817   /* set find mode for completion */   /* set find mode for completion */
818   command_mode = FIND_EXE_ONLY;   command_mode = FIND_EXE_ONLY;
819   for (i = 0; int_buf[i]; i++)   for (i = 0; int_buf[i]; i++) {
820   if (int_buf[i] == ' ' || int_buf[i] == '<' || int_buf[i] == '>') {   if (int_buf[i] == ' ' || int_buf[i] == '<' || int_buf[i] == '>') {
821   if (int_buf[i] == ' ' && command_mode == FIND_EXE_ONLY   if (int_buf[i] == ' ' && command_mode == FIND_EXE_ONLY
822   && matchBuf[pos_buf[0]] == 'c'   && matchBuf[pos_buf[0]] == 'c'
# Line 740  static int find_match(char *matchBuf, in Line 828  static int find_match(char *matchBuf, in
828   break;   break;
829   }   }
830   }   }
831     }
832   for (i = 0; int_buf[i]; i++)   for (i = 0; int_buf[i]; i++)
833   /* "strlen" */;   /* "strlen" */;
834   /* find last word */   /* find last word */
# Line 771  static int find_match(char *matchBuf, in Line 860  static int find_match(char *matchBuf, in
860   *len_with_quotes = j ? j - pos_buf[0] : 0;   *len_with_quotes = j ? j - pos_buf[0] : 0;
861    
862   return command_mode;   return command_mode;
863    }
864  #undef int_buf  #undef int_buf
865  #undef pos_buf  #undef pos_buf
 }  
866    
867  /*  /*
868   * display by column (original idea from ls applet,   * display by column (original idea from ls applet,
# Line 787  static void showfiles(void) Line 876  static void showfiles(void)
876   int nrows = nfiles;   int nrows = nfiles;
877   int l;   int l;
878    
879   /* find the longest file name-  use that as the column width */   /* find the longest file name - use that as the column width */
880   for (row = 0; row < nrows; row++) {   for (row = 0; row < nrows; row++) {
881   l = strlen(matches[row]);   l = unicode_strlen(matches[row]);
882   if (column_width < l)   if (column_width < l)
883   column_width = l;   column_width = l;
884   }   }
# Line 809  static void showfiles(void) Line 898  static void showfiles(void)
898    
899   for (nc = 1; nc < ncols && n+nrows < nfiles; n += nrows, nc++) {   for (nc = 1; nc < ncols && n+nrows < nfiles; n += nrows, nc++) {
900   printf("%s%-*s", matches[n],   printf("%s%-*s", matches[n],
901   (int)(column_width - strlen(matches[n])), "");   (int)(column_width - unicode_strlen(matches[n])), ""
902     );
903   }   }
904   puts(matches[n]);   puts(matches[n]);
905   }   }
# Line 818  static void showfiles(void) Line 908  static void showfiles(void)
908  static char *add_quote_for_spec_chars(char *found)  static char *add_quote_for_spec_chars(char *found)
909  {  {
910   int l = 0;   int l = 0;
911   char *s = xmalloc((strlen(found) + 1) * 2);   char *s = xzalloc((strlen(found) + 1) * 2);
912    
913   while (*found) {   while (*found) {
914   if (strchr(" `\"#$%^&*()=+{}[]:;\'|\\<>", *found))   if (strchr(" `\"#$%^&*()=+{}[]:;'|\\<>", *found))
915   s[l++] = '\\';   s[l++] = '\\';
916   s[l++] = *found++;   s[l++] = *found++;
917   }   }
918   s[l] = 0;   /* s[l] = '\0'; - already is */
919   return s;   return s;
920  }  }
921    
# Line 842  static void input_tab(smallint *lastWasT Line 932  static void input_tab(smallint *lastWasT
932  #define matchBuf (S.input_tab__matchBuf)  #define matchBuf (S.input_tab__matchBuf)
933   int find_type;   int find_type;
934   int recalc_pos;   int recalc_pos;
935    #if ENABLE_FEATURE_ASSUME_UNICODE
936     /* cursor pos in command converted to multibyte form */
937     int cursor_mb;
938    #endif
939    
940   *lastWasTab = TRUE;             /* flop trigger */   *lastWasTab = TRUE;             /* flop trigger */
941    
942   /* Make a local copy of the string -- up   /* Make a local copy of the string --
943   * to the position of the cursor */   * up to the position of the cursor */
944   tmp = strncpy(matchBuf, command_ps, cursor);   save_string(matchBuf, cursor + 1);
945   tmp[cursor] = '\0';  #if ENABLE_FEATURE_ASSUME_UNICODE
946     cursor_mb = strlen(matchBuf);
947    #endif
948     tmp = matchBuf;
949    
950   find_type = find_match(matchBuf, &recalc_pos);   find_type = find_match(matchBuf, &recalc_pos);
951    
# Line 859  static void input_tab(smallint *lastWasT Line 956  static void input_tab(smallint *lastWasT
956   /* If the word starts with `~' and there is no slash in the word,   /* If the word starts with `~' and there is no slash in the word,
957   * then try completing this word as a username. */   * then try completing this word as a username. */
958   if (state->flags & USERNAME_COMPLETION)   if (state->flags & USERNAME_COMPLETION)
959   if (matchBuf[0] == '~' && strchr(matchBuf, '/') == 0)   if (matchBuf[0] == '~' && strchr(matchBuf, '/') == NULL)
960   username_tab_completion(matchBuf, NULL);   username_tab_completion(matchBuf, NULL);
961  #endif  #endif
962   /* Try to match any executable in our path and everything   /* Try to match any executable in our path and everything
# Line 885  static void input_tab(smallint *lastWasT Line 982  static void input_tab(smallint *lastWasT
982   num_matches = n + 1;   num_matches = n + 1;
983   }   }
984   /* Did we find exactly one match? */   /* Did we find exactly one match? */
985   if (!matches || num_matches > 1) {   if (!matches || num_matches > 1) { /* no */
986   beep();   beep();
987   if (!matches)   if (!matches)
988   return;         /* not found */   return;         /* not found */
989   /* find minimal match */   /* find minimal match */
990   tmp1 = xstrdup(matches[0]);   tmp1 = xstrdup(matches[0]);
991   for (tmp = tmp1; *tmp; tmp++)   for (tmp = tmp1; *tmp; tmp++) {
992   for (len_found = 1; len_found < num_matches; len_found++)   for (len_found = 1; len_found < num_matches; len_found++) {
993   if (matches[len_found][(tmp - tmp1)] != *tmp) {   if (matches[len_found][tmp - tmp1] != *tmp) {
994   *tmp = '\0';   *tmp = '\0';
995   break;   break;
996   }   }
997     }
998     }
999   if (*tmp1 == '\0') {        /* have unique */   if (*tmp1 == '\0') {        /* have unique */
1000   free(tmp1);   free(tmp1);
1001   return;   return;
# Line 914  static void input_tab(smallint *lastWasT Line 1013  static void input_tab(smallint *lastWasT
1013   tmp[len_found+1] = '\0';   tmp[len_found+1] = '\0';
1014   }   }
1015   }   }
1016    
1017   len_found = strlen(tmp);   len_found = strlen(tmp);
1018   /* have space to placed match? */  #if !ENABLE_FEATURE_ASSUME_UNICODE
1019   if ((len_found - strlen(matchBuf) + command_len) < MAX_LINELEN) {   /* have space to place the match? */
1020   /* before word for match   */   /* The result consists of three parts with these lengths: */
1021   command_ps[cursor - recalc_pos] = '\0';   /* (cursor - recalc_pos) + len_found + (command_len - cursor) */
1022   /* save   tail line        */   /* it simplifies into: */
1023     if ((int)(len_found + command_len - recalc_pos) < S.maxsize) {
1024     /* save tail */
1025   strcpy(matchBuf, command_ps + cursor);   strcpy(matchBuf, command_ps + cursor);
1026   /* add    match            */   /* add match and tail */
1027   strcat(command_ps, tmp);   sprintf(&command_ps[cursor - recalc_pos], "%s%s", tmp, matchBuf);
  /* add    tail             */  
  strcat(command_ps, matchBuf);  
  /* back to begin word for match    */  
  input_backward(recalc_pos);  
  /* new pos                         */  
  recalc_pos = cursor + len_found;  
  /* new len                         */  
1028   command_len = strlen(command_ps);   command_len = strlen(command_ps);
1029   /* write out the matched command   */   /* new pos */
1030     recalc_pos = cursor - recalc_pos + len_found;
1031     /* write out the matched command */
1032   redraw(cmdedit_y, command_len - recalc_pos);   redraw(cmdedit_y, command_len - recalc_pos);
1033   }   }
1034    #else
1035     {
1036     char command[MAX_LINELEN];
1037     int len = save_string(command, sizeof(command));
1038     /* have space to place the match? */
1039     /* (cursor_mb - recalc_pos) + len_found + (len - cursor_mb) */
1040     if ((int)(len_found + len - recalc_pos) < MAX_LINELEN) {
1041     /* save tail */
1042     strcpy(matchBuf, command + cursor_mb);
1043     /* where do we want to have cursor after all? */
1044     strcpy(&command[cursor_mb - recalc_pos], tmp);
1045     len = load_string(command, S.maxsize);
1046     /* add match and tail */
1047     sprintf(&command[cursor_mb - recalc_pos], "%s%s", tmp, matchBuf);
1048     command_len = load_string(command, S.maxsize);
1049     /* write out the matched command */
1050     redraw(cmdedit_y, command_len - len);
1051     }
1052     }
1053    #endif
1054   free(tmp);   free(tmp);
1055  #undef matchBuf  #undef matchBuf
1056   } else {   } else {
# Line 941  static void input_tab(smallint *lastWasT Line 1058  static void input_tab(smallint *lastWasT
1058   * just hit TAB again, print a list of all the   * just hit TAB again, print a list of all the
1059   * available choices... */   * available choices... */
1060   if (matches && num_matches > 0) {   if (matches && num_matches > 0) {
1061   int sav_cursor = cursor;        /* change goto_new_line() */   /* changed by goto_new_line() */
1062     int sav_cursor = cursor;
1063    
1064   /* Go to the next line */   /* Go to the next line */
1065   goto_new_line();   goto_new_line();
# Line 954  static void input_tab(smallint *lastWasT Line 1072  static void input_tab(smallint *lastWasT
1072  #endif  /* FEATURE_COMMAND_TAB_COMPLETION */  #endif  /* FEATURE_COMMAND_TAB_COMPLETION */
1073    
1074    
1075    line_input_t* FAST_FUNC new_line_input_t(int flags)
1076    {
1077     line_input_t *n = xzalloc(sizeof(*n));
1078     n->flags = flags;
1079     return n;
1080    }
1081    
1082    
1083  #if MAX_HISTORY > 0  #if MAX_HISTORY > 0
1084    
1085  static void save_command_ps_at_cur_history(void)  static void save_command_ps_at_cur_history(void)
1086  {  {
1087   if (command_ps[0] != '\0') {   if (command_ps[0] != BB_NUL) {
1088   int cur = state->cur_history;   int cur = state->cur_history;
1089   free(state->history[cur]);   free(state->history[cur]);
1090    
1091    # if ENABLE_FEATURE_ASSUME_UNICODE
1092     {
1093     char tbuf[MAX_LINELEN];
1094     save_string(tbuf, sizeof(tbuf));
1095     state->history[cur] = xstrdup(tbuf);
1096     }
1097    # else
1098   state->history[cur] = xstrdup(command_ps);   state->history[cur] = xstrdup(command_ps);
1099    # endif
1100   }   }
1101  }  }
1102    
# Line 989  static int get_next_history(void) Line 1124  static int get_next_history(void)
1124   return 0;   return 0;
1125  }  }
1126    
1127  #if ENABLE_FEATURE_EDITING_SAVEHISTORY  # if ENABLE_FEATURE_EDITING_SAVEHISTORY
1128    /* We try to ensure that concurrent additions to the history
1129     * do not overwrite each other.
1130     * Otherwise shell users get unhappy.
1131     *
1132     * History file is trimmed lazily, when it grows several times longer
1133     * than configured MAX_HISTORY lines.
1134     */
1135    
1136    static void free_line_input_t(line_input_t *n)
1137    {
1138     int i = n->cnt_history;
1139     while (i > 0)
1140     free(n->history[--i]);
1141     free(n);
1142    }
1143    
1144  /* state->flags is already checked to be nonzero */  /* state->flags is already checked to be nonzero */
1145  static void load_history(const char *fromfile)  static void load_history(line_input_t *st_parm)
1146  {  {
1147     char *temp_h[MAX_HISTORY];
1148     char *line;
1149   FILE *fp;   FILE *fp;
1150   int hi;   unsigned idx, i, line_len;
1151    
1152   /* NB: do not trash old history if file can't be opened */   /* NB: do not trash old history if file can't be opened */
1153    
1154   fp = fopen_for_read(fromfile);   fp = fopen_for_read(st_parm->hist_file);
1155   if (fp) {   if (fp) {
1156   /* clean up old history */   /* clean up old history */
1157   for (hi = state->cnt_history; hi > 0;) {   for (idx = st_parm->cnt_history; idx > 0;) {
1158   hi--;   idx--;
1159   free(state->history[hi]);   free(st_parm->history[idx]);
1160   state->history[hi] = NULL;   st_parm->history[idx] = NULL;
1161   }   }
1162    
1163   for (hi = 0; hi < MAX_HISTORY;) {   /* fill temp_h[], retaining only last MAX_HISTORY lines */
1164   char *hl = xmalloc_fgetline(fp);   memset(temp_h, 0, sizeof(temp_h));
1165   int l;   st_parm->cnt_history_in_file = idx = 0;
1166     while ((line = xmalloc_fgetline(fp)) != NULL) {
1167   if (!hl)   if (line[0] == '\0') {
1168   break;   free(line);
  l = strlen(hl);  
  if (l >= MAX_LINELEN)  
  hl[MAX_LINELEN-1] = '\0';  
  if (l == 0) {  
  free(hl);  
1169   continue;   continue;
1170   }   }
1171   state->history[hi++] = hl;   free(temp_h[idx]);
1172     temp_h[idx] = line;
1173     st_parm->cnt_history_in_file++;
1174     idx++;
1175     if (idx == MAX_HISTORY)
1176     idx = 0;
1177   }   }
1178   fclose(fp);   fclose(fp);
1179   state->cnt_history = hi;  
1180     /* find first non-NULL temp_h[], if any */
1181     if (st_parm->cnt_history_in_file) {
1182     while (temp_h[idx] == NULL) {
1183     idx++;
1184     if (idx == MAX_HISTORY)
1185     idx = 0;
1186     }
1187     }
1188    
1189     /* copy temp_h[] to st_parm->history[] */
1190     for (i = 0; i < MAX_HISTORY;) {
1191     line = temp_h[idx];
1192     if (!line)
1193     break;
1194     idx++;
1195     if (idx == MAX_HISTORY)
1196     idx = 0;
1197     line_len = strlen(line);
1198     if (line_len >= MAX_LINELEN)
1199     line[MAX_LINELEN-1] = '\0';
1200     st_parm->history[i++] = line;
1201     }
1202     st_parm->cnt_history = i;
1203   }   }
1204  }  }
1205    
1206  /* state->flags is already checked to be nonzero */  /* state->flags is already checked to be nonzero */
1207  static void save_history(const char *tofile)  static void save_history(char *str)
1208  {  {
1209   FILE *fp;   int fd;
1210     int len, len2;
1211    
1212   fp = fopen_for_write(tofile);   fd = open(state->hist_file, O_WRONLY | O_CREAT | O_APPEND, 0666);
1213   if (fp) {   if (fd < 0)
1214     return;
1215     xlseek(fd, 0, SEEK_END); /* paranoia */
1216     len = strlen(str);
1217     str[len] = '\n'; /* we (try to) do atomic write */
1218     len2 = full_write(fd, str, len + 1);
1219     str[len] = '\0';
1220     close(fd);
1221     if (len2 != len + 1)
1222     return; /* "wtf?" */
1223    
1224     /* did we write so much that history file needs trimming? */
1225     state->cnt_history_in_file++;
1226     if (state->cnt_history_in_file > MAX_HISTORY * 4) {
1227     FILE *fp;
1228     char *new_name;
1229     line_input_t *st_temp;
1230   int i;   int i;
1231    
1232   for (i = 0; i < state->cnt_history; i++) {   /* we may have concurrently written entries from others.
1233   fprintf(fp, "%s\n", state->history[i]);   * load them */
1234   }   st_temp = new_line_input_t(state->flags);
1235   fclose(fp);   st_temp->hist_file = state->hist_file;
1236   }   load_history(st_temp);
1237  }  
1238  #else   /* write out temp file and replace hist_file atomically */
1239  #define load_history(a) ((void)0)   new_name = xasprintf("%s.%u.new", state->hist_file, (int) getpid());
1240  #define save_history(a) ((void)0)   fp = fopen_for_write(new_name);
1241  #endif /* FEATURE_COMMAND_SAVEHISTORY */   if (fp) {
1242     for (i = 0; i < st_temp->cnt_history; i++)
1243     fprintf(fp, "%s\n", st_temp->history[i]);
1244     fclose(fp);
1245     if (rename(new_name, state->hist_file) == 0)
1246     state->cnt_history_in_file = st_temp->cnt_history;
1247     }
1248     free(new_name);
1249     free_line_input_t(st_temp);
1250     }
1251    }
1252    # else
1253    #  define load_history(a) ((void)0)
1254    #  define save_history(a) ((void)0)
1255    # endif /* FEATURE_COMMAND_SAVEHISTORY */
1256    
1257  static void remember_in_history(const char *str)  static void remember_in_history(char *str)
1258  {  {
1259   int i;   int i;
1260    
# Line 1076  static void remember_in_history(const ch Line 1283  static void remember_in_history(const ch
1283   /* i <= MAX_HISTORY */   /* i <= MAX_HISTORY */
1284   state->cur_history = i;   state->cur_history = i;
1285   state->cnt_history = i;   state->cnt_history = i;
1286  #if ENABLE_FEATURE_EDITING_SAVEHISTORY  # if MAX_HISTORY > 0 && ENABLE_FEATURE_EDITING_SAVEHISTORY
1287   if ((state->flags & SAVE_HISTORY) && state->hist_file)   if ((state->flags & SAVE_HISTORY) && state->hist_file)
1288   save_history(state->hist_file);   save_history(str);
1289  #endif  # endif
1290   USE_FEATURE_EDITING_FANCY_PROMPT(num_ok_lines++;)   IF_FEATURE_EDITING_FANCY_PROMPT(num_ok_lines++;)
1291  }  }
1292    
1293  #else /* MAX_HISTORY == 0 */  #else /* MAX_HISTORY == 0 */
1294  #define remember_in_history(a) ((void)0)  # define remember_in_history(a) ((void)0)
1295  #endif /* MAX_HISTORY */  #endif /* MAX_HISTORY */
1296    
1297    
1298    #if ENABLE_FEATURE_EDITING_VI
1299  /*  /*
  * This function is used to grab a character buffer  
  * from the input file descriptor and allows you to  
  * a string with full command editing (sort of like  
  * a mini readline).  
  *  
  * The following standard commands are not implemented:  
  * ESC-b -- Move back one word  
  * ESC-f -- Move forward one word  
  * ESC-d -- Delete back one word  
  * ESC-h -- Delete forward one word  
  * CTL-t -- Transpose two characters  
  *  
  * Minimalist vi-style command line editing available if configured.  
1300   * vi mode implemented 2005 by Paul Fox <pgf@foxharp.boston.ma.us>   * vi mode implemented 2005 by Paul Fox <pgf@foxharp.boston.ma.us>
1301   */   */
   
 #if ENABLE_FEATURE_EDITING_VI  
1302  static void  static void
1303  vi_Word_motion(char *command, int eat)  vi_Word_motion(int eat)
1304  {  {
1305   while (cursor < command_len && !isspace(command[cursor]))   CHAR_T *command = command_ps;
1306    
1307     while (cursor < command_len && !BB_isspace(command[cursor]))
1308   input_forward();   input_forward();
1309   if (eat) while (cursor < command_len && isspace(command[cursor]))   if (eat) while (cursor < command_len && BB_isspace(command[cursor]))
1310   input_forward();   input_forward();
1311  }  }
1312    
1313  static void  static void
1314  vi_word_motion(char *command, int eat)  vi_word_motion(int eat)
1315  {  {
1316   if (isalnum(command[cursor]) || command[cursor] == '_') {   CHAR_T *command = command_ps;
1317    
1318     if (BB_isalnum(command[cursor]) || command[cursor] == '_') {
1319   while (cursor < command_len   while (cursor < command_len
1320   && (isalnum(command[cursor+1]) || command[cursor+1] == '_'))   && (BB_isalnum(command[cursor+1]) || command[cursor+1] == '_')
1321     ) {
1322   input_forward();   input_forward();
1323   } else if (ispunct(command[cursor])) {   }
1324   while (cursor < command_len && ispunct(command[cursor+1]))   } else if (BB_ispunct(command[cursor])) {
1325     while (cursor < command_len && BB_ispunct(command[cursor+1]))
1326   input_forward();   input_forward();
1327   }   }
1328    
1329   if (cursor < command_len)   if (cursor < command_len)
1330   input_forward();   input_forward();
1331    
1332   if (eat && cursor < command_len && isspace(command[cursor]))   if (eat) {
1333   while (cursor < command_len && isspace(command[cursor]))   while (cursor < command_len && BB_isspace(command[cursor]))
1334   input_forward();   input_forward();
1335     }
1336  }  }
1337    
1338  static void  static void
1339  vi_End_motion(char *command)  vi_End_motion(void)
1340  {  {
1341     CHAR_T *command = command_ps;
1342    
1343   input_forward();   input_forward();
1344   while (cursor < command_len && isspace(command[cursor]))   while (cursor < command_len && BB_isspace(command[cursor]))
1345   input_forward();   input_forward();
1346   while (cursor < command_len-1 && !isspace(command[cursor+1]))   while (cursor < command_len-1 && !BB_isspace(command[cursor+1]))
1347   input_forward();   input_forward();
1348  }  }
1349    
1350  static void  static void
1351  vi_end_motion(char *command)  vi_end_motion(void)
1352  {  {
1353     CHAR_T *command = command_ps;
1354    
1355   if (cursor >= command_len-1)   if (cursor >= command_len-1)
1356   return;   return;
1357   input_forward();   input_forward();
1358   while (cursor < command_len-1 && isspace(command[cursor]))   while (cursor < command_len-1 && BB_isspace(command[cursor]))
1359   input_forward();   input_forward();
1360   if (cursor >= command_len-1)   if (cursor >= command_len-1)
1361   return;   return;
1362   if (isalnum(command[cursor]) || command[cursor] == '_') {   if (BB_isalnum(command[cursor]) || command[cursor] == '_') {
1363   while (cursor < command_len-1   while (cursor < command_len-1
1364   && (isalnum(command[cursor+1]) || command[cursor+1] == '_')   && (BB_isalnum(command[cursor+1]) || command[cursor+1] == '_')
1365   ) {   ) {
1366   input_forward();   input_forward();
1367   }   }
1368   } else if (ispunct(command[cursor])) {   } else if (BB_ispunct(command[cursor])) {
1369   while (cursor < command_len-1 && ispunct(command[cursor+1]))   while (cursor < command_len-1 && BB_ispunct(command[cursor+1]))
1370   input_forward();   input_forward();
1371   }   }
1372  }  }
1373    
1374  static void  static void
1375  vi_Back_motion(char *command)  vi_Back_motion(void)
1376  {  {
1377   while (cursor > 0 && isspace(command[cursor-1]))   CHAR_T *command = command_ps;
1378    
1379     while (cursor > 0 && BB_isspace(command[cursor-1]))
1380   input_backward(1);   input_backward(1);
1381   while (cursor > 0 && !isspace(command[cursor-1]))   while (cursor > 0 && !BB_isspace(command[cursor-1]))
1382   input_backward(1);   input_backward(1);
1383  }  }
1384    
1385  static void  static void
1386  vi_back_motion(char *command)  vi_back_motion(void)
1387  {  {
1388     CHAR_T *command = command_ps;
1389    
1390   if (cursor <= 0)   if (cursor <= 0)
1391   return;   return;
1392   input_backward(1);   input_backward(1);
1393   while (cursor > 0 && isspace(command[cursor]))   while (cursor > 0 && BB_isspace(command[cursor]))
1394   input_backward(1);   input_backward(1);
1395   if (cursor <= 0)   if (cursor <= 0)
1396   return;   return;
1397   if (isalnum(command[cursor]) || command[cursor] == '_') {   if (BB_isalnum(command[cursor]) || command[cursor] == '_') {
1398   while (cursor > 0   while (cursor > 0
1399   && (isalnum(command[cursor-1]) || command[cursor-1] == '_')   && (BB_isalnum(command[cursor-1]) || command[cursor-1] == '_')
1400   ) {   ) {
1401   input_backward(1);   input_backward(1);
1402   }   }
1403   } else if (ispunct(command[cursor])) {   } else if (BB_ispunct(command[cursor])) {
1404   while (cursor > 0 && ispunct(command[cursor-1]))   while (cursor > 0 && BB_ispunct(command[cursor-1]))
1405   input_backward(1);   input_backward(1);
1406   }   }
1407  }  }
1408  #endif  #endif
1409    
1410    /* Modelled after bash 4.0 behavior of Ctrl-<arrow> */
1411    static void ctrl_left(void)
1412    {
1413     CHAR_T *command = command_ps;
1414    
1415     while (1) {
1416     CHAR_T c;
1417    
1418     input_backward(1);
1419     if (cursor == 0)
1420     break;
1421     c = command[cursor];
1422     if (c != ' ' && !BB_ispunct(c)) {
1423     /* we reached a "word" delimited by spaces/punct.
1424     * go to its beginning */
1425     while (1) {
1426     c = command[cursor - 1];
1427     if (c == ' ' || BB_ispunct(c))
1428     break;
1429     input_backward(1);
1430     if (cursor == 0)
1431     break;
1432     }
1433     break;
1434     }
1435     }
1436    }
1437    static void ctrl_right(void)
1438    {
1439     CHAR_T *command = command_ps;
1440    
1441     while (1) {
1442     CHAR_T c;
1443    
1444     c = command[cursor];
1445     if (c == BB_NUL)
1446     break;
1447     if (c != ' ' && !BB_ispunct(c)) {
1448     /* we reached a "word" delimited by spaces/punct.
1449     * go to its end + 1 */
1450     while (1) {
1451     input_forward();
1452     c = command[cursor];
1453     if (c == BB_NUL || c == ' ' || BB_ispunct(c))
1454     break;
1455     }
1456     break;
1457     }
1458     input_forward();
1459     }
1460    }
1461    
1462    
1463  /*  /*
1464   * read_line_input and its helpers   * read_line_input and its helpers
1465   */   */
1466    
1467    #if ENABLE_FEATURE_EDITING_ASK_TERMINAL
1468    static void ask_terminal(void)
1469    {
1470     /* Ask terminal where is the cursor now.
1471     * lineedit_read_key handles response and corrects
1472     * our idea of current cursor position.
1473     * Testcase: run "echo -n long_line_long_line_long_line",
1474     * then type in a long, wrapping command and try to
1475     * delete it using backspace key.
1476     * Note: we print it _after_ prompt, because
1477     * prompt may contain CR. Example: PS1='\[\r\n\]\w '
1478     */
1479     /* Problem: if there is buffered input on stdin,
1480     * the response will be delivered later,
1481     * possibly to an unsuspecting application.
1482     * Testcase: "sleep 1; busybox ash" + press and hold [Enter].
1483     * Result:
1484     * ~/srcdevel/bbox/fix/busybox.t4 #
1485     * ~/srcdevel/bbox/fix/busybox.t4 #
1486     * ^[[59;34~/srcdevel/bbox/fix/busybox.t4 #  <-- garbage
1487     * ~/srcdevel/bbox/fix/busybox.t4 #
1488     *
1489     * Checking for input with poll only makes the race narrower,
1490     * I still can trigger it. Strace:
1491     *
1492     * write(1, "~/srcdevel/bbox/fix/busybox.t4 # ", 33) = 33
1493     * poll([{fd=0, events=POLLIN}], 1, 0) = 0 (Timeout)  <-- no input exists
1494     * write(1, "\33[6n", 4) = 4  <-- send the ESC sequence, quick!
1495     * poll([{fd=0, events=POLLIN}], 1, 4294967295) = 1 ([{fd=0, revents=POLLIN}])
1496     * read(0, "\n", 1)      = 1  <-- oh crap, user's input got in first
1497     */
1498     struct pollfd pfd;
1499    
1500     pfd.fd = STDIN_FILENO;
1501     pfd.events = POLLIN;
1502     if (safe_poll(&pfd, 1, 0) == 0) {
1503     S.sent_ESC_br6n = 1;
1504     out1str("\033" "[6n");
1505     fflush_all(); /* make terminal see it ASAP! */
1506     }
1507    }
1508    #else
1509    #define ask_terminal() ((void)0)
1510    #endif
1511    
1512  #if !ENABLE_FEATURE_EDITING_FANCY_PROMPT  #if !ENABLE_FEATURE_EDITING_FANCY_PROMPT
1513  static void parse_and_put_prompt(const char *prmt_ptr)  static void parse_and_put_prompt(const char *prmt_ptr)
1514  {  {
# Line 1247  static void parse_and_put_prompt(const c Line 1552  static void parse_and_put_prompt(const c
1552   c = *prmt_ptr++;   c = *prmt_ptr++;
1553    
1554   switch (c) {   switch (c) {
1555  #if ENABLE_FEATURE_GETUSERNAME_AND_HOMEDIR  # if ENABLE_FEATURE_GETUSERNAME_AND_HOMEDIR
1556   case 'u':   case 'u':
1557   pbuf = user_buf ? user_buf : (char*)"";   pbuf = user_buf ? user_buf : (char*)"";
1558   break;   break;
1559  #endif  # endif
1560   case 'h':   case 'h':
1561   pbuf = free_me = safe_gethostname();   pbuf = free_me = safe_gethostname();
1562   *strchrnul(pbuf, '.') = '\0';   *strchrnul(pbuf, '.') = '\0';
# Line 1259  static void parse_and_put_prompt(const c Line 1564  static void parse_and_put_prompt(const c
1564   case '$':   case '$':
1565   c = (geteuid() == 0 ? '#' : '$');   c = (geteuid() == 0 ? '#' : '$');
1566   break;   break;
1567  #if ENABLE_FEATURE_GETUSERNAME_AND_HOMEDIR  # if ENABLE_FEATURE_GETUSERNAME_AND_HOMEDIR
1568   case 'w':   case 'w':
1569   /* /home/user[/something] -> ~[/something] */   /* /home/user[/something] -> ~[/something] */
1570   pbuf = cwd_buf;   pbuf = cwd_buf;
# Line 1272  static void parse_and_put_prompt(const c Line 1577  static void parse_and_put_prompt(const c
1577   pbuf = free_me = xasprintf("~%s", cwd_buf + l);   pbuf = free_me = xasprintf("~%s", cwd_buf + l);
1578   }   }
1579   break;   break;
1580  #endif  # endif
1581   case 'W':   case 'W':
1582   pbuf = cwd_buf;   pbuf = cwd_buf;
1583   cp = strrchr(pbuf, '/');   cp = strrchr(pbuf, '/');
# Line 1337  static void cmdedit_setwidth(unsigned w, Line 1642  static void cmdedit_setwidth(unsigned w,
1642   int new_y = (cursor + cmdedit_prmt_len) / w;   int new_y = (cursor + cmdedit_prmt_len) / w;
1643   /* redraw */   /* redraw */
1644   redraw((new_y >= cmdedit_y ? new_y : cmdedit_y), command_len - cursor);   redraw((new_y >= cmdedit_y ? new_y : cmdedit_y), command_len - cursor);
1645   fflush(stdout);   fflush_all();
1646   }   }
1647  }  }
1648    
# Line 1350  static void win_changed(int nsig) Line 1655  static void win_changed(int nsig)
1655   signal(SIGWINCH, win_changed); /* rearm ourself */   signal(SIGWINCH, win_changed); /* rearm ourself */
1656  }  }
1657    
1658  /*  static int lineedit_read_key(char *read_key_buffer)
1659   * The emacs and vi modes share much of the code in the big  {
1660   * command loop.  Commands entered when in vi's command mode (aka   int64_t ic;
1661   * "escape mode") get an extra bit added to distinguish them --   struct pollfd pfd;
1662   * this keeps them from being self-inserted.  This clutters the   int delay = -1;
1663   * big switch a bit, but keeps all the code in one place.  #if ENABLE_FEATURE_ASSUME_UNICODE
1664   */   char unicode_buf[MB_CUR_MAX + 1];
1665     int unicode_idx = 0;
1666    #endif
1667    
1668     pfd.fd = STDIN_FILENO;
1669     pfd.events = POLLIN;
1670     do {
1671    #if ENABLE_FEATURE_EDITING_ASK_TERMINAL || ENABLE_FEATURE_ASSUME_UNICODE
1672     poll_again:
1673    #endif
1674     if (read_key_buffer[0] == 0) {
1675     /* Wait for input. Can't just call read_key,
1676     * it returns at once if stdin
1677     * is in non-blocking mode. */
1678     safe_poll(&pfd, 1, delay);
1679     }
1680     /* Note: read_key sets errno to 0 on success: */
1681     ic = read_key(STDIN_FILENO, read_key_buffer);
1682    
1683    #if ENABLE_FEATURE_EDITING_ASK_TERMINAL
1684     if ((int32_t)ic == KEYCODE_CURSOR_POS
1685     && S.sent_ESC_br6n
1686     ) {
1687     S.sent_ESC_br6n = 0;
1688     if (cursor == 0) { /* otherwise it may be bogus */
1689     int col = ((ic >> 32) & 0x7fff) - 1;
1690     if (col > cmdedit_prmt_len) {
1691     cmdedit_x += (col - cmdedit_prmt_len);
1692     while (cmdedit_x >= cmdedit_termw) {
1693     cmdedit_x -= cmdedit_termw;
1694     cmdedit_y++;
1695     }
1696     }
1697     }
1698     goto poll_again;
1699     }
1700    #endif
1701    
1702  #define vbit 0x100  #if ENABLE_FEATURE_ASSUME_UNICODE
1703     {
1704     wchar_t wc;
1705    
1706     if ((int32_t)ic < 0) /* KEYCODE_xxx */
1707     return ic;
1708     unicode_buf[unicode_idx++] = ic;
1709     unicode_buf[unicode_idx] = '\0';
1710     if (mbstowcs(&wc, unicode_buf, 1) != 1 && unicode_idx < MB_CUR_MAX) {
1711     delay = 50;
1712     goto poll_again;
1713     }
1714     ic = wc;
1715     }
1716    #endif
1717     } while (errno == EAGAIN);
1718    
1719     return ic;
1720    }
1721    
1722  /* leave out the "vi-mode"-only case labels if vi editing isn't  /* leave out the "vi-mode"-only case labels if vi editing isn't
1723   * configured. */   * configured. */
1724  #define vi_case(caselabel) USE_FEATURE_EDITING(case caselabel)  #define vi_case(caselabel) IF_FEATURE_EDITING_VI(case caselabel)
1725    
1726  /* convert uppercase ascii to equivalent control char, for readability */  /* convert uppercase ascii to equivalent control char, for readability */
1727  #undef CTRL  #undef CTRL
1728  #define CTRL(a) ((a) & ~0x40)  #define CTRL(a) ((a) & ~0x40)
1729    
1730  /* Returns:  /* maxsize must be >= 2.
1731     * Returns:
1732   * -1 on read errors or EOF, or on bare Ctrl-D,   * -1 on read errors or EOF, or on bare Ctrl-D,
1733   * 0  on ctrl-C (the line entered is still returned in 'command'),   * 0  on ctrl-C (the line entered is still returned in 'command'),
1734   * >0 length of input string, including terminating '\n'   * >0 length of input string, including terminating '\n'
# Line 1379  int FAST_FUNC read_line_input(const char Line 1739  int FAST_FUNC read_line_input(const char
1739  #if ENABLE_FEATURE_TAB_COMPLETION  #if ENABLE_FEATURE_TAB_COMPLETION
1740   smallint lastWasTab = FALSE;   smallint lastWasTab = FALSE;
1741  #endif  #endif
  unsigned ic;  
  unsigned char c;  
1742   smallint break_out = 0;   smallint break_out = 0;
1743  #if ENABLE_FEATURE_EDITING_VI  #if ENABLE_FEATURE_EDITING_VI
1744   smallint vi_cmdmode = 0;   smallint vi_cmdmode = 0;
  smalluint prevc;  
1745  #endif  #endif
1746   struct termios initial_settings;   struct termios initial_settings;
1747   struct termios new_settings;   struct termios new_settings;
1748     char read_key_buffer[KEYCODE_BUFFER_SIZE];
1749    
1750   INIT_S();   INIT_S();
1751    
# Line 1396  int FAST_FUNC read_line_input(const char Line 1754  int FAST_FUNC read_line_input(const char
1754   ) {   ) {
1755   /* Happens when e.g. stty -echo was run before */   /* Happens when e.g. stty -echo was run before */
1756   parse_and_put_prompt(prompt);   parse_and_put_prompt(prompt);
1757   fflush(stdout);   /* fflush_all(); - done by parse_and_put_prompt */
1758   if (fgets(command, maxsize, stdin) == NULL)   if (fgets(command, maxsize, stdin) == NULL)
1759   len = -1; /* EOF or error */   len = -1; /* EOF or error */
1760   else   else
# Line 1405  int FAST_FUNC read_line_input(const char Line 1763  int FAST_FUNC read_line_input(const char
1763   return len;   return len;
1764   }   }
1765    
1766     init_unicode();
1767    
1768  // FIXME: audit & improve this  // FIXME: audit & improve this
1769   if (maxsize > MAX_LINELEN)   if (maxsize > MAX_LINELEN)
1770   maxsize = MAX_LINELEN;   maxsize = MAX_LINELEN;
1771     S.maxsize = maxsize;
1772    
1773   /* With null flags, no other fields are ever used */   /* With null flags, no other fields are ever used */
1774   state = st ? st : (line_input_t*) &const_int_0;   state = st ? st : (line_input_t*) &const_int_0;
 #if ENABLE_FEATURE_EDITING_SAVEHISTORY  
  if ((state->flags & SAVE_HISTORY) && state->hist_file)  
  load_history(state->hist_file);  
 #endif  
1775  #if MAX_HISTORY > 0  #if MAX_HISTORY > 0
1776    # if ENABLE_FEATURE_EDITING_SAVEHISTORY
1777     if ((state->flags & SAVE_HISTORY) && state->hist_file)
1778     if (state->cnt_history == 0)
1779     load_history(state);
1780    # endif
1781   if (state->flags & DO_HISTORY)   if (state->flags & DO_HISTORY)
1782   state->cur_history = state->cnt_history;   state->cur_history = state->cnt_history;
1783  #endif  #endif
# Line 1423  int FAST_FUNC read_line_input(const char Line 1785  int FAST_FUNC read_line_input(const char
1785   /* prepare before init handlers */   /* prepare before init handlers */
1786   cmdedit_y = 0;  /* quasireal y, not true if line > xt*yt */   cmdedit_y = 0;  /* quasireal y, not true if line > xt*yt */
1787   command_len = 0;   command_len = 0;
1788    #if ENABLE_FEATURE_ASSUME_UNICODE
1789     command_ps = xzalloc(maxsize * sizeof(command_ps[0]));
1790    #else
1791   command_ps = command;   command_ps = command;
1792   command[0] = '\0';   command[0] = '\0';
1793    #endif
1794    #define command command_must_not_be_used
1795    
1796   new_settings = initial_settings;   new_settings = initial_settings;
1797   new_settings.c_lflag &= ~ICANON;        /* unbuffered input */   new_settings.c_lflag &= ~ICANON;        /* unbuffered input */
# Line 1435  int FAST_FUNC read_line_input(const char Line 1802  int FAST_FUNC read_line_input(const char
1802   new_settings.c_cc[VTIME] = 0;   new_settings.c_cc[VTIME] = 0;
1803   /* Turn off CTRL-C, so we can trap it */   /* Turn off CTRL-C, so we can trap it */
1804  #ifndef _POSIX_VDISABLE  #ifndef _POSIX_VDISABLE
1805  #define _POSIX_VDISABLE '\0'  # define _POSIX_VDISABLE '\0'
1806  #endif  #endif
1807   new_settings.c_cc[VINTR] = _POSIX_VDISABLE;   new_settings.c_cc[VINTR] = _POSIX_VDISABLE;
1808   tcsetattr_stdin_TCSANOW(&new_settings);   tcsetattr_stdin_TCSANOW(&new_settings);
# Line 1456  int FAST_FUNC read_line_input(const char Line 1823  int FAST_FUNC read_line_input(const char
1823  #endif  #endif
1824    
1825  #if 0  #if 0
1826   for (ic = 0; ic <= MAX_HISTORY; ic++)   for (i = 0; i <= MAX_HISTORY; i++)
1827   bb_error_msg("history[%d]:'%s'", ic, state->history[ic]);   bb_error_msg("history[%d]:'%s'", i, state->history[i]);
1828   bb_error_msg("cur_history:%d cnt_history:%d", state->cur_history, state->cnt_history);   bb_error_msg("cur_history:%d cnt_history:%d", state->cur_history, state->cnt_history);
1829  #endif  #endif
1830    
1831   /* Print out the command prompt */   /* Print out the command prompt, optionally ask where cursor is */
1832   parse_and_put_prompt(prompt);   parse_and_put_prompt(prompt);
1833     ask_terminal();
1834    
1835     read_key_buffer[0] = 0;
1836   while (1) {   while (1) {
1837   fflush(NULL);   /*
1838     * The emacs and vi modes share much of the code in the big
1839   if (nonblock_safe_read(STDIN_FILENO, &c, 1) < 1) {   * command loop.  Commands entered when in vi's command mode
1840   /* if we can't read input then exit */   * (aka "escape mode") get an extra bit added to distinguish
1841   goto prepare_to_die;   * them - this keeps them from being self-inserted. This
1842   }   * clutters the big switch a bit, but keeps all the code
1843     * in one place.
1844     */
1845     enum {
1846     VI_CMDMODE_BIT = 0x40000000,
1847     /* 0x80000000 bit flags KEYCODE_xxx */
1848     };
1849     int32_t ic, ic_raw;
1850    
1851   ic = c;   fflush_all();
1852     ic = ic_raw = lineedit_read_key(read_key_buffer);
1853    
1854  #if ENABLE_FEATURE_EDITING_VI  #if ENABLE_FEATURE_EDITING_VI
1855   newdelflag = 1;   newdelflag = 1;
1856   if (vi_cmdmode)   if (vi_cmdmode) {
1857   ic |= vbit;   /* btw, since KEYCODE_xxx are all < 0, this doesn't
1858     * change ic if it contains one of them: */
1859     ic |= VI_CMDMODE_BIT;
1860     }
1861  #endif  #endif
1862    
1863   switch (ic) {   switch (ic) {
1864   case '\n':   case '\n':
1865   case '\r':   case '\r':
1866   vi_case('\n'|vbit:)   vi_case('\n'|VI_CMDMODE_BIT:)
1867   vi_case('\r'|vbit:)   vi_case('\r'|VI_CMDMODE_BIT:)
1868   /* Enter */   /* Enter */
1869   goto_new_line();   goto_new_line();
1870   break_out = 1;   break_out = 1;
1871   break;   break;
1872   case CTRL('A'):   case CTRL('A'):
1873   vi_case('0'|vbit:)   vi_case('0'|VI_CMDMODE_BIT:)
1874   /* Control-a -- Beginning of line */   /* Control-a -- Beginning of line */
1875   input_backward(cursor);   input_backward(cursor);
1876   break;   break;
1877   case CTRL('B'):   case CTRL('B'):
1878   vi_case('h'|vbit:)   vi_case('h'|VI_CMDMODE_BIT:)
1879   vi_case('\b'|vbit:)   vi_case('\b'|VI_CMDMODE_BIT:)
1880   vi_case('\x7f'|vbit:) /* DEL */   vi_case('\x7f'|VI_CMDMODE_BIT:) /* DEL */
1881   /* Control-b -- Move back one character */   /* Control-b -- Move back one character */
1882   input_backward(1);   input_backward(1);
1883   break;   break;
  case CTRL('C'):  
  vi_case(CTRL('C')|vbit:)  
  /* Control-c -- stop gathering input */  
  goto_new_line();  
  command_len = 0;  
  break_out = -1; /* "do not append '\n'" */  
  break;  
  case CTRL('D'):  
  /* Control-d -- Delete one character, or exit  
  * if the len=0 and no chars to delete */  
  if (command_len == 0) {  
  errno = 0;  
  prepare_to_die:  
  /* to control stopped jobs */  
  break_out = command_len = -1;  
  break;  
  }  
  input_delete(0);  
  break;  
   
1884   case CTRL('E'):   case CTRL('E'):
1885   vi_case('$'|vbit:)   vi_case('$'|VI_CMDMODE_BIT:)
1886   /* Control-e -- End of line */   /* Control-e -- End of line */
1887   input_end();   input_end();
1888   break;   break;
1889   case CTRL('F'):   case CTRL('F'):
1890   vi_case('l'|vbit:)   vi_case('l'|VI_CMDMODE_BIT:)
1891   vi_case(' '|vbit:)   vi_case(' '|VI_CMDMODE_BIT:)
1892   /* Control-f -- Move forward one character */   /* Control-f -- Move forward one character */
1893   input_forward();   input_forward();
1894   break;   break;
   
1895   case '\b':   case '\b':
1896   case '\x7f': /* DEL */   case '\x7f': /* DEL */
1897   /* Control-h and DEL */   /* Control-h and DEL */
1898   input_backspace();   input_backspace();
1899   break;   break;
   
1900  #if ENABLE_FEATURE_TAB_COMPLETION  #if ENABLE_FEATURE_TAB_COMPLETION
1901   case '\t':   case '\t':
1902   input_tab(&lastWasTab);   input_tab(&lastWasTab);
1903   break;   break;
1904  #endif  #endif
   
1905   case CTRL('K'):   case CTRL('K'):
1906   /* Control-k -- clear to end of line */   /* Control-k -- clear to end of line */
1907   command[cursor] = 0;   command_ps[cursor] = BB_NUL;
1908   command_len = cursor;   command_len = cursor;
1909   printf("\033[J");   printf("\033[J");
1910   break;   break;
1911   case CTRL('L'):   case CTRL('L'):
1912   vi_case(CTRL('L')|vbit:)   vi_case(CTRL('L')|VI_CMDMODE_BIT:)
1913   /* Control-l -- clear screen */   /* Control-l -- clear screen */
1914   printf("\033[H");   printf("\033[H");
1915   redraw(0, command_len - cursor);   redraw(0, command_len - cursor);
1916   break;   break;
   
1917  #if MAX_HISTORY > 0  #if MAX_HISTORY > 0
1918   case CTRL('N'):   case CTRL('N'):
1919   vi_case(CTRL('N')|vbit:)   vi_case(CTRL('N')|VI_CMDMODE_BIT:)
1920   vi_case('j'|vbit:)   vi_case('j'|VI_CMDMODE_BIT:)
1921   /* Control-n -- Get next command in history */   /* Control-n -- Get next command in history */
1922   if (get_next_history())   if (get_next_history())
1923   goto rewrite_line;   goto rewrite_line;
1924   break;   break;
1925   case CTRL('P'):   case CTRL('P'):
1926   vi_case(CTRL('P')|vbit:)   vi_case(CTRL('P')|VI_CMDMODE_BIT:)
1927   vi_case('k'|vbit:)   vi_case('k'|VI_CMDMODE_BIT:)
1928   /* Control-p -- Get previous command from history */   /* Control-p -- Get previous command from history */
1929   if (get_previous_history())   if (get_previous_history())
1930   goto rewrite_line;   goto rewrite_line;
1931   break;   break;
1932  #endif  #endif
   
1933   case CTRL('U'):   case CTRL('U'):
1934   vi_case(CTRL('U')|vbit:)   vi_case(CTRL('U')|VI_CMDMODE_BIT:)
1935   /* Control-U -- Clear line before cursor */   /* Control-U -- Clear line before cursor */
1936   if (cursor) {   if (cursor) {
  overlapping_strcpy(command, command + cursor);  
1937   command_len -= cursor;   command_len -= cursor;
1938     memmove(command_ps, command_ps + cursor,
1939     (command_len + 1) * sizeof(command_ps[0]));
1940   redraw(cmdedit_y, command_len);   redraw(cmdedit_y, command_len);
1941   }   }
1942   break;   break;
1943   case CTRL('W'):   case CTRL('W'):
1944   vi_case(CTRL('W')|vbit:)   vi_case(CTRL('W')|VI_CMDMODE_BIT:)
1945   /* Control-W -- Remove the last word */   /* Control-W -- Remove the last word */
1946   while (cursor > 0 && isspace(command[cursor-1]))   while (cursor > 0 && BB_isspace(command_ps[cursor-1]))
1947   input_backspace();   input_backspace();
1948   while (cursor > 0 && !isspace(command[cursor-1]))   while (cursor > 0 && !BB_isspace(command_ps[cursor-1]))
1949   input_backspace();   input_backspace();
1950   break;   break;
1951    
1952  #if ENABLE_FEATURE_EDITING_VI  #if ENABLE_FEATURE_EDITING_VI
1953   case 'i'|vbit:   case 'i'|VI_CMDMODE_BIT:
1954   vi_cmdmode = 0;   vi_cmdmode = 0;
1955   break;   break;
1956   case 'I'|vbit:   case 'I'|VI_CMDMODE_BIT:
1957   input_backward(cursor);   input_backward(cursor);
1958   vi_cmdmode = 0;   vi_cmdmode = 0;
1959   break;   break;
1960   case 'a'|vbit:   case 'a'|VI_CMDMODE_BIT:
1961   input_forward();   input_forward();
1962   vi_cmdmode = 0;   vi_cmdmode = 0;
1963   break;   break;
1964   case 'A'|vbit:   case 'A'|VI_CMDMODE_BIT:
1965   input_end();   input_end();
1966   vi_cmdmode = 0;   vi_cmdmode = 0;
1967   break;   break;
1968   case 'x'|vbit:   case 'x'|VI_CMDMODE_BIT:
1969   input_delete(1);   input_delete(1);
1970   break;   break;
1971   case 'X'|vbit:   case 'X'|VI_CMDMODE_BIT:
1972   if (cursor > 0) {   if (cursor > 0) {
1973   input_backward(1);   input_backward(1);
1974   input_delete(1);   input_delete(1);
1975   }   }
1976   break;   break;
1977   case 'W'|vbit:   case 'W'|VI_CMDMODE_BIT:
1978   vi_Word_motion(command, 1);   vi_Word_motion(1);
1979   break;   break;
1980   case 'w'|vbit:   case 'w'|VI_CMDMODE_BIT:
1981   vi_word_motion(command, 1);   vi_word_motion(1);
1982   break;   break;
1983   case 'E'|vbit:   case 'E'|VI_CMDMODE_BIT:
1984   vi_End_motion(command);   vi_End_motion();
1985   break;   break;
1986   case 'e'|vbit:   case 'e'|VI_CMDMODE_BIT:
1987   vi_end_motion(command);   vi_end_motion();
1988   break;   break;
1989   case 'B'|vbit:   case 'B'|VI_CMDMODE_BIT:
1990   vi_Back_motion(command);   vi_Back_motion();
1991   break;   break;
1992   case 'b'|vbit:   case 'b'|VI_CMDMODE_BIT:
1993   vi_back_motion(command);   vi_back_motion();
1994   break;   break;
1995   case 'C'|vbit:   case 'C'|VI_CMDMODE_BIT:
1996   vi_cmdmode = 0;   vi_cmdmode = 0;
1997   /* fall through */   /* fall through */
1998   case 'D'|vbit:   case 'D'|VI_CMDMODE_BIT:
1999   goto clear_to_eol;   goto clear_to_eol;
2000    
2001   case 'c'|vbit:   case 'c'|VI_CMDMODE_BIT:
2002   vi_cmdmode = 0;   vi_cmdmode = 0;
2003   /* fall through */   /* fall through */
2004   case 'd'|vbit: {   case 'd'|VI_CMDMODE_BIT: {
2005   int nc, sc;   int nc, sc;
2006   sc = cursor;  
2007   prevc = ic;   ic = lineedit_read_key(read_key_buffer);
2008   if (safe_read(STDIN_FILENO, &c, 1) < 1)   if (errno) /* error */
2009   goto prepare_to_die;   goto prepare_to_die;
2010   if (c == (prevc & 0xff)) {   if (ic == ic_raw) { /* "cc", "dd" */
  /* "cc", "dd" */  
2011   input_backward(cursor);   input_backward(cursor);
2012   goto clear_to_eol;   goto clear_to_eol;
2013   break;   break;
2014   }   }
2015   switch (c) {  
2016     sc = cursor;
2017     switch (ic) {
2018   case 'w':   case 'w':
2019   case 'W':   case 'W':
2020   case 'e':   case 'e':
2021   case 'E':   case 'E':
2022   switch (c) {   switch (ic) {
2023   case 'w':   /* "dw", "cw" */   case 'w':   /* "dw", "cw" */
2024   vi_word_motion(command, vi_cmdmode);   vi_word_motion(vi_cmdmode);
2025   break;   break;
2026   case 'W':   /* 'dW', 'cW' */   case 'W':   /* 'dW', 'cW' */
2027   vi_Word_motion(command, vi_cmdmode);   vi_Word_motion(vi_cmdmode);
2028   break;   break;
2029   case 'e':   /* 'de', 'ce' */   case 'e':   /* 'de', 'ce' */
2030   vi_end_motion(command);   vi_end_motion();
2031   input_forward();   input_forward();
2032   break;   break;
2033   case 'E':   /* 'dE', 'cE' */   case 'E':   /* 'dE', 'cE' */
2034   vi_End_motion(command);   vi_End_motion();
2035   input_forward();   input_forward();
2036   break;   break;
2037   }   }
# Line 1684  int FAST_FUNC read_line_input(const char Line 2042  int FAST_FUNC read_line_input(const char
2042   break;   break;
2043   case 'b':  /* "db", "cb" */   case 'b':  /* "db", "cb" */
2044   case 'B':  /* implemented as B */   case 'B':  /* implemented as B */
2045   if (c == 'b')   if (ic == 'b')
2046   vi_back_motion(command);   vi_back_motion();
2047   else   else
2048   vi_Back_motion(command);   vi_Back_motion();
2049   while (sc-- > cursor)   while (sc-- > cursor)
2050   input_delete(1);   input_delete(1);
2051   break;   break;
# Line 1695  int FAST_FUNC read_line_input(const char Line 2053  int FAST_FUNC read_line_input(const char
2053   input_delete(1);   input_delete(1);
2054   break;   break;
2055   case '$':  /* "d$", "c$" */   case '$':  /* "d$", "c$" */
2056   clear_to_eol:   clear_to_eol:
2057   while (cursor < command_len)   while (cursor < command_len)
2058   input_delete(1);   input_delete(1);
2059   break;   break;
2060   }   }
2061   break;   break;
2062   }   }
2063   case 'p'|vbit:   case 'p'|VI_CMDMODE_BIT:
2064   input_forward();   input_forward();
2065   /* fallthrough */   /* fallthrough */
2066   case 'P'|vbit:   case 'P'|VI_CMDMODE_BIT:
2067   put();   put();
2068   break;   break;
2069   case 'r'|vbit:   case 'r'|VI_CMDMODE_BIT:
2070   if (safe_read(STDIN_FILENO, &c, 1) < 1)  //FIXME: unicode case?
2071     ic = lineedit_read_key(read_key_buffer);
2072     if (errno) /* error */
2073   goto prepare_to_die;   goto prepare_to_die;
2074   if (c == 0)   if (ic < ' ' || ic > 255) {
2075   beep();   beep();
2076   else {   } else {
2077   *(command + cursor) = c;   command_ps[cursor] = ic;
2078   bb_putchar(c);   bb_putchar(ic);
2079   bb_putchar('\b');   bb_putchar('\b');
2080   }   }
2081   break;   break;
 #endif /* FEATURE_COMMAND_EDITING_VI */  
   
2082   case '\x1b': /* ESC */   case '\x1b': /* ESC */
   
 #if ENABLE_FEATURE_EDITING_VI  
2083   if (state->flags & VI_MODE) {   if (state->flags & VI_MODE) {
2084   /* ESC: insert mode --> command mode */   /* insert mode --> command mode */
2085   vi_cmdmode = 1;   vi_cmdmode = 1;
2086   input_backward(1);   input_backward(1);
  break;  
  }  
 #endif  
  /* escape sequence follows */  
  if (safe_read(STDIN_FILENO, &c, 1) < 1)  
  goto prepare_to_die;  
  /* different vt100 emulations */  
  if (c == '[' || c == 'O') {  
  vi_case('['|vbit:)  
  vi_case('O'|vbit:)  
  if (safe_read(STDIN_FILENO, &c, 1) < 1)  
  goto prepare_to_die;  
  }  
  if (c >= '1' && c <= '9') {  
  unsigned char dummy;  
   
  if (safe_read(STDIN_FILENO, &dummy, 1) < 1)  
  goto prepare_to_die;  
  if (dummy != '~')  
  c = '\0';  
2087   }   }
2088     break;
2089    #endif /* FEATURE_COMMAND_EDITING_VI */
2090    
  switch (c) {  
 #if ENABLE_FEATURE_TAB_COMPLETION  
  case '\t':                      /* Alt-Tab */  
  input_tab(&lastWasTab);  
  break;  
 #endif  
2091  #if MAX_HISTORY > 0  #if MAX_HISTORY > 0
2092   case 'A':   case KEYCODE_UP:
2093   /* Up Arrow -- Get previous command from history */   if (get_previous_history())
2094   if (get_previous_history())   goto rewrite_line;
2095   goto rewrite_line;   beep();
2096   beep();   break;
2097     case KEYCODE_DOWN:
2098     if (!get_next_history())
2099   break;   break;
  case 'B':  
  /* Down Arrow -- Get next command in history */  
  if (!get_next_history())  
  break;  
2100   rewrite_line:   rewrite_line:
2101   /* Rewrite the line with the selected history item */   /* Rewrite the line with the selected history item */
2102   /* change command */   /* change command */
2103   command_len = strlen(strcpy(command, state->history[state->cur_history] ? : ""));   command_len = load_string(state->history[state->cur_history] ?
2104   /* redraw and go to eol (bol, in vi */   state->history[state->cur_history] : "", maxsize);
2105   redraw(cmdedit_y, (state->flags & VI_MODE) ? 9999 : 0);   /* redraw and go to eol (bol, in vi) */
2106   break;   redraw(cmdedit_y, (state->flags & VI_MODE) ? 9999 : 0);
2107     break;
2108  #endif  #endif
2109   case 'C':   case KEYCODE_RIGHT:
2110   /* Right Arrow -- Move forward one character */   input_forward();
2111   input_forward();   break;
2112   break;   case KEYCODE_LEFT:
2113   case 'D':   input_backward(1);
2114   /* Left Arrow -- Move back one character */   break;
2115   input_backward(1);   case KEYCODE_CTRL_LEFT:
2116   break;   ctrl_left();
2117   case '3':   break;
2118   /* Delete */   case KEYCODE_CTRL_RIGHT:
2119   input_delete(0);   ctrl_right();
2120   break;   break;
2121   case '1': // vt100? linux vt? or what?   case KEYCODE_DELETE:
2122   case '7': // vt100? linux vt? or what?   input_delete(0);
2123   case 'H': /* xterm's <Home> */   break;
2124   input_backward(cursor);   case KEYCODE_HOME:
2125   break;   input_backward(cursor);
2126   case '4': // vt100? linux vt? or what?   break;
2127   case '8': // vt100? linux vt? or what?   case KEYCODE_END:
2128   case 'F': /* xterm's <End> */   input_end();
  input_end();  
  break;  
  default:  
  c = '\0';  
  beep();  
  }  
2129   break;   break;
2130    
2131   default:        /* If it's regular input, do the normal thing */   default:
2132     if (initial_settings.c_cc[VINTR] != 0
2133   /* Control-V -- force insert of next char */   && ic_raw == initial_settings.c_cc[VINTR]
2134   if (c == CTRL('V')) {   ) {
2135   if (safe_read(STDIN_FILENO, &c, 1) < 1)   /* Ctrl-C (usually) - stop gathering input */
2136   goto prepare_to_die;   goto_new_line();
2137   if (c == 0) {   command_len = 0;
2138   beep();   break_out = -1; /* "do not append '\n'" */
2139     break;
2140     }
2141     if (initial_settings.c_cc[VEOF] != 0
2142     && ic_raw == initial_settings.c_cc[VEOF]
2143     ) {
2144     /* Ctrl-D (usually) - delete one character,
2145     * or exit if len=0 and no chars to delete */
2146     if (command_len == 0) {
2147     errno = 0;
2148    #if ENABLE_FEATURE_EDITING_VI
2149     prepare_to_die:
2150    #endif
2151     break_out = command_len = -1;
2152   break;   break;
2153   }   }
2154     input_delete(0);
2155     break;
2156   }   }
2157    // /* Control-V -- force insert of next char */
2158  #if ENABLE_FEATURE_EDITING_VI  // if (c == CTRL('V')) {
2159   if (vi_cmdmode)  /* Don't self-insert */  // if (safe_read(STDIN_FILENO, &c, 1) < 1)
2160    // goto prepare_to_die;
2161    // if (c == 0) {
2162    // beep();
2163    // break;
2164    // }
2165    // }
2166     if (ic < ' '
2167     || (!ENABLE_FEATURE_ASSUME_UNICODE && ic >= 256)
2168     || (ENABLE_FEATURE_ASSUME_UNICODE && ic >= VI_CMDMODE_BIT)
2169     ) {
2170     /* If VI_CMDMODE_BIT is set, ic is >= 256
2171     * and vi mode ignores unexpected chars.
2172     * Otherwise, we are here if ic is a
2173     * control char or an unhandled ESC sequence,
2174     * which is also ignored.
2175     */
2176   break;   break;
2177  #endif   }
2178   if ((int)command_len >= (maxsize - 2))        /* Need to leave space for enter */   if ((int)command_len >= (maxsize - 2)) {
2179     /* Not enough space for the char and EOL */
2180   break;   break;
2181     }
2182    
2183   command_len++;   command_len++;
2184   if (cursor == (command_len - 1)) {      /* Append if at the end of the line */   if (cursor == (command_len - 1)) {
2185   command[cursor] = c;   /* We are at the end, append */
2186   command[cursor+1] = '\0';   command_ps[cursor] = ic;
2187     command_ps[cursor + 1] = BB_NUL;
2188   cmdedit_set_out_char(' ');   cmdedit_set_out_char(' ');
2189   } else {                        /* Insert otherwise */   } else {
2190     /* In the middle, insert */
2191   int sc = cursor;   int sc = cursor;
2192    
2193   memmove(command + sc + 1, command + sc, command_len - sc);   memmove(command_ps + sc + 1, command_ps + sc,
2194   command[sc] = c;   (command_len - sc) * sizeof(command_ps[0]));
2195     command_ps[sc] = ic;
2196   sc++;   sc++;
2197   /* rewrite from cursor */   /* rewrite from cursor */
2198   input_end();   input_end();
# Line 1839  int FAST_FUNC read_line_input(const char Line 2200  int FAST_FUNC read_line_input(const char
2200   input_backward(cursor - sc);   input_backward(cursor - sc);
2201   }   }
2202   break;   break;
2203   }   } /* switch (ic) */
2204   if (break_out)                  /* Enter is the command terminator, no more input. */  
2205     if (break_out)
2206   break;   break;
2207    
2208  #if ENABLE_FEATURE_TAB_COMPLETION  #if ENABLE_FEATURE_TAB_COMPLETION
2209   if (c != '\t')   if (ic_raw != '\t')
2210   lastWasTab = FALSE;   lastWasTab = FALSE;
2211  #endif  #endif
2212     } /* while (1) */
2213    
2214    #if ENABLE_FEATURE_EDITING_ASK_TERMINAL
2215     if (S.sent_ESC_br6n) {
2216     /* "sleep 1; busybox ash" + hold [Enter] to trigger.
2217     * We sent "ESC [ 6 n", but got '\n' first, and
2218     * KEYCODE_CURSOR_POS response is now buffered from terminal.
2219     * It's bad already and not much can be done with it
2220     * (it _will_ be visible for the next process to read stdin),
2221     * but without this delay it even shows up on the screen
2222     * as garbage because we restore echo settings with tcsetattr
2223     * before it comes in. UGLY!
2224     */
2225     usleep(20*1000);
2226   }   }
2227    #endif
2228    
2229    /* Stop bug catching using "command_must_not_be_used" trick */
2230    #undef command
2231    
2232    #if ENABLE_FEATURE_ASSUME_UNICODE
2233     command[0] = '\0';
2234     if (command_len > 0)
2235     command_len = save_string(command, maxsize - 1);
2236     free(command_ps);
2237    #endif
2238    
2239   if (command_len > 0)   if (command_len > 0)
2240   remember_in_history(command);   remember_in_history(command);
# Line 1865  int FAST_FUNC read_line_input(const char Line 2252  int FAST_FUNC read_line_input(const char
2252   tcsetattr_stdin_TCSANOW(&initial_settings);   tcsetattr_stdin_TCSANOW(&initial_settings);
2253   /* restore SIGWINCH handler */   /* restore SIGWINCH handler */
2254   signal(SIGWINCH, previous_SIGWINCH_handler);   signal(SIGWINCH, previous_SIGWINCH_handler);
2255   fflush(stdout);   fflush_all();
2256    
2257   len = command_len;   len = command_len;
2258   DEINIT_S();   DEINIT_S();
# Line 1873  int FAST_FUNC read_line_input(const char Line 2260  int FAST_FUNC read_line_input(const char
2260   return len; /* can't return command_len, DEINIT_S() destroys it */   return len; /* can't return command_len, DEINIT_S() destroys it */
2261  }  }
2262    
 line_input_t* FAST_FUNC new_line_input_t(int flags)  
 {  
  line_input_t *n = xzalloc(sizeof(*n));  
  n->flags = flags;  
  return n;  
 }  
   
2263  #else  #else
2264    
2265  #undef read_line_input  #undef read_line_input
2266  int FAST_FUNC read_line_input(const char* prompt, char* command, int maxsize)  int FAST_FUNC read_line_input(const char* prompt, char* command, int maxsize)
2267  {  {
2268   fputs(prompt, stdout);   fputs(prompt, stdout);
2269   fflush(stdout);   fflush_all();
2270   fgets(command, maxsize, stdin);   fgets(command, maxsize, stdin);
2271   return strlen(command);   return strlen(command);
2272  }  }
2273    
2274  #endif  /* FEATURE_COMMAND_EDITING */  #endif  /* FEATURE_EDITING */
2275    
2276    
2277  /*  /*

Legend:
Removed from v.983  
changed lines
  Added in v.984