aboutsummaryrefslogtreecommitdiff
path: root/libbb/lineedit.c
diff options
context:
space:
mode:
authorRon Yorston <rmy@pobox.com>2017-11-03 14:16:08 +0000
committerRon Yorston <rmy@pobox.com>2017-11-03 14:16:08 +0000
commitd6ce08aeb85b3698ddaa281016b70e16aeb9fb35 (patch)
tree02ad9bc0684859515fe891f3d6b0a1086e0db156 /libbb/lineedit.c
parentab450021a99ba66126cc6d668fb06ec3829a572b (diff)
parenta5060b8364faa7c677c8950f1315c451403b0660 (diff)
downloadbusybox-w32-d6ce08aeb85b3698ddaa281016b70e16aeb9fb35.tar.gz
busybox-w32-d6ce08aeb85b3698ddaa281016b70e16aeb9fb35.tar.bz2
busybox-w32-d6ce08aeb85b3698ddaa281016b70e16aeb9fb35.zip
Merge branch 'busybox' into merge
Diffstat (limited to 'libbb/lineedit.c')
-rw-r--r--libbb/lineedit.c88
1 files changed, 64 insertions, 24 deletions
diff --git a/libbb/lineedit.c b/libbb/lineedit.c
index d85057e72..c0edb7399 100644
--- a/libbb/lineedit.c
+++ b/libbb/lineedit.c
@@ -37,11 +37,6 @@
37 * 37 *
38 * Unicode in PS1 is not fully supported: prompt length calulation is wrong, 38 * Unicode in PS1 is not fully supported: prompt length calulation is wrong,
39 * resulting in line wrap problems with long (multi-line) input. 39 * resulting in line wrap problems with long (multi-line) input.
40 *
41 * Multi-line PS1 (e.g. PS1="\n[\w]\n$ ") has problems with history
42 * browsing: up/down arrows result in scrolling.
43 * It stems from simplistic "cmdedit_y = cmdedit_prmt_len / cmdedit_termw"
44 * calculation of how many lines the prompt takes.
45 */ 40 */
46#include "busybox.h" 41#include "busybox.h"
47#include "NUM_APPLETS.h" 42#include "NUM_APPLETS.h"
@@ -133,7 +128,7 @@ struct lineedit_statics {
133 128
134 unsigned cmdedit_x; /* real x (col) terminal position */ 129 unsigned cmdedit_x; /* real x (col) terminal position */
135 unsigned cmdedit_y; /* pseudoreal y (row) terminal position */ 130 unsigned cmdedit_y; /* pseudoreal y (row) terminal position */
136 unsigned cmdedit_prmt_len; /* length of prompt (without colors etc) */ 131 unsigned cmdedit_prmt_len; /* on-screen length of last/sole prompt line */
137 132
138 unsigned cursor; 133 unsigned cursor;
139 int command_len; /* must be signed */ 134 int command_len; /* must be signed */
@@ -143,6 +138,7 @@ struct lineedit_statics {
143 CHAR_T *command_ps; 138 CHAR_T *command_ps;
144 139
145 const char *cmdedit_prompt; 140 const char *cmdedit_prompt;
141 const char *prompt_last_line; /* last/sole prompt line */
146 142
147#if ENABLE_USERNAME_OR_HOMEDIR 143#if ENABLE_USERNAME_OR_HOMEDIR
148 char *user_buf; 144 char *user_buf;
@@ -185,6 +181,7 @@ extern struct lineedit_statics *const lineedit_ptr_to_statics;
185#define command_len (S.command_len ) 181#define command_len (S.command_len )
186#define command_ps (S.command_ps ) 182#define command_ps (S.command_ps )
187#define cmdedit_prompt (S.cmdedit_prompt ) 183#define cmdedit_prompt (S.cmdedit_prompt )
184#define prompt_last_line (S.prompt_last_line)
188#define user_buf (S.user_buf ) 185#define user_buf (S.user_buf )
189#define home_pwd_buf (S.home_pwd_buf ) 186#define home_pwd_buf (S.home_pwd_buf )
190#define matches (S.matches ) 187#define matches (S.matches )
@@ -473,14 +470,20 @@ static void beep(void)
473 bb_putchar('\007'); 470 bb_putchar('\007');
474} 471}
475 472
476static void put_prompt(void) 473/* Full or last/sole prompt line, reset edit cursor, calculate terminal cursor.
474 * cmdedit_y is always calculated for the last/sole prompt line.
475 */
476static void put_prompt_custom(bool is_full)
477{ 477{
478 fputs(cmdedit_prompt, stdout); 478 fputs((is_full ? cmdedit_prompt : prompt_last_line), stdout);
479 cursor = 0; 479 cursor = 0;
480 cmdedit_y = cmdedit_prmt_len / cmdedit_termw; /* new quasireal y */ 480 cmdedit_y = cmdedit_prmt_len / cmdedit_termw; /* new quasireal y */
481 cmdedit_x = cmdedit_prmt_len % cmdedit_termw; 481 cmdedit_x = cmdedit_prmt_len % cmdedit_termw;
482} 482}
483 483
484#define put_prompt_last_line() put_prompt_custom(0)
485#define put_prompt() put_prompt_custom(1)
486
484/* Move back one character */ 487/* Move back one character */
485/* (optimized for slow terminals) */ 488/* (optimized for slow terminals) */
486static void input_backward(unsigned num) 489static void input_backward(unsigned num)
@@ -547,7 +550,7 @@ static void input_backward(unsigned num)
547 printf("\r" ESC"[%uA", cmdedit_y); 550 printf("\r" ESC"[%uA", cmdedit_y);
548 cmdedit_y = 0; 551 cmdedit_y = 0;
549 sv_cursor = cursor; 552 sv_cursor = cursor;
550 put_prompt(); /* sets cursor to 0 */ 553 put_prompt_last_line(); /* sets cursor to 0 */
551 while (cursor < sv_cursor) 554 while (cursor < sv_cursor)
552 put_cur_glyph_and_inc_cursor(); 555 put_cur_glyph_and_inc_cursor();
553 } else { 556 } else {
@@ -568,18 +571,27 @@ static void input_backward(unsigned num)
568 } 571 }
569} 572}
570 573
571/* draw prompt, editor line, and clear tail */ 574/* See redraw and draw_full below */
572static void redraw(int y, int back_cursor) 575static void draw_custom(int y, int back_cursor, bool is_full)
573{ 576{
574 if (y > 0) /* up y lines */ 577 if (y > 0) /* up y lines */
575 printf(ESC"[%uA", y); 578 printf(ESC"[%uA", y);
576 bb_putchar('\r'); 579 bb_putchar('\r');
577 put_prompt(); 580 put_prompt_custom(is_full);
578 put_till_end_and_adv_cursor(); 581 put_till_end_and_adv_cursor();
579 printf(SEQ_CLEAR_TILL_END_OF_SCREEN); 582 printf(SEQ_CLEAR_TILL_END_OF_SCREEN);
580 input_backward(back_cursor); 583 input_backward(back_cursor);
581} 584}
582 585
586/* Move y lines up, draw last/sole prompt line, editor line[s], and clear tail.
587 * goal: redraw the prompt+input+cursor in-place, overwriting the previous */
588#define redraw(y, back_cursor) draw_custom((y), (back_cursor), 0)
589
590/* Like above, but without moving up, and while using all the prompt lines.
591 * goal: draw a full prompt+input+cursor unrelated to a previous position.
592 * note: cmdedit_y always ends up relating to the last/sole prompt line */
593#define draw_full(back_cursor) draw_custom(0, (back_cursor), 1)
594
583/* Delete the char in front of the cursor, optionally saving it 595/* Delete the char in front of the cursor, optionally saving it
584 * for later putback */ 596 * for later putback */
585#if !ENABLE_FEATURE_EDITING_VI 597#if !ENABLE_FEATURE_EDITING_VI
@@ -1159,7 +1171,7 @@ static NOINLINE void input_tab(smallint *lastWasTab)
1159 int sav_cursor = cursor; 1171 int sav_cursor = cursor;
1160 goto_new_line(); 1172 goto_new_line();
1161 showfiles(); 1173 showfiles();
1162 redraw(0, command_len - sav_cursor); 1174 draw_full(command_len - sav_cursor);
1163 } 1175 }
1164 return; 1176 return;
1165 } 1177 }
@@ -1835,14 +1847,37 @@ static void ask_terminal(void)
1835#define ask_terminal() ((void)0) 1847#define ask_terminal() ((void)0)
1836#endif 1848#endif
1837 1849
1850/* Note about multi-line PS1 (e.g. "\n\w \u@\h\n> ") and prompt redrawing:
1851 *
1852 * If the prompt has any newlines, after we print it once we use only its last
1853 * line to redraw in-place, which makes it simpler to calculate how many lines
1854 * we should move the cursor up to align the redraw (cmdedit_y). The earlier
1855 * prompt lines just stay on screen and we redraw below them.
1856 *
1857 * Use cases for all prompt lines beyond the initial draw:
1858 * - After clear-screen (^L) or after displaying tab-completion choices, we
1859 * print the full prompt, as it isn't redrawn in-place.
1860 * - During terminal resize we could try to redraw all lines, but we don't,
1861 * because it requires delicate alignment, it's good enough with only the
1862 * last line, and doing it wrong is arguably worse than not doing it at all.
1863 *
1864 * Terminology wise, if it doesn't mention "full", then it means the last/sole
1865 * prompt line. We use the prompt (last/sole line) while redrawing in-place,
1866 * and the full where we need a fresh one unrelated to an earlier position.
1867 *
1868 * If PS1 is not multiline, the last/sole line and the full are the same string.
1869 */
1870
1838/* Called just once at read_line_input() init time */ 1871/* Called just once at read_line_input() init time */
1839#if !ENABLE_FEATURE_EDITING_FANCY_PROMPT 1872#if !ENABLE_FEATURE_EDITING_FANCY_PROMPT
1840static void parse_and_put_prompt(const char *prmt_ptr) 1873static void parse_and_put_prompt(const char *prmt_ptr)
1841{ 1874{
1842 const char *p; 1875 const char *p;
1843 cmdedit_prompt = prmt_ptr; 1876 cmdedit_prompt = prompt_last_line = prmt_ptr;
1844 p = strrchr(prmt_ptr, '\n'); 1877 p = strrchr(prmt_ptr, '\n');
1845 cmdedit_prmt_len = unicode_strwidth(p ? p+1 : prmt_ptr); 1878 if (p)
1879 prompt_last_line = p + 1;
1880 cmdedit_prmt_len = unicode_strwidth(prompt_last_line);
1846 put_prompt(); 1881 put_prompt();
1847} 1882}
1848#else 1883#else
@@ -2035,7 +2070,11 @@ static void parse_and_put_prompt(const char *prmt_ptr)
2035 if (cwd_buf != (char *)bb_msg_unknown) 2070 if (cwd_buf != (char *)bb_msg_unknown)
2036 free(cwd_buf); 2071 free(cwd_buf);
2037# endif 2072# endif
2038 cmdedit_prompt = prmt_mem_ptr; 2073 /* see comment (above this function) about multiline prompt redrawing */
2074 cmdedit_prompt = prompt_last_line = prmt_mem_ptr;
2075 prmt_ptr = strrchr(cmdedit_prompt, '\n');
2076 if (prmt_ptr)
2077 prompt_last_line = prmt_ptr + 1;
2039 put_prompt(); 2078 put_prompt();
2040} 2079}
2041#endif 2080#endif
@@ -2207,7 +2246,7 @@ static int32_t reverse_i_search(int timeout)
2207 match_buf[0] = '\0'; 2246 match_buf[0] = '\0';
2208 2247
2209 /* Save and replace the prompt */ 2248 /* Save and replace the prompt */
2210 saved_prompt = cmdedit_prompt; 2249 saved_prompt = prompt_last_line;
2211 saved_prmt_len = cmdedit_prmt_len; 2250 saved_prmt_len = cmdedit_prmt_len;
2212 goto set_prompt; 2251 goto set_prompt;
2213 2252
@@ -2280,10 +2319,10 @@ static int32_t reverse_i_search(int timeout)
2280 cursor = match - matched_history_line; 2319 cursor = match - matched_history_line;
2281//FIXME: cursor position for Unicode case 2320//FIXME: cursor position for Unicode case
2282 2321
2283 free((char*)cmdedit_prompt); 2322 free((char*)prompt_last_line);
2284 set_prompt: 2323 set_prompt:
2285 cmdedit_prompt = xasprintf("(reverse-i-search)'%s': ", match_buf); 2324 prompt_last_line = xasprintf("(reverse-i-search)'%s': ", match_buf);
2286 cmdedit_prmt_len = unicode_strwidth(cmdedit_prompt); 2325 cmdedit_prmt_len = unicode_strwidth(prompt_last_line);
2287 goto do_redraw; 2326 goto do_redraw;
2288 } 2327 }
2289 } 2328 }
@@ -2303,8 +2342,8 @@ static int32_t reverse_i_search(int timeout)
2303 if (matched_history_line) 2342 if (matched_history_line)
2304 command_len = load_string(matched_history_line); 2343 command_len = load_string(matched_history_line);
2305 2344
2306 free((char*)cmdedit_prompt); 2345 free((char*)prompt_last_line);
2307 cmdedit_prompt = saved_prompt; 2346 prompt_last_line = saved_prompt;
2308 cmdedit_prmt_len = saved_prmt_len; 2347 cmdedit_prmt_len = saved_prmt_len;
2309 redraw(cmdedit_y, command_len - cursor); 2348 redraw(cmdedit_y, command_len - cursor);
2310 2349
@@ -2524,8 +2563,9 @@ int FAST_FUNC read_line_input(line_input_t *st, const char *prompt, char *comman
2524 case CTRL('L'): 2563 case CTRL('L'):
2525 vi_case(CTRL('L')|VI_CMDMODE_BIT:) 2564 vi_case(CTRL('L')|VI_CMDMODE_BIT:)
2526 /* Control-l -- clear screen */ 2565 /* Control-l -- clear screen */
2527 printf(ESC"[H"); /* cursor to top,left */ 2566 /* cursor to top,left; clear to the end of screen */
2528 redraw(0, command_len - cursor); 2567 printf(ESC"[H" ESC"[J");
2568 draw_full(command_len - cursor);
2529 break; 2569 break;
2530#if MAX_HISTORY > 0 2570#if MAX_HISTORY > 0
2531 case CTRL('N'): 2571 case CTRL('N'):