lineedit+unicode: make TAB completion work again
authorDenys Vlasenko <vda.linux@googlemail.com>
Sun, 12 Jul 2009 00:50:35 +0000 (02:50 +0200)
committerDenys Vlasenko <vda.linux@googlemail.com>
Sun, 12 Jul 2009 00:50:35 +0000 (02:50 +0200)
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
libbb/lineedit.c

index 6e836d83ec8e593e8f8763b89c3dcabd3bd9a2c1..8b7ff4f8e6ba3b38ce10c3d26ce35f48edfa184e 100644 (file)
@@ -115,6 +115,9 @@ struct lineedit_statics {
 
        unsigned cursor;
        unsigned command_len;
+       /* *int* maxsize: we want x in "if (x > S.maxsize)"
+        * to _not_ be promoted to unsigned */
+       int maxsize;
        CHAR_T *command_ps;
 
        const char *cmdedit_prompt;
@@ -389,12 +392,12 @@ static void put_prompt(void)
 /* draw prompt, editor line, and clear tail */
 static void redraw(int y, int back_cursor)
 {
-       if (y > 0)                              /* up to start y */
-               printf("\033[%dA", y);
+       if (y > 0)  /* up to start y */
+               printf("\033[%uA", y);
        bb_putchar('\r');
        put_prompt();
-       input_end();                            /* rewrite */
-       printf("\033[J");                       /* erase after cursor */
+       input_end();      /* rewrite */
+       printf("\033[J"); /* erase after cursor */
        input_backward(back_cursor);
 }
 
@@ -687,7 +690,7 @@ static void exe_n_cwd_tab_completion(char *command, int type)
        memmove(pos_buf+(is), pos_buf+(in), (MAX_LINELEN+1-(is)-(in)) * sizeof(pos_buf[0])); \
 } while (0)
 
-static int find_match(char *matchBuf, int *len_with_quotes)
+static NOINLINE int find_match(char *matchBuf, int *len_with_quotes)
 {
        int i, j;
        int command_mode;
@@ -922,12 +925,19 @@ static void input_tab(smallint *lastWasTab)
 #define matchBuf (S.input_tab__matchBuf)
                int find_type;
                int recalc_pos;
+#if ENABLE_FEATURE_ASSUME_UNICODE
+               /* cursor pos in command converted to multibyte form */
+               int cursor_mb;
+#endif
 
                *lastWasTab = TRUE;             /* flop trigger */
 
                /* Make a local copy of the string --
                 * up to the position of the cursor */
                save_string(matchBuf, cursor + 1);
+#if ENABLE_FEATURE_ASSUME_UNICODE
+               cursor_mb = strlen(matchBuf);
+#endif
                tmp = matchBuf;
 
                find_type = find_match(matchBuf, &recalc_pos);
@@ -939,7 +949,7 @@ static void input_tab(smallint *lastWasTab)
                /* If the word starts with `~' and there is no slash in the word,
                 * then try completing this word as a username. */
                if (state->flags & USERNAME_COMPLETION)
-                       if (matchBuf[0] == '~' && strchr(matchBuf, '/') == 0)
+                       if (matchBuf[0] == '~' && strchr(matchBuf, '/') == NULL)
                                username_tab_completion(matchBuf, NULL);
 #endif
                /* Try to match any executable in our path and everything
@@ -965,18 +975,20 @@ static void input_tab(smallint *lastWasTab)
                        num_matches = n + 1;
                }
                /* Did we find exactly one match? */
-               if (!matches || num_matches > 1) {
+               if (!matches || num_matches > 1) { /* no */
                        beep();
                        if (!matches)
                                return;         /* not found */
                        /* find minimal match */
                        tmp1 = xstrdup(matches[0]);
-                       for (tmp = tmp1; *tmp; tmp++)
-                               for (len_found = 1; len_found < num_matches; len_found++)
-                                       if (matches[len_found][(tmp - tmp1)] != *tmp) {
+                       for (tmp = tmp1; *tmp; tmp++) {
+                               for (len_found = 1; len_found < num_matches; len_found++) {
+                                       if (matches[len_found][tmp - tmp1] != *tmp) {
                                                *tmp = '\0';
                                                break;
                                        }
+                               }
+                       }
                        if (*tmp1 == '\0') {        /* have unique */
                                free(tmp1);
                                return;
@@ -994,29 +1006,44 @@ static void input_tab(smallint *lastWasTab)
                                tmp[len_found+1] = '\0';
                        }
                }
+
                len_found = strlen(tmp);
-               /* have space to placed match? */
-               if ((len_found - strlen(matchBuf) + command_len) < MAX_LINELEN) {
-                       /* before word for match   */
-//TODO:
 #if !ENABLE_FEATURE_ASSUME_UNICODE
-                       command_ps[cursor - recalc_pos] = '\0';
-                       /* save   tail line        */
+               /* have space to place the match? */
+               /* The result consists of three parts with these lengths: */
+               /* (cursor - recalc_pos) + len_found + (command_len - cursor) */
+               /* it simplifies into: */
+               if ((int)(len_found + command_len - recalc_pos) < S.maxsize) {
+                       /* save tail */
                        strcpy(matchBuf, command_ps + cursor);
-                       /* add    match            */
-                       strcat(command_ps, tmp);
-                       /* 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                         */
+                       /* add match and tail */
+                       sprintf(&command_ps[cursor - recalc_pos], "%s%s", tmp, matchBuf);
                        command_len = strlen(command_ps);
-                       /* write out the matched command   */
-#endif
+                       /* new pos */
+                       recalc_pos = cursor - recalc_pos + len_found;
+                       /* write out the matched command */
                        redraw(cmdedit_y, command_len - recalc_pos);
                }
+#else
+               {
+                       char command[MAX_LINELEN];
+                       int len = save_string(command, sizeof(command));
+                       /* have space to place the match? */
+                       /* (cursor_mb - recalc_pos) + len_found + (len - cursor_mb) */
+                       if ((int)(len_found + len - recalc_pos) < MAX_LINELEN) {
+                               /* save tail */
+                               strcpy(matchBuf, command + cursor_mb);
+                               /* where do we want to have cursor after all? */
+                               strcpy(&command[cursor_mb - recalc_pos], tmp);
+                               len = load_string(command, S.maxsize);
+                               /* add match and tail */
+                               sprintf(&command[cursor_mb - recalc_pos], "%s%s", tmp, matchBuf);
+                               command_len = load_string(command, S.maxsize);
+                               /* write out the matched command */
+                               redraw(cmdedit_y, command_len - len);
+                       }
+               }
+#endif
                free(tmp);
 #undef matchBuf
        } else {
@@ -1024,7 +1051,8 @@ static void input_tab(smallint *lastWasTab)
                 * just hit TAB again, print a list of all the
                 * available choices... */
                if (matches && num_matches > 0) {
-                       int sav_cursor = cursor;        /* change goto_new_line() */
+                       /* changed by goto_new_line() */
+                       int sav_cursor = cursor;
 
                        /* Go to the next line */
                        goto_new_line();
@@ -1641,6 +1669,7 @@ int FAST_FUNC read_line_input(const char *prompt, char *command, int maxsize, li
 // FIXME: audit & improve this
        if (maxsize > MAX_LINELEN)
                maxsize = MAX_LINELEN;
+       S.maxsize = maxsize;
 
        /* With null flags, no other fields are ever used */
        state = st ? st : (line_input_t*) &const_int_0;