diff options
author | Peter Stephenson <p.stephenson@samsung.com> | 2021-08-27 09:36:06 +0100 |
---|---|---|
committer | Peter Stephenson <p.stephenson@samsung.com> | 2021-08-27 09:36:06 +0100 |
commit | e5cd2dd980302f328d232d933f646c3dc02828bf (patch) | |
tree | ce6f58610a53c0f9a6d8b82957eaba440d22c78d /Src/input.c | |
parent | 1a78e46564d7e480affb7f832b33605c13a01e55 (diff) | |
download | zsh-e5cd2dd980302f328d232d933f646c3dc02828bf.tar.gz zsh-e5cd2dd980302f328d232d933f646c3dc02828bf.tar.xz zsh-e5cd2dd980302f328d232d933f646c3dc02828bf.zip |
49290: Replace stdio for buffered shell input.
The previous method allowed memory management to interact with signal handlers, causing occasional crashes on some system. Instead, use a simple pre-allocated buffer and raw system calls.
Diffstat (limited to 'Src/input.c')
-rw-r--r-- | Src/input.c | 122 |
1 files changed, 111 insertions, 11 deletions
diff --git a/Src/input.c b/Src/input.c index f568cc135..3c6ad7002 100644 --- a/Src/input.c +++ b/Src/input.c @@ -80,11 +80,6 @@ /**/ int SHIN; -/* buffered shell input for non-interactive shells */ - -/**/ -FILE *bshin; - /* != 0 means we are reading input from a string */ /**/ @@ -129,7 +124,116 @@ static struct instacks *instack, *instacktop; static int instacksz = INSTACK_INITIAL; -/* Read a line from bshin. Convert tokens and * +/* Size of buffer for non-interactive command input */ + +#define SHINBUFSIZE 8192 + +/* Input buffer for non-interactive command input */ +static char *shinbuffer; + +/* Pointer into shinbuffer */ +static char *shinbufptr; + +/* End of contents read into shinbuffer */ +static char *shinbufendptr; + +/* Entry on SHIN buffer save stack */ +struct shinsaveentry { + /* Next entry on stack */ + struct shinsaveentry *next; + /* Saved shinbuffer */ + char *buffer; + /* Saved shinbufptr */ + char *ptr; + /* Saved shinbufendptr */ + char *endptr; +}; + +/* SHIN buffer save stack */ +struct shinsaveentry *shinsavestack; + +/* Reset the input buffer for SHIN, discarding any pending input */ + +/**/ +void +shinbufreset(void) +{ + shinbufendptr = shinbufptr = shinbuffer; +} + +/* Allocate a new shinbuffer + * + * Only called at shell initialisation and when saving on the stack. + */ + +/**/ +void +shinbufalloc(void) +{ + shinbuffer = zalloc(SHINBUFSIZE); + shinbufreset(); +} + +/* Save entry on SHIN buffer save stack */ + +/**/ +void +shinbufsave(void) +{ + struct shinsaveentry *entry = + (struct shinsaveentry *)zalloc(sizeof(struct shinsaveentry)); + + entry->next = shinsavestack; + entry->buffer = shinbuffer; + entry->ptr = shinbufptr; + entry->endptr = shinbufendptr; + + shinsavestack = entry; + + shinbufalloc(); +} + +/* Restore entry from SHIN buffer save stack */ + +/**/ +void +shinbufrestore(void) +{ + struct shinsaveentry *entry = shinsavestack; + + zfree(shinbuffer, SHINBUFSIZE); + + shinbuffer = entry->buffer; + shinbufptr = entry->ptr; + shinbufendptr = entry->endptr; + + shinsavestack = entry->next; + zfree(entry, sizeof(struct shinsaveentry)); +} + +/* Get a character from SHIN, -1 if none available */ + +/**/ +static int +shingetchar(void) +{ + int nread; + + if (shinbufptr < shinbufendptr) + return STOUC(*shinbufptr++); + + shinbufreset(); + do { + errno = 0; + nread = read(SHIN, shinbuffer, SHINBUFSIZE); + } while (nread < 0 && errno == EINTR); + if (nread <= 0) + return -1; + shinbufendptr = shinbuffer + nread; + return STOUC(*shinbufptr++); +} + +/* Read a line from SHIN. Convert tokens and * * null characters to Meta c^32 character pairs. */ /**/ @@ -147,11 +251,7 @@ shingetline(void) winch_unblock(); dont_queue_signals(); for (;;) { - /* Can't fgets() here because we need to accept '\0' bytes */ - do { - errno = 0; - c = fgetc(bshin); - } while (c < 0 && errno == EINTR); + c = shingetchar(); if (c < 0 || c == '\n') { winch_block(); restore_queue_signals(q); |