aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--libbb/lineedit.c88
1 files changed, 64 insertions, 24 deletions
diff --git a/libbb/lineedit.c b/libbb/lineedit.c
index 3a092ffe2..c0e35bb21 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 )
@@ -437,14 +434,20 @@ static void beep(void)
437 bb_putchar('\007'); 434 bb_putchar('\007');
438} 435}
439 436
440static void put_prompt(void) 437/* Full or last/sole prompt line, reset edit cursor, calculate terminal cursor.
438 * cmdedit_y is always calculated for the last/sole prompt line.
439 */
440static void put_prompt_custom(bool is_full)
441{ 441{
442 fputs(cmdedit_prompt, stdout); 442 fputs((is_full ? cmdedit_prompt : prompt_last_line), stdout);
443 cursor = 0; 443 cursor = 0;
444 cmdedit_y = cmdedit_prmt_len / cmdedit_termw; /* new quasireal y */ 444 cmdedit_y = cmdedit_prmt_len / cmdedit_termw; /* new quasireal y */
445 cmdedit_x = cmdedit_prmt_len % cmdedit_termw; 445 cmdedit_x = cmdedit_prmt_len % cmdedit_termw;
446} 446}
447 447
448#define put_prompt_last_line() put_prompt_custom(0)
449#define put_prompt() put_prompt_custom(1)
450
448/* Move back one character */ 451/* Move back one character */
449/* (optimized for slow terminals) */ 452/* (optimized for slow terminals) */
450static void input_backward(unsigned num) 453static void input_backward(unsigned num)
@@ -509,7 +512,7 @@ static void input_backward(unsigned num)
509 printf("\r" ESC"[%uA", cmdedit_y); 512 printf("\r" ESC"[%uA", cmdedit_y);
510 cmdedit_y = 0; 513 cmdedit_y = 0;
511 sv_cursor = cursor; 514 sv_cursor = cursor;
512 put_prompt(); /* sets cursor to 0 */ 515 put_prompt_last_line(); /* sets cursor to 0 */
513 while (cursor < sv_cursor) 516 while (cursor < sv_cursor)
514 put_cur_glyph_and_inc_cursor(); 517 put_cur_glyph_and_inc_cursor();
515 } else { 518 } else {
@@ -530,18 +533,27 @@ static void input_backward(unsigned num)
530 } 533 }
531} 534}
532 535
533/* draw prompt, editor line, and clear tail */ 536/* See redraw and draw_full below */
534static void redraw(int y, int back_cursor) 537static void draw_custom(int y, int back_cursor, bool is_full)
535{ 538{
536 if (y > 0) /* up y lines */ 539 if (y > 0) /* up y lines */
537 printf(ESC"[%uA", y); 540 printf(ESC"[%uA", y);
538 bb_putchar('\r'); 541 bb_putchar('\r');
539 put_prompt(); 542 put_prompt_custom(is_full);
540 put_till_end_and_adv_cursor(); 543 put_till_end_and_adv_cursor();
541 printf(SEQ_CLEAR_TILL_END_OF_SCREEN); 544 printf(SEQ_CLEAR_TILL_END_OF_SCREEN);
542 input_backward(back_cursor); 545 input_backward(back_cursor);
543} 546}
544 547
548/* Move y lines up, draw last/sole prompt line, editor line[s], and clear tail.
549 * goal: redraw the prompt+input+cursor in-place, overwriting the previous */
550#define redraw(y, back_cursor) draw_custom((y), (back_cursor), 0)
551
552/* Like above, but without moving up, and while using all the prompt lines.
553 * goal: draw a full prompt+input+cursor unrelated to a previous position.
554 * note: cmdedit_y always ends up relating to the last/sole prompt line */
555#define draw_full(back_cursor) draw_custom(0, (back_cursor), 1)
556
545/* Delete the char in front of the cursor, optionally saving it 557/* Delete the char in front of the cursor, optionally saving it
546 * for later putback */ 558 * for later putback */
547#if !ENABLE_FEATURE_EDITING_VI 559#if !ENABLE_FEATURE_EDITING_VI
@@ -1106,7 +1118,7 @@ static NOINLINE void input_tab(smallint *lastWasTab)
1106 int sav_cursor = cursor; 1118 int sav_cursor = cursor;
1107 goto_new_line(); 1119 goto_new_line();
1108 showfiles(); 1120 showfiles();
1109 redraw(0, command_len - sav_cursor); 1121 draw_full(command_len - sav_cursor);
1110 } 1122 }
1111 return; 1123 return;
1112 } 1124 }
@@ -1782,14 +1794,37 @@ static void ask_terminal(void)
1782#define ask_terminal() ((void)0) 1794#define ask_terminal() ((void)0)
1783#endif 1795#endif
1784 1796
1797/* Note about multi-line PS1 (e.g. "\n\w \u@\h\n> ") and prompt redrawing:
1798 *
1799 * If the prompt has any newlines, after we print it once we use only its last
1800 * line to redraw in-place, which makes it simpler to calculate how many lines
1801 * we should move the cursor up to align the redraw (cmdedit_y). The earlier
1802 * prompt lines just stay on screen and we redraw below them.
1803 *
1804 * Use cases for all prompt lines beyond the initial draw:
1805 * - After clear-screen (^L) or after displaying tab-completion choices, we
1806 * print the full prompt, as it isn't redrawn in-place.
1807 * - During terminal resize we could try to redraw all lines, but we don't,
1808 * because it requires delicate alignment, it's good enough with only the
1809 * last line, and doing it wrong is arguably worse than not doing it at all.
1810 *
1811 * Terminology wise, if it doesn't mention "full", then it means the last/sole
1812 * prompt line. We use the prompt (last/sole line) while redrawing in-place,
1813 * and the full where we need a fresh one unrelated to an earlier position.
1814 *
1815 * If PS1 is not multiline, the last/sole line and the full are the same string.
1816 */
1817
1785/* Called just once at read_line_input() init time */ 1818/* Called just once at read_line_input() init time */
1786#if !ENABLE_FEATURE_EDITING_FANCY_PROMPT 1819#if !ENABLE_FEATURE_EDITING_FANCY_PROMPT
1787static void parse_and_put_prompt(const char *prmt_ptr) 1820static void parse_and_put_prompt(const char *prmt_ptr)
1788{ 1821{
1789 const char *p; 1822 const char *p;
1790 cmdedit_prompt = prmt_ptr; 1823 cmdedit_prompt = prompt_last_line = prmt_ptr;
1791 p = strrchr(prmt_ptr, '\n'); 1824 p = strrchr(prmt_ptr, '\n');
1792 cmdedit_prmt_len = unicode_strwidth(p ? p+1 : prmt_ptr); 1825 if (p)
1826 prompt_last_line = p + 1;
1827 cmdedit_prmt_len = unicode_strwidth(prompt_last_line);
1793 put_prompt(); 1828 put_prompt();
1794} 1829}
1795#else 1830#else
@@ -1973,7 +2008,11 @@ static void parse_and_put_prompt(const char *prmt_ptr)
1973 if (cwd_buf != (char *)bb_msg_unknown) 2008 if (cwd_buf != (char *)bb_msg_unknown)
1974 free(cwd_buf); 2009 free(cwd_buf);
1975# endif 2010# endif
1976 cmdedit_prompt = prmt_mem_ptr; 2011 /* see comment (above this function) about multiline prompt redrawing */
2012 cmdedit_prompt = prompt_last_line = prmt_mem_ptr;
2013 prmt_ptr = strrchr(cmdedit_prompt, '\n');
2014 if (prmt_ptr)
2015 prompt_last_line = prmt_ptr + 1;
1977 put_prompt(); 2016 put_prompt();
1978} 2017}
1979#endif 2018#endif
@@ -2145,7 +2184,7 @@ static int32_t reverse_i_search(int timeout)
2145 match_buf[0] = '\0'; 2184 match_buf[0] = '\0';
2146 2185
2147 /* Save and replace the prompt */ 2186 /* Save and replace the prompt */
2148 saved_prompt = cmdedit_prompt; 2187 saved_prompt = prompt_last_line;
2149 saved_prmt_len = cmdedit_prmt_len; 2188 saved_prmt_len = cmdedit_prmt_len;
2150 goto set_prompt; 2189 goto set_prompt;
2151 2190
@@ -2218,10 +2257,10 @@ static int32_t reverse_i_search(int timeout)
2218 cursor = match - matched_history_line; 2257 cursor = match - matched_history_line;
2219//FIXME: cursor position for Unicode case 2258//FIXME: cursor position for Unicode case
2220 2259
2221 free((char*)cmdedit_prompt); 2260 free((char*)prompt_last_line);
2222 set_prompt: 2261 set_prompt:
2223 cmdedit_prompt = xasprintf("(reverse-i-search)'%s': ", match_buf); 2262 prompt_last_line = xasprintf("(reverse-i-search)'%s': ", match_buf);
2224 cmdedit_prmt_len = unicode_strwidth(cmdedit_prompt); 2263 cmdedit_prmt_len = unicode_strwidth(prompt_last_line);
2225 goto do_redraw; 2264 goto do_redraw;
2226 } 2265 }
2227 } 2266 }
@@ -2241,8 +2280,8 @@ static int32_t reverse_i_search(int timeout)
2241 if (matched_history_line) 2280 if (matched_history_line)
2242 command_len = load_string(matched_history_line); 2281 command_len = load_string(matched_history_line);
2243 2282
2244 free((char*)cmdedit_prompt); 2283 free((char*)prompt_last_line);
2245 cmdedit_prompt = saved_prompt; 2284 prompt_last_line = saved_prompt;
2246 cmdedit_prmt_len = saved_prmt_len; 2285 cmdedit_prmt_len = saved_prmt_len;
2247 redraw(cmdedit_y, command_len - cursor); 2286 redraw(cmdedit_y, command_len - cursor);
2248 2287
@@ -2451,8 +2490,9 @@ int FAST_FUNC read_line_input(line_input_t *st, const char *prompt, char *comman
2451 case CTRL('L'): 2490 case CTRL('L'):
2452 vi_case(CTRL('L')|VI_CMDMODE_BIT:) 2491 vi_case(CTRL('L')|VI_CMDMODE_BIT:)
2453 /* Control-l -- clear screen */ 2492 /* Control-l -- clear screen */
2454 printf(ESC"[H"); /* cursor to top,left */ 2493 /* cursor to top,left; clear to the end of screen */
2455 redraw(0, command_len - cursor); 2494 printf(ESC"[H" ESC"[J");
2495 draw_full(command_len - cursor);
2456 break; 2496 break;
2457#if MAX_HISTORY > 0 2497#if MAX_HISTORY > 0
2458 case CTRL('N'): 2498 case CTRL('N'):