diff options
author | Denys Vlasenko <vda.linux@googlemail.com> | 2016-11-27 22:25:07 +0100 |
---|---|---|
committer | Denys Vlasenko <vda.linux@googlemail.com> | 2016-11-27 22:25:07 +0100 |
commit | bff71d3b9d4244abc5182c38d0a98f4913188391 (patch) | |
tree | cd881f8a0563b32ad537f0bcae455dab2aa1decb | |
parent | 710b6ce9b0dba1b13028e7205bade70eefc2543f (diff) | |
download | busybox-w32-bff71d3b9d4244abc5182c38d0a98f4913188391.tar.gz busybox-w32-bff71d3b9d4244abc5182c38d0a98f4913188391.tar.bz2 busybox-w32-bff71d3b9d4244abc5182c38d0a98f4913188391.zip |
lineedit: fix two bugs in SIGWINCH signal handling
(1) restore entire sigaction, not only signal handler function
(2) do not use stdio when not sure WINCH did not interrupt a printf() or such.
function old new delta
cmdedit_setwidth - 81 +81
read_line_input 3682 3722 +40
lineedit_read_key 138 155 +17
put_prompt 55 51 -4
win_changed 93 47 -46
------------------------------------------------------------------------------
(add/remove: 1/0 grow/shrink: 2/2 up/down: 138/-50) Total: 88 bytes
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
-rw-r--r-- | libbb/lineedit.c | 70 |
1 files changed, 41 insertions, 29 deletions
diff --git a/libbb/lineedit.c b/libbb/lineedit.c index 4d7828cfa..7a1b5433d 100644 --- a/libbb/lineedit.c +++ b/libbb/lineedit.c | |||
@@ -129,8 +129,7 @@ static const char null_str[] ALIGN1 = ""; | |||
129 | struct lineedit_statics { | 129 | struct lineedit_statics { |
130 | line_input_t *state; | 130 | line_input_t *state; |
131 | 131 | ||
132 | volatile unsigned cmdedit_termw; /* = 80; */ /* actual terminal width */ | 132 | unsigned cmdedit_termw; /* = 80; */ /* actual terminal width */ |
133 | sighandler_t previous_SIGWINCH_handler; | ||
134 | 133 | ||
135 | unsigned cmdedit_x; /* real x (col) terminal position */ | 134 | unsigned cmdedit_x; /* real x (col) terminal position */ |
136 | unsigned cmdedit_y; /* pseudoreal y (row) terminal position */ | 135 | unsigned cmdedit_y; /* pseudoreal y (row) terminal position */ |
@@ -155,15 +154,22 @@ struct lineedit_statics { | |||
155 | unsigned num_matches; | 154 | unsigned num_matches; |
156 | #endif | 155 | #endif |
157 | 156 | ||
157 | unsigned SIGWINCH_saved; | ||
158 | volatile unsigned SIGWINCH_count; | ||
159 | volatile smallint ok_to_redraw; | ||
160 | |||
158 | #if ENABLE_FEATURE_EDITING_VI | 161 | #if ENABLE_FEATURE_EDITING_VI |
159 | # define DELBUFSIZ 128 | 162 | # define DELBUFSIZ 128 |
160 | CHAR_T *delptr; | ||
161 | smallint newdelflag; /* whether delbuf should be reused yet */ | 163 | smallint newdelflag; /* whether delbuf should be reused yet */ |
164 | CHAR_T *delptr; | ||
162 | CHAR_T delbuf[DELBUFSIZ]; /* a place to store deleted characters */ | 165 | CHAR_T delbuf[DELBUFSIZ]; /* a place to store deleted characters */ |
163 | #endif | 166 | #endif |
164 | #if ENABLE_FEATURE_EDITING_ASK_TERMINAL | 167 | #if ENABLE_FEATURE_EDITING_ASK_TERMINAL |
165 | smallint sent_ESC_br6n; | 168 | smallint sent_ESC_br6n; |
166 | #endif | 169 | #endif |
170 | |||
171 | /* Largish struct, keeping it last results in smaller code */ | ||
172 | struct sigaction SIGWINCH_handler; | ||
167 | }; | 173 | }; |
168 | 174 | ||
169 | /* See lineedit_ptr_hack.c */ | 175 | /* See lineedit_ptr_hack.c */ |
@@ -172,7 +178,6 @@ extern struct lineedit_statics *const lineedit_ptr_to_statics; | |||
172 | #define S (*lineedit_ptr_to_statics) | 178 | #define S (*lineedit_ptr_to_statics) |
173 | #define state (S.state ) | 179 | #define state (S.state ) |
174 | #define cmdedit_termw (S.cmdedit_termw ) | 180 | #define cmdedit_termw (S.cmdedit_termw ) |
175 | #define previous_SIGWINCH_handler (S.previous_SIGWINCH_handler) | ||
176 | #define cmdedit_x (S.cmdedit_x ) | 181 | #define cmdedit_x (S.cmdedit_x ) |
177 | #define cmdedit_y (S.cmdedit_y ) | 182 | #define cmdedit_y (S.cmdedit_y ) |
178 | #define cmdedit_prmt_len (S.cmdedit_prmt_len) | 183 | #define cmdedit_prmt_len (S.cmdedit_prmt_len) |
@@ -434,14 +439,11 @@ static void beep(void) | |||
434 | 439 | ||
435 | static void put_prompt(void) | 440 | static void put_prompt(void) |
436 | { | 441 | { |
437 | unsigned w; | ||
438 | |||
439 | fputs(cmdedit_prompt, stdout); | 442 | fputs(cmdedit_prompt, stdout); |
440 | fflush_all(); | 443 | fflush_all(); |
441 | cursor = 0; | 444 | cursor = 0; |
442 | w = cmdedit_termw; /* read volatile var once */ | 445 | cmdedit_y = cmdedit_prmt_len / cmdedit_termw; /* new quasireal y */ |
443 | cmdedit_y = cmdedit_prmt_len / w; /* new quasireal y */ | 446 | cmdedit_x = cmdedit_prmt_len % cmdedit_termw; |
444 | cmdedit_x = cmdedit_prmt_len % w; | ||
445 | } | 447 | } |
446 | 448 | ||
447 | /* Move back one character */ | 449 | /* Move back one character */ |
@@ -513,13 +515,11 @@ static void input_backward(unsigned num) | |||
513 | put_cur_glyph_and_inc_cursor(); | 515 | put_cur_glyph_and_inc_cursor(); |
514 | } else { | 516 | } else { |
515 | int lines_up; | 517 | int lines_up; |
516 | unsigned width; | ||
517 | /* num = chars to go back from the beginning of current line: */ | 518 | /* num = chars to go back from the beginning of current line: */ |
518 | num -= cmdedit_x; | 519 | num -= cmdedit_x; |
519 | width = cmdedit_termw; /* read volatile var once */ | ||
520 | /* num=1...w: one line up, w+1...2w: two, etc: */ | 520 | /* num=1...w: one line up, w+1...2w: two, etc: */ |
521 | lines_up = 1 + (num - 1) / width; | 521 | lines_up = 1 + (num - 1) / cmdedit_termw; |
522 | cmdedit_x = (width * cmdedit_y - num) % width; | 522 | cmdedit_x = (cmdedit_termw * cmdedit_y - num) % cmdedit_termw; |
523 | cmdedit_y -= lines_up; | 523 | cmdedit_y -= lines_up; |
524 | /* go to 1st column; go up */ | 524 | /* go to 1st column; go up */ |
525 | printf("\r" ESC"[%uA", lines_up); | 525 | printf("\r" ESC"[%uA", lines_up); |
@@ -1978,28 +1978,29 @@ static void parse_and_put_prompt(const char *prmt_ptr) | |||
1978 | } | 1978 | } |
1979 | #endif | 1979 | #endif |
1980 | 1980 | ||
1981 | static void cmdedit_setwidth(unsigned w, int redraw_flg) | 1981 | static void cmdedit_setwidth(int redraw_flg) |
1982 | { | 1982 | { |
1983 | cmdedit_termw = w; | 1983 | get_terminal_width_height(STDIN_FILENO, &cmdedit_termw, NULL); |
1984 | if (redraw_flg) { | 1984 | if (redraw_flg) { |
1985 | /* new y for current cursor */ | 1985 | /* new y for current cursor */ |
1986 | int new_y = (cursor + cmdedit_prmt_len) / w; | 1986 | int new_y = (cursor + cmdedit_prmt_len) / cmdedit_termw; |
1987 | /* redraw */ | 1987 | /* redraw */ |
1988 | redraw((new_y >= cmdedit_y ? new_y : cmdedit_y), command_len - cursor); | 1988 | redraw((new_y >= cmdedit_y ? new_y : cmdedit_y), command_len - cursor); |
1989 | fflush_all(); | 1989 | fflush_all(); |
1990 | } | 1990 | } |
1991 | } | 1991 | } |
1992 | 1992 | ||
1993 | static void win_changed(int nsig) | 1993 | static void win_changed(int nsig UNUSED_PARAM) |
1994 | { | 1994 | { |
1995 | int sv_errno = errno; | 1995 | if (S.ok_to_redraw) { |
1996 | unsigned width; | 1996 | /* We are in read_key(), safe to redraw immediately */ |
1997 | 1997 | int sv_errno = errno; | |
1998 | get_terminal_width_height(0, &width, NULL); | 1998 | cmdedit_setwidth(/*redraw_flg:*/ 1); |
1999 | //FIXME: cmdedit_setwidth() -> redraw() -> printf() -> KABOOM! (we are in signal handler!) | 1999 | errno = sv_errno; |
2000 | cmdedit_setwidth(width, /*redraw_flg:*/ nsig); | 2000 | } else { |
2001 | 2001 | /* Signal main loop that redraw is necessary */ | |
2002 | errno = sv_errno; | 2002 | S.SIGWINCH_count++; |
2003 | } | ||
2003 | } | 2004 | } |
2004 | 2005 | ||
2005 | static int lineedit_read_key(char *read_key_buffer, int timeout) | 2006 | static int lineedit_read_key(char *read_key_buffer, int timeout) |
@@ -2018,7 +2019,9 @@ static int lineedit_read_key(char *read_key_buffer, int timeout) | |||
2018 | * | 2019 | * |
2019 | * Note: read_key sets errno to 0 on success. | 2020 | * Note: read_key sets errno to 0 on success. |
2020 | */ | 2021 | */ |
2022 | S.ok_to_redraw = 1; | ||
2021 | ic = read_key(STDIN_FILENO, read_key_buffer, timeout); | 2023 | ic = read_key(STDIN_FILENO, read_key_buffer, timeout); |
2024 | S.ok_to_redraw = 0; | ||
2022 | if (errno) { | 2025 | if (errno) { |
2023 | #if ENABLE_UNICODE_SUPPORT | 2026 | #if ENABLE_UNICODE_SUPPORT |
2024 | if (errno == EAGAIN && unicode_idx != 0) | 2027 | if (errno == EAGAIN && unicode_idx != 0) |
@@ -2355,9 +2358,11 @@ int FAST_FUNC read_line_input(line_input_t *st, const char *prompt, char *comman | |||
2355 | ask_terminal(); | 2358 | ask_terminal(); |
2356 | 2359 | ||
2357 | /* Install window resize handler (NB: after *all* init is complete) */ | 2360 | /* Install window resize handler (NB: after *all* init is complete) */ |
2358 | //FIXME: save entire sigaction! | 2361 | S.SIGWINCH_handler.sa_handler = win_changed; |
2359 | previous_SIGWINCH_handler = signal(SIGWINCH, win_changed); | 2362 | S.SIGWINCH_handler.sa_flags = SA_RESTART; |
2360 | win_changed(0); /* get initial window size */ | 2363 | sigaction(SIGWINCH, &S.SIGWINCH_handler, &S.SIGWINCH_handler); |
2364 | |||
2365 | cmdedit_setwidth(/*redraw_flg:*/ 0); /* get initial window size */ | ||
2361 | 2366 | ||
2362 | read_key_buffer[0] = 0; | 2367 | read_key_buffer[0] = 0; |
2363 | while (1) { | 2368 | while (1) { |
@@ -2370,6 +2375,13 @@ int FAST_FUNC read_line_input(line_input_t *st, const char *prompt, char *comman | |||
2370 | * in one place. | 2375 | * in one place. |
2371 | */ | 2376 | */ |
2372 | int32_t ic, ic_raw; | 2377 | int32_t ic, ic_raw; |
2378 | unsigned count; | ||
2379 | |||
2380 | count = S.SIGWINCH_count; | ||
2381 | if (S.SIGWINCH_saved != count) { | ||
2382 | S.SIGWINCH_saved = count; | ||
2383 | cmdedit_setwidth(/*redraw_flg:*/ 1); | ||
2384 | } | ||
2373 | 2385 | ||
2374 | fflush_all(); | 2386 | fflush_all(); |
2375 | ic = ic_raw = lineedit_read_key(read_key_buffer, timeout); | 2387 | ic = ic_raw = lineedit_read_key(read_key_buffer, timeout); |
@@ -2808,7 +2820,7 @@ int FAST_FUNC read_line_input(line_input_t *st, const char *prompt, char *comman | |||
2808 | /* restore initial_settings */ | 2820 | /* restore initial_settings */ |
2809 | tcsetattr_stdin_TCSANOW(&initial_settings); | 2821 | tcsetattr_stdin_TCSANOW(&initial_settings); |
2810 | /* restore SIGWINCH handler */ | 2822 | /* restore SIGWINCH handler */ |
2811 | signal(SIGWINCH, previous_SIGWINCH_handler); | 2823 | sigaction_set(SIGWINCH, &S.SIGWINCH_handler); |
2812 | fflush_all(); | 2824 | fflush_all(); |
2813 | 2825 | ||
2814 | len = command_len; | 2826 | len = command_len; |