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>
This commit is contained in:
Denys Vlasenko 2016-11-27 22:25:07 +01:00
parent 710b6ce9b0
commit bff71d3b9d

View File

@ -129,8 +129,7 @@ static const char null_str[] ALIGN1 = "";
struct lineedit_statics { struct lineedit_statics {
line_input_t *state; line_input_t *state;
volatile unsigned cmdedit_termw; /* = 80; */ /* actual terminal width */ unsigned cmdedit_termw; /* = 80; */ /* actual terminal width */
sighandler_t previous_SIGWINCH_handler;
unsigned cmdedit_x; /* real x (col) terminal position */ unsigned cmdedit_x; /* real x (col) terminal position */
unsigned cmdedit_y; /* pseudoreal y (row) terminal position */ unsigned cmdedit_y; /* pseudoreal y (row) terminal position */
@ -155,15 +154,22 @@ struct lineedit_statics {
unsigned num_matches; unsigned num_matches;
#endif #endif
unsigned SIGWINCH_saved;
volatile unsigned SIGWINCH_count;
volatile smallint ok_to_redraw;
#if ENABLE_FEATURE_EDITING_VI #if ENABLE_FEATURE_EDITING_VI
# define DELBUFSIZ 128 # define DELBUFSIZ 128
CHAR_T *delptr;
smallint newdelflag; /* whether delbuf should be reused yet */ smallint newdelflag; /* whether delbuf should be reused yet */
CHAR_T *delptr;
CHAR_T delbuf[DELBUFSIZ]; /* a place to store deleted characters */ CHAR_T delbuf[DELBUFSIZ]; /* a place to store deleted characters */
#endif #endif
#if ENABLE_FEATURE_EDITING_ASK_TERMINAL #if ENABLE_FEATURE_EDITING_ASK_TERMINAL
smallint sent_ESC_br6n; smallint sent_ESC_br6n;
#endif #endif
/* Largish struct, keeping it last results in smaller code */
struct sigaction SIGWINCH_handler;
}; };
/* See lineedit_ptr_hack.c */ /* See lineedit_ptr_hack.c */
@ -172,7 +178,6 @@ extern struct lineedit_statics *const lineedit_ptr_to_statics;
#define S (*lineedit_ptr_to_statics) #define S (*lineedit_ptr_to_statics)
#define state (S.state ) #define state (S.state )
#define cmdedit_termw (S.cmdedit_termw ) #define cmdedit_termw (S.cmdedit_termw )
#define previous_SIGWINCH_handler (S.previous_SIGWINCH_handler)
#define cmdedit_x (S.cmdedit_x ) #define cmdedit_x (S.cmdedit_x )
#define cmdedit_y (S.cmdedit_y ) #define cmdedit_y (S.cmdedit_y )
#define cmdedit_prmt_len (S.cmdedit_prmt_len) #define cmdedit_prmt_len (S.cmdedit_prmt_len)
@ -434,14 +439,11 @@ static void beep(void)
static void put_prompt(void) static void put_prompt(void)
{ {
unsigned w;
fputs(cmdedit_prompt, stdout); fputs(cmdedit_prompt, stdout);
fflush_all(); fflush_all();
cursor = 0; cursor = 0;
w = cmdedit_termw; /* read volatile var once */ cmdedit_y = cmdedit_prmt_len / cmdedit_termw; /* new quasireal y */
cmdedit_y = cmdedit_prmt_len / w; /* new quasireal y */ cmdedit_x = cmdedit_prmt_len % cmdedit_termw;
cmdedit_x = cmdedit_prmt_len % w;
} }
/* Move back one character */ /* Move back one character */
@ -513,13 +515,11 @@ static void input_backward(unsigned num)
put_cur_glyph_and_inc_cursor(); put_cur_glyph_and_inc_cursor();
} else { } else {
int lines_up; int lines_up;
unsigned width;
/* num = chars to go back from the beginning of current line: */ /* num = chars to go back from the beginning of current line: */
num -= cmdedit_x; num -= cmdedit_x;
width = cmdedit_termw; /* read volatile var once */
/* num=1...w: one line up, w+1...2w: two, etc: */ /* num=1...w: one line up, w+1...2w: two, etc: */
lines_up = 1 + (num - 1) / width; lines_up = 1 + (num - 1) / cmdedit_termw;
cmdedit_x = (width * cmdedit_y - num) % width; cmdedit_x = (cmdedit_termw * cmdedit_y - num) % cmdedit_termw;
cmdedit_y -= lines_up; cmdedit_y -= lines_up;
/* go to 1st column; go up */ /* go to 1st column; go up */
printf("\r" ESC"[%uA", lines_up); printf("\r" ESC"[%uA", lines_up);
@ -1978,28 +1978,29 @@ static void parse_and_put_prompt(const char *prmt_ptr)
} }
#endif #endif
static void cmdedit_setwidth(unsigned w, int redraw_flg) static void cmdedit_setwidth(int redraw_flg)
{ {
cmdedit_termw = w; get_terminal_width_height(STDIN_FILENO, &cmdedit_termw, NULL);
if (redraw_flg) { if (redraw_flg) {
/* new y for current cursor */ /* new y for current cursor */
int new_y = (cursor + cmdedit_prmt_len) / w; int new_y = (cursor + cmdedit_prmt_len) / cmdedit_termw;
/* redraw */ /* redraw */
redraw((new_y >= cmdedit_y ? new_y : cmdedit_y), command_len - cursor); redraw((new_y >= cmdedit_y ? new_y : cmdedit_y), command_len - cursor);
fflush_all(); fflush_all();
} }
} }
static void win_changed(int nsig) static void win_changed(int nsig UNUSED_PARAM)
{ {
int sv_errno = errno; if (S.ok_to_redraw) {
unsigned width; /* We are in read_key(), safe to redraw immediately */
int sv_errno = errno;
get_terminal_width_height(0, &width, NULL); cmdedit_setwidth(/*redraw_flg:*/ 1);
//FIXME: cmdedit_setwidth() -> redraw() -> printf() -> KABOOM! (we are in signal handler!) errno = sv_errno;
cmdedit_setwidth(width, /*redraw_flg:*/ nsig); } else {
/* Signal main loop that redraw is necessary */
errno = sv_errno; S.SIGWINCH_count++;
}
} }
static int lineedit_read_key(char *read_key_buffer, int timeout) 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)
* *
* Note: read_key sets errno to 0 on success. * Note: read_key sets errno to 0 on success.
*/ */
S.ok_to_redraw = 1;
ic = read_key(STDIN_FILENO, read_key_buffer, timeout); ic = read_key(STDIN_FILENO, read_key_buffer, timeout);
S.ok_to_redraw = 0;
if (errno) { if (errno) {
#if ENABLE_UNICODE_SUPPORT #if ENABLE_UNICODE_SUPPORT
if (errno == EAGAIN && unicode_idx != 0) 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
ask_terminal(); ask_terminal();
/* Install window resize handler (NB: after *all* init is complete) */ /* Install window resize handler (NB: after *all* init is complete) */
//FIXME: save entire sigaction! S.SIGWINCH_handler.sa_handler = win_changed;
previous_SIGWINCH_handler = signal(SIGWINCH, win_changed); S.SIGWINCH_handler.sa_flags = SA_RESTART;
win_changed(0); /* get initial window size */ sigaction(SIGWINCH, &S.SIGWINCH_handler, &S.SIGWINCH_handler);
cmdedit_setwidth(/*redraw_flg:*/ 0); /* get initial window size */
read_key_buffer[0] = 0; read_key_buffer[0] = 0;
while (1) { while (1) {
@ -2370,6 +2375,13 @@ int FAST_FUNC read_line_input(line_input_t *st, const char *prompt, char *comman
* in one place. * in one place.
*/ */
int32_t ic, ic_raw; int32_t ic, ic_raw;
unsigned count;
count = S.SIGWINCH_count;
if (S.SIGWINCH_saved != count) {
S.SIGWINCH_saved = count;
cmdedit_setwidth(/*redraw_flg:*/ 1);
}
fflush_all(); fflush_all();
ic = ic_raw = lineedit_read_key(read_key_buffer, timeout); 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
/* restore initial_settings */ /* restore initial_settings */
tcsetattr_stdin_TCSANOW(&initial_settings); tcsetattr_stdin_TCSANOW(&initial_settings);
/* restore SIGWINCH handler */ /* restore SIGWINCH handler */
signal(SIGWINCH, previous_SIGWINCH_handler); sigaction_set(SIGWINCH, &S.SIGWINCH_handler);
fflush_all(); fflush_all();
len = command_len; len = command_len;