mirror of
https://github.com/zsh-users/zsh
synced 2024-09-06 08:25:56 +00:00
566 lines
15 KiB
C
566 lines
15 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 flag INP_ALCONT shows that the stack element was pushed
|
|
* by an alias expansion; it 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;
|
|
|
|
/* buffered shell input for non-interactive shells */
|
|
|
|
/**/
|
|
FILE *bshin;
|
|
|
|
/* != 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;
|
|
|
|
/* Read a line from bshin. 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;
|
|
|
|
p = buf;
|
|
for (;;) {
|
|
do {
|
|
errno = 0;
|
|
c = fgetc(bshin);
|
|
} while (c < 0 && errno == EINTR);
|
|
if (c < 0 || c == '\n') {
|
|
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) {
|
|
line = zrealloc(line, ll + (p - buf) + 1);
|
|
memcpy(line + ll, buf, p - buf);
|
|
ll += p - buf;
|
|
line[ll] = '\0';
|
|
p = buf;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* 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 = STOUC(*inbufptr++)))
|
|
continue;
|
|
if (((inbufflags & INP_LINENO) || !strin) && lastc == '\n')
|
|
lineno++;
|
|
return lastc;
|
|
}
|
|
|
|
/* If the next element down the input stack is a continuation of
|
|
* this, use it.
|
|
*/
|
|
if (inbufflags & INP_CONT) {
|
|
inpoptop();
|
|
continue;
|
|
}
|
|
/*
|
|
* Otherwise, see if we have reached the end of input
|
|
* (due to an error, or to reading from a single string).
|
|
*/
|
|
if (strin || errflag) {
|
|
lexstop = 1;
|
|
return ' ';
|
|
}
|
|
/* As a last resort, get some more input */
|
|
if (inputline())
|
|
return ' ';
|
|
}
|
|
}
|
|
|
|
/* Read a line from the current command stream and store it as input */
|
|
|
|
/**/
|
|
static int
|
|
inputline(void)
|
|
{
|
|
char *ingetcline, *ingetcpmptl = NULL, *ingetcpmptr = NULL;
|
|
|
|
/* If reading code interactively, work out the prompts. */
|
|
if (interact && isset(SHINSTDIN)) {
|
|
if (!isfirstln) {
|
|
ingetcpmptl = prompt2;
|
|
if (rprompt2)
|
|
ingetcpmptr = rprompt2;
|
|
}
|
|
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, 0, NULL, NULL), &pptlen);
|
|
write(2, (WRITE_ARG_2_T)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 = (char *)zleread(ingetcpmptl, ingetcpmptr, flags);
|
|
histdone |= HISTFLAG_SETTY;
|
|
}
|
|
if (!ingetcline) {
|
|
return lexstop = 1;
|
|
}
|
|
if (errflag) {
|
|
free(ingetcline);
|
|
return lexstop = errflag = 1;
|
|
}
|
|
if (isset(VERBOSE)) {
|
|
/* Output the whole line read so far. */
|
|
zputs(ingetcline, stderr);
|
|
fflush(stderr);
|
|
}
|
|
if (*ingetcline && ingetcline[strlen(ingetcline) - 1] == '\n' &&
|
|
interact && isset(SUNKEYBOARDHACK) && isset(SHINSTDIN) &&
|
|
SHTTY != -1 && *ingetcline && ingetcline[1] &&
|
|
ingetcline[strlen(ingetcline) - 2] == '`') {
|
|
/* Junk an unmatched "`" at the end of the line. */
|
|
int ct;
|
|
char *ptr;
|
|
|
|
for (ct = 0, ptr = ingetcline; *ptr; ptr++)
|
|
if (*ptr == '`')
|
|
ct++;
|
|
if (ct & 1) {
|
|
ptr[-2] = '\n';
|
|
ptr[-1] = '\0';
|
|
}
|
|
}
|
|
isfirstch = 1;
|
|
/* 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)
|
|
{
|
|
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;
|
|
}
|
|
|
|
/*
|
|
* 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--;
|
|
}
|
|
#ifdef DEBUG
|
|
else if (!(inbufflags & INP_CONT)) {
|
|
/* Just for debugging */
|
|
fprintf(stderr, "Attempt to inungetc() at start of input.\n");
|
|
}
|
|
#endif
|
|
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 *)zcalloc(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) {
|
|
/*
|
|
* 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) && !instacktop->bufleft);
|
|
inbufflags = INP_CONT|INP_ALIAS;
|
|
inbufleft = 0;
|
|
inbuf = inbufptr = "";
|
|
}
|
|
}
|
|
}
|
|
|
|
/* stuff a whole file into the input queue and print it */
|
|
|
|
/**/
|
|
int
|
|
stuff(char *fn)
|
|
{
|
|
FILE *in;
|
|
char *buf;
|
|
off_t len;
|
|
|
|
if (!(in = fopen(unmeta(fn), "r"))) {
|
|
zerr("can't open %s", fn, 0);
|
|
return 1;
|
|
}
|
|
fseek(in, 0, 2);
|
|
len = ftell(in);
|
|
fseek(in, 0, 0);
|
|
buf = (char *)zalloc(len + 1);
|
|
if (!(fread(buf, len, 1, in))) {
|
|
zerr("read error on %s", fn, 0);
|
|
fclose(in);
|
|
zfree(buf, len + 1);
|
|
return 1;
|
|
}
|
|
fclose(in);
|
|
buf[len] = '\0';
|
|
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;
|
|
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;
|
|
instacktop->flags = inbufflags | INP_ALCONT;
|
|
if ((instacktop->alias = inalias))
|
|
inalias->inuse = 1;
|
|
} else {
|
|
/* 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 (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))
|
|
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();
|
|
}
|