1
0
Fork 0
mirror of git://git.code.sf.net/p/zsh/code synced 2024-12-29 16:25:35 +01:00
zsh/Src/input.c
Bart Schaefer 98a6892cb1 52244: Fix a batch of minor defects reported by Coverity.
Coverity defects 1547831, 1547826 (remove unused function), 1521551,
1500752, 1500747, 1401549, 1372423, 1270645, 1255799, 1255792, 1255789,
1255787, 1255782, 1255750
2023-10-26 08:27:18 -07:00

852 lines
22 KiB
C

/*
* input.c - read and store lines of input
*
* This file is part of zsh, the Z shell.
*
* Copyright (c) 1992-1997 Paul Falstad
* All rights reserved.
*
* Permission is hereby granted, without written agreement and without
* license or royalty fees, to use, copy, modify, and distribute this
* software and to distribute modified versions of this software for any
* purpose, provided that the above copyright notice and the following
* two paragraphs appear in all copies of this software.
*
* In no event shall Paul Falstad or the Zsh Development Group be liable
* to any party for direct, indirect, special, incidental, or consequential
* damages arising out of the use of this software and its documentation,
* even if Paul Falstad and the Zsh Development Group have been advised of
* the possibility of such damage.
*
* Paul Falstad and the Zsh Development Group specifically disclaim any
* warranties, including, but not limited to, the implied warranties of
* merchantability and fitness for a particular purpose. The software
* provided hereunder is on an "as is" basis, and Paul Falstad and the
* Zsh Development Group have no obligation to provide maintenance,
* support, updates, enhancements, or modifications.
*
*/
/*
* This file deals with input buffering, supplying characters to the
* history expansion code a character at a time. Input is stored on a
* stack, which allows insertion of strings into the input, possibly with
* flags marking the end of alias expansion, with minimal copying of
* strings. The same stack is used to record the fact that the input
* is a history or alias expansion and to store the alias while it is in use.
*
* Input is taken either from zle, if appropriate, or read directly from
* the input file, or may be supplied by some other part of the shell (such
* as `eval' or $(...) substitution). In the last case, it should be
* supplied by pushing a new level onto the stack, via inpush(input_string,
* flag, alias); if the current input really needs to be altered, use
* inputsetline(input_string, flag). `Flag' can include or's of INP_FREE
* (if the input string is to be freed when used), INP_CONT (if the input
* is to continue onto what's already in the input queue), INP_ALIAS
* (push supplied alias onto stack) or INP_HIST (ditto, but used to
* mark history expansion). `alias' is ignored unless INP_ALIAS or
* INP_HIST is supplied. INP_ALIAS is always set if INP_HIST is.
*
* Note that the input string is itself used as the input buffer: it is not
* copied, nor is it every written back to, so using a constant string
* should work. Consequently, when passing areas of memory from the heap
* it is necessary that that heap last as long as the operation of reading
* the string. After the string is read, the stack should be popped with
* inpop(), which effectively flushes any unread input as well as restoring
* the previous input state.
*
* The internal flags INP_ALCONT and INP_HISTCONT show that the stack
* element was pushed by an alias or history expansion; they should not
* be needed elsewhere.
*
* The global variable inalmore is set to indicate aliases should
* continue to be expanded because the last alias expansion ended
* in a space. It is only reset after a complete word was read
* without expanding a new alias, in exalias().
*
* PWS 1996/12/10
*/
#ifdef HAVE_STDIO_H
#include <stdio.h>
#endif
#include "zsh.mdh"
#include "input.pro"
/* the shell input fd */
/**/
int SHIN;
/* != 0 means we are reading input from a string */
/**/
int strin;
/* total # of characters waiting to be read. */
/**/
mod_export int inbufct;
/* the flags controlling the input routines in input.c: see INP_* in zsh.h */
/**/
int inbufflags;
static char *inbuf; /* Current input buffer */
static char *inbufptr; /* Pointer into input buffer */
static char *inbufpush; /* Character at which to re-push alias */
static int inbufleft; /* Characters left in current input
stack element */
/* Input must be stacked since the input queue is used by
* various different parts of the shell.
*/
struct instacks {
char *buf, *bufptr;
Alias alias;
int bufleft, bufct, flags;
};
static struct instacks *instack, *instacktop;
/*
* Input stack size. We need to push the stack for aliases, history
* expansion, and reading from internal strings: only if these operations
* are nested do we need more than one extra level. Thus we shouldn't need
* too much space as a rule. Initially, INSTACK_INITIAL is allocated; if
* more is required, an extra INSTACK_EXPAND is added each time.
*/
#define INSTACK_INITIAL 4
#define INSTACK_EXPAND 4
static int instacksz = INSTACK_INITIAL;
/* 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 */
static 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, rsize = isset(SHINSTDIN) ? 1 : SHINBUFSIZE;
if (shinbufptr < shinbufendptr)
return (unsigned char) *shinbufptr++;
shinbufreset();
#ifdef USE_LSEEK
if (rsize == 1 && lseek(SHIN, 0, SEEK_CUR) != (off_t)-1)
rsize = SHINBUFSIZE;
if (rsize > 1) {
do {
errno = 0;
nread = read(SHIN, shinbuffer, rsize);
} while (nread < 0 && errno == EINTR);
if (nread <= 0)
return -1;
if (isset(SHINSTDIN) &&
(shinbufendptr = memchr(shinbuffer, '\n', nread))) {
shinbufendptr++;
rsize = (shinbufendptr - shinbuffer);
if (nread > rsize &&
lseek(SHIN, -(nread - rsize), SEEK_CUR) < 0)
zerr("lseek(%d, %d): %e", SHIN, -(nread - rsize), errno);
} else
shinbufendptr = shinbuffer + nread;
return (unsigned char) *shinbufptr++;
}
#endif
for (;;) {
errno = 0;
nread = read(SHIN, shinbufendptr, 1);
if (nread > 0) {
/* Use line buffering (POSIX requirement) */
if (*shinbufendptr++ == '\n')
break;
if (shinbufendptr == shinbuffer + SHINBUFSIZE)
break;
} else if (nread == 0 || errno != EINTR)
break;
}
if (shinbufendptr == shinbuffer)
return -1;
return (unsigned char) *shinbufptr++;
}
/* Read a line from SHIN. Convert tokens and *
* null characters to Meta c^32 character pairs. */
/**/
mod_export char *
shingetline(void)
{
char *line = NULL;
int ll = 0;
int c;
char buf[BUFSIZ];
char *p;
int q = queue_signal_level();
p = buf;
winch_unblock();
dont_queue_signals();
for (;;) {
c = shingetchar();
if (c < 0 || c == '\n') {
winch_block();
restore_queue_signals(q);
if (c == '\n')
*p++ = '\n';
if (p > buf) {
*p++ = '\0';
line = zrealloc(line, ll + (p - buf));
memcpy(line + ll, buf, p - buf);
}
return line;
}
if (imeta(c)) {
*p++ = Meta;
*p++ = c ^ 32;
} else
*p++ = c;
if (p >= buf + BUFSIZ - 1) {
winch_block();
queue_signals();
line = zrealloc(line, ll + (p - buf) + 1);
memcpy(line + ll, buf, p - buf);
ll += p - buf;
line[ll] = '\0';
p = buf;
winch_unblock();
dont_queue_signals();
}
}
}
/* Get the next character from the input.
* Will call inputline() to get a new line where necessary.
*/
/**/
int
ingetc(void)
{
int lastc = ' ';
if (lexstop)
return ' ';
for (;;) {
if (inbufleft) {
inbufleft--;
inbufct--;
if (itok(lastc = (unsigned char) *inbufptr++))
continue;
if (((inbufflags & INP_LINENO) || !strin) && lastc == '\n')
lineno++;
break;
}
/*
* See if we have reached the end of input
* (due to an error, or to reading from a single string).
* Check the remaining characters left, since if there aren't
* any we don't want to pop the stack---it'll mark any aliases
* as not in use before we've finished processing.
*/
if (!inbufct && (strin || errflag)) {
lexstop = 1;
break;
}
/* If the next element down the input stack is a continuation of
* this, use it.
*/
if (inbufflags & INP_CONT) {
inpoptop();
continue;
}
/* As a last resort, get some more input */
if (inputline())
break;
}
if (!lexstop)
zshlex_raw_add(lastc);
return lastc;
}
/* Read a line from the current command stream and store it as input */
/**/
static int
inputline(void)
{
char *ingetcline, **ingetcpmptl = NULL, **ingetcpmptr = NULL;
int context = ZLCON_LINE_START;
/* If reading code interactively, work out the prompts. */
if (interact && isset(SHINSTDIN)) {
if (!isfirstln) {
ingetcpmptl = &prompt2;
if (rprompt2)
ingetcpmptr = &rprompt2;
context = ZLCON_LINE_CONT;
}
else {
ingetcpmptl = &prompt;
if (rprompt)
ingetcpmptr = &rprompt;
}
}
if (!(interact && isset(SHINSTDIN) && SHTTY != -1 && isset(USEZLE))) {
/*
* If not using zle, read the line straight from the input file.
* Possibly we don't get the whole line at once: in that case,
* we get another chunk with the next call to inputline().
*/
if (interact && isset(SHINSTDIN)) {
/*
* We may still be interactive (e.g. running under emacs),
* so output a prompt if necessary. We don't know enough
* about the input device to be able to handle an rprompt,
* though.
*/
char *pptbuf;
int pptlen;
pptbuf = unmetafy(promptexpand(ingetcpmptl ? *ingetcpmptl : NULL,
0, NULL, NULL), &pptlen);
write_loop(2, pptbuf, pptlen);
free(pptbuf);
}
ingetcline = shingetline();
} else {
/*
* Since we may have to read multiple lines before getting
* a complete piece of input, we tell zle not to restore the
* original tty settings after reading each chunk. Instead,
* this is done when the history mechanism for the current input
* terminates, which is not until we have the whole input.
* This is supposed to minimise problems on systems that clobber
* typeahead when the terminal settings are altered.
* pws 1998/03/12
*/
int flags = ZLRF_HISTORY|ZLRF_NOSETTY;
if (isset(IGNOREEOF))
flags |= ZLRF_IGNOREEOF;
ingetcline = zleentry(ZLE_CMD_READ, ingetcpmptl, ingetcpmptr,
flags, context);
histdone |= HISTFLAG_SETTY;
}
if (!ingetcline) {
return lexstop = 1;
}
if (errflag) {
free(ingetcline);
errflag |= ERRFLAG_ERROR;
return lexstop = 1;
}
if (isset(VERBOSE)) {
/* Output the whole line read so far. */
zputs(ingetcline, stderr);
fflush(stderr);
}
if (keyboardhackchar && *ingetcline &&
ingetcline[strlen(ingetcline) - 1] == '\n' &&
interact && isset(SHINSTDIN) &&
SHTTY != -1 && ingetcline[1])
{
char *stripptr = ingetcline + strlen(ingetcline) - 2;
if (*stripptr == keyboardhackchar) {
/* Junk an unwanted character at the end of the line.
(key too close to return key) */
int ct = 1; /* force odd */
char *ptr;
if (keyboardhackchar == '\'' || keyboardhackchar == '"' ||
keyboardhackchar == '`') {
/*
* for the chars above, also require an odd count before
* junking
*/
for (ct = 0, ptr = ingetcline; *ptr; ptr++)
if (*ptr == keyboardhackchar)
ct++;
}
if (ct & 1) {
stripptr[0] = '\n';
stripptr[1] = '\0';
}
}
}
isfirstch = 1;
if ((inbufflags & INP_APPEND) && inbuf) {
/*
* We need new input but need to be able to back up
* over the old input, so append this line.
* Pushing the line onto the stack doesn't have the right
* effect.
*
* This is quite a simple and inefficient fix, but currently
* we only need it when backing up over a multi-line $((...
* that turned out to be a command substitution rather than
* a math substitution, which is a very special case.
* So it's not worth rewriting.
*/
char *oinbuf = inbuf;
int newlen = strlen(ingetcline);
int oldlen = (int)(inbufptr - inbuf) + inbufleft;
if (inbufflags & INP_FREE) {
inbuf = realloc(inbuf, oldlen + newlen + 1);
} else {
inbuf = zalloc(oldlen + newlen + 1);
memcpy(inbuf, oinbuf, oldlen);
}
inbufptr += inbuf - oinbuf;
strcpy(inbuf + oldlen, ingetcline);
free(ingetcline);
inbufleft += newlen;
inbufct += newlen;
inbufflags |= INP_FREE;
} else {
/* Put this into the input channel. */
inputsetline(ingetcline, INP_FREE);
}
return 0;
}
/*
* Put a string in the input queue:
* inbuf is only freeable if the flags include INP_FREE.
*/
/**/
static void
inputsetline(char *str, int flags)
{
queue_signals();
if ((inbufflags & INP_FREE) && inbuf) {
free(inbuf);
}
inbuf = inbufptr = str;
inbufleft = strlen(inbuf);
/*
* inbufct must reflect the total number of characters left,
* as it used by other parts of the shell, so we need to take account
* of whether the input stack continues, and whether there
* is an extra space to add on at the end.
*/
if (flags & INP_CONT)
inbufct += inbufleft;
else
inbufct = inbufleft;
inbufflags = flags;
unqueue_signals();
}
/*
* Backup one character of the input.
* The last character can always be backed up, provided we didn't just
* expand an alias or a history reference.
* In fact, the character is ignored and the previous character is used.
* (If that's wrong, the bug is in the calling code. Use the #ifdef DEBUG
* code to check.)
*/
/**/
void
inungetc(int c)
{
if (!lexstop) {
if (inbufptr != inbuf) {
#ifdef DEBUG
/* Just for debugging: enable only if foul play suspected. */
if (inbufptr[-1] != (char) c)
fprintf(stderr, "Warning: backing up wrong character.\n");
#endif
/* Just decrement the pointer: if it's not the same
* character being pushed back, we're in trouble anyway.
*/
inbufptr--;
inbufct++;
inbufleft++;
if (((inbufflags & INP_LINENO) || !strin) && c == '\n')
lineno--;
}
else if (!(inbufflags & INP_CONT)) {
#ifdef DEBUG
/* Just for debugging */
fprintf(stderr, "Attempt to inungetc() at start of input.\n");
#endif
zerr("Garbled input at %c (binary file as commands?)", c);
return;
}
else {
/*
* The character is being backed up from a previous input stack
* layer. However, there was an expansion in the middle, so we
* can't back up where we want to. Instead, we just push it
* onto the input stack as an extra character.
*/
char *cback = (char *)zshcalloc(2);
cback[0] = (char) c;
inpush(cback, INP_FREE|INP_CONT, NULL);
}
/* If we are back at the start of a segment,
* we may need to restore an alias popped from the stack.
* Note this may be a dummy (history expansion) entry.
*/
if (inbufptr == inbufpush &&
(inbufflags & (INP_ALCONT|INP_HISTCONT))) {
/*
* Go back up the stack over all entries which were alias
* expansions and were pushed with nothing remaining to read.
*/
do {
if (instacktop->alias)
instacktop->alias->inuse = 1;
instacktop++;
} while ((instacktop->flags & (INP_ALCONT|INP_HISTCONT))
&& !instacktop->bufleft);
if (inbufflags & INP_HISTCONT)
inbufflags = INP_CONT|INP_ALIAS|INP_HIST;
else
inbufflags = INP_CONT|INP_ALIAS;
inbufleft = 0;
inbuf = inbufptr = "";
}
zshlex_raw_back();
}
}
/* stuff a whole file into memory and return it */
/**/
off_t
zstuff(char **out, const char *fn)
{
FILE *in;
char *buf;
off_t len;
if (!(in = fopen(unmeta(fn), "r"))) {
zerr("can't open %s", fn);
return -1;
}
queue_signals();
fseek(in, 0, SEEK_END);
len = ftell(in);
fseek(in, 0, SEEK_SET);
buf = (char *)zalloc(len + 1);
if (len && !(fread(buf, len, 1, in))) {
zerr("read error on %s", fn);
fclose(in);
zfree(buf, len + 1);
unqueue_signals();
return -1;
}
fclose(in);
buf[len] = '\0';
*out = buf;
unqueue_signals();
return len;
}
/* stuff a whole file into the input queue and print it */
/**/
int
stuff(char *fn)
{
char *buf;
off_t len = zstuff(&buf, fn);
if (len < 0)
return 1;
fwrite(buf, len, 1, stderr);
fflush(stderr);
inputsetline(metafy(buf, len, META_REALLOC), INP_FREE);
return 0;
}
/* flush input queue */
/**/
void
inerrflush(void)
{
while (!lexstop && inbufct)
ingetc();
}
/* Set some new input onto a new element of the input stack */
/**/
mod_export void
inpush(char *str, int flags, Alias inalias)
{
if (!instack) {
/* Initial stack allocation */
instack = (struct instacks *)zalloc(instacksz*sizeof(struct instacks));
instacktop = instack;
}
instacktop->buf = inbuf;
instacktop->bufptr = inbufptr;
instacktop->bufleft = inbufleft;
instacktop->bufct = inbufct;
inbufflags &= ~(INP_ALCONT|INP_HISTCONT);
if (flags & (INP_ALIAS|INP_HIST)) {
/*
* Text is expansion for history or alias, so continue
* back to old level when done. Also mark stack top
* as alias continuation so as to back up if necessary,
* and mark alias as in use.
*/
flags |= INP_CONT|INP_ALIAS;
if (flags & INP_HIST)
instacktop->flags = inbufflags | INP_HISTCONT;
else
instacktop->flags = inbufflags | INP_ALCONT;
if ((instacktop->alias = inalias))
inalias->inuse = 1;
} else {
instacktop->alias = NULL;
/* If we are continuing an alias expansion, record the alias
* expansion in new set of flags (do we need this?)
*/
if (((instacktop->flags = inbufflags) & INP_ALIAS) &&
(flags & INP_CONT))
flags |= INP_ALIAS;
}
instacktop++;
if (instacktop == instack + instacksz) {
/* Expand the stack */
instack = (struct instacks *)
realloc(instack,
(instacksz + INSTACK_EXPAND)*sizeof(struct instacks));
instacktop = instack + instacksz;
instacksz += INSTACK_EXPAND;
}
/*
* We maintain the entry above the highest one with real
* text as a flag to inungetc() that it can stop re-pushing the stack.
*/
instacktop->flags = 0;
inbufpush = inbuf = NULL;
inputsetline(str, flags);
}
/* Remove the top element of the stack */
/**/
static void
inpoptop(void)
{
if (!lexstop) {
inbufflags &= ~(INP_ALCONT|INP_HISTCONT);
while (inbufptr > inbuf) {
inbufptr--;
inbufct++;
inbufleft++;
/*
* As elsewhere in input and history mechanisms:
* unwinding aliases and unwinding history have different
* implications as aliases are after the lexer while
* history is before, but they're both pushed onto
* the input stack.
*/
if ((inbufflags & (INP_ALIAS|INP_HIST|INP_RAW_KEEP)) == INP_ALIAS)
zshlex_raw_back();
}
}
if (inbuf && (inbufflags & INP_FREE))
free(inbuf);
instacktop--;
inbuf = instacktop->buf;
inbufptr = inbufpush = instacktop->bufptr;
inbufleft = instacktop->bufleft;
inbufct = instacktop->bufct;
inbufflags = instacktop->flags;
if (!(inbufflags & (INP_ALCONT|INP_HISTCONT)))
return;
if (instacktop->alias) {
char *t = instacktop->alias->text;
/* a real alias: mark it as unused. */
instacktop->alias->inuse = 0;
if (*t && t[strlen(t) - 1] == ' ') {
inalmore = 1;
histbackword();
}
}
}
/* Remove the top element of the stack and all its continuations. */
/**/
mod_export void
inpop(void)
{
int remcont;
do {
remcont = inbufflags & INP_CONT;
inpoptop();
} while (remcont);
}
/*
* Expunge any aliases from the input stack; they shouldn't appear
* in the history and need to be flushed explicitly when we encounter
* an error.
*/
/**/
void
inpopalias(void)
{
while (inbufflags & INP_ALIAS)
inpoptop();
}
/*
* Get pointer to remaining string to read.
*/
/**/
char *
ingetptr(void)
{
return inbufptr;
}
/*
* Check if the current input line, including continuations, is
* expanding an alias. This does not detect alias expansions that
* have been fully processed and popped from the input stack.
* If there is an alias, the most recently expanded is returned,
* else NULL.
*/
/**/
char *input_hasalias(void)
{
int flags = inbufflags;
struct instacks *instackptr = instacktop;
char *alias_nam = NULL;
for (;;)
{
if (!(flags & INP_CONT))
break;
DPUTS(instackptr == instack, "BUG: continuation at bottom of instack");
instackptr--;
if (instackptr->alias)
alias_nam = instackptr->alias->node.nam;
flags = instackptr->flags;
}
return alias_nam;
}