lineedit: fix multi-line PS1 handling: calculate PS1 length from last \n
authorDenys Vlasenko <vda.linux@googlemail.com>
Mon, 19 Aug 2013 14:45:04 +0000 (16:45 +0200)
committerDenys Vlasenko <vda.linux@googlemail.com>
Mon, 19 Aug 2013 14:45:04 +0000 (16:45 +0200)
function                                             old     new   delta
parse_and_put_prompt                                 755     774     +19

Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
libbb/lineedit.c

index ecf3066..b3f16cd 100644 (file)
  * and the \] escape to signal the end of such a sequence. Example:
  *
  * PS1='\[\033[01;32m\]\u@\h\[\033[01;34m\] \w \$\[\033[00m\] '
+ *
+ * Unicode in PS1 is not fully supported: prompt length calulation is wrong,
+ * resulting in line wrap problems with long (multi-line) input.
+ *
+ * Multi-line PS1 (e.g. PS1="\n[\w]\n$ ") has problems with history
+ * browsing: up/down arrows result in scrolling.
+ * It stems from simplistic "cmdedit_y = cmdedit_prmt_len / cmdedit_termw"
+ * calculation of how many lines the prompt takes.
  */
 #include "libbb.h"
 #include "unicode.h"
@@ -1759,34 +1767,36 @@ static void ask_terminal(void)
 #define ask_terminal() ((void)0)
 #endif
 
+/* Called just once at read_line_input() init time */
 #if !ENABLE_FEATURE_EDITING_FANCY_PROMPT
 static void parse_and_put_prompt(const char *prmt_ptr)
 {
+       const char *p;
        cmdedit_prompt = prmt_ptr;
-       cmdedit_prmt_len = unicode_strlen(prmt_ptr);
+       p = strrchr(prmt_ptr, '\n');
+       cmdedit_prmt_len = unicode_strlen(p ? p+1 : prmt_ptr);
        put_prompt();
 }
 #else
 static void parse_and_put_prompt(const char *prmt_ptr)
 {
-       int prmt_len = 0;
-       size_t cur_prmt_len = 0;
-       char flg_not_length = '[';
+       int prmt_size = 0;
        char *prmt_mem_ptr = xzalloc(1);
 # if ENABLE_USERNAME_OR_HOMEDIR
        char *cwd_buf = NULL;
 # endif
-       char timebuf[sizeof("HH:MM:SS")];
+       char flg_not_length = '[';
        char cbuf[2];
-       char c;
-       char *pbuf;
 
        /*cmdedit_prmt_len = 0; - already is */
 
        cbuf[1] = '\0'; /* never changes */
 
        while (*prmt_ptr) {
+               char timebuf[sizeof("HH:MM:SS")];
                char *free_me = NULL;
+               char *pbuf;
+               char c;
 
                pbuf = cbuf;
                c = *prmt_ptr++;
@@ -1924,16 +1934,22 @@ static void parse_and_put_prompt(const char *prmt_ptr)
                        } /* if */
                } /* if */
                cbuf[0] = c;
-               cur_prmt_len = strlen(pbuf);
-               prmt_len += cur_prmt_len;
-               if (flg_not_length != ']') {
-#if 0 /*ENABLE_UNICODE_SUPPORT - won't work, pbuf is one BYTE string here. FIXME */
-                       cmdedit_prmt_len += unicode_strlen(pbuf);
+               {
+                       int n = strlen(pbuf);
+                       prmt_size += n;
+                       if (c == '\n')
+                               cmdedit_prmt_len = 0;
+                       else if (flg_not_length != ']') {
+#if 0 /*ENABLE_UNICODE_SUPPORT*/
+/* Won't work, pbuf is one BYTE string here instead of an one Unicode char string. */
+/* FIXME */
+                               cmdedit_prmt_len += unicode_strlen(pbuf);
 #else
-                       cmdedit_prmt_len += cur_prmt_len;
+                               cmdedit_prmt_len += n;
 #endif
+                       }
                }
-               prmt_mem_ptr = strcat(xrealloc(prmt_mem_ptr, prmt_len+1), pbuf);
+               prmt_mem_ptr = strcat(xrealloc(prmt_mem_ptr, prmt_size+1), pbuf);
                free(free_me);
        } /* while */