0,0 → 1,531 |
/* $NetBSD: input.c,v 1.34 2000/05/22 10:18:47 elric Exp $ */ |
|
/*- |
* Copyright (c) 1991, 1993 |
* The Regents of the University of California. All rights reserved. |
* |
* This code is derived from software contributed to Berkeley by |
* Kenneth Almquist. |
* |
* Redistribution and use in source and binary forms, with or without |
* modification, are permitted provided that the following conditions |
* are met: |
* 1. Redistributions of source code must retain the above copyright |
* notice, this list of conditions and the following disclaimer. |
* 2. Redistributions in binary form must reproduce the above copyright |
* notice, this list of conditions and the following disclaimer in the |
* documentation and/or other materials provided with the distribution. |
* 3. All advertising materials mentioning features or use of this software |
* must display the following acknowledgement: |
* This product includes software developed by the University of |
* California, Berkeley and its contributors. |
* 4. Neither the name of the University nor the names of its contributors |
* may be used to endorse or promote products derived from this software |
* without specific prior written permission. |
* |
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND |
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE |
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
* SUCH DAMAGE. |
*/ |
|
#include <sys/cdefs.h> |
#ifndef lint |
#if 0 |
static char sccsid[] = "@(#)input.c 8.3 (Berkeley) 6/9/95"; |
#else |
__RCSID("$NetBSD: input.c,v 1.34 2000/05/22 10:18:47 elric Exp $"); |
#endif |
#endif /* not lint */ |
|
#include <stdio.h> /* defines BUFSIZ */ |
#include <fcntl.h> |
#include <errno.h> |
#include <unistd.h> |
#include <stdlib.h> |
#include <string.h> |
|
/* |
* This file implements the input routines used by the parser. |
*/ |
|
#include "shell.h" |
#include "redir.h" |
#include "syntax.h" |
#include "input.h" |
#include "output.h" |
#include "options.h" |
#include "memalloc.h" |
#include "error.h" |
#include "alias.h" |
#include "parser.h" |
#ifndef SMALL |
#include "myhistedit.h" |
#endif |
|
#ifdef HETIO |
#include "hetio.h" |
#endif |
|
#define EOF_NLEFT -99 /* value of parsenleft when EOF pushed back */ |
|
MKINIT |
struct strpush { |
struct strpush *prev; /* preceding string on stack */ |
char *prevstring; |
int prevnleft; |
int prevlleft; |
struct alias *ap; /* if push was associated with an alias */ |
}; |
|
/* |
* The parsefile structure pointed to by the global variable parsefile |
* contains information about the current file being read. |
*/ |
|
MKINIT |
struct parsefile { |
struct parsefile *prev; /* preceding file on stack */ |
int linno; /* current line */ |
int fd; /* file descriptor (or -1 if string) */ |
int nleft; /* number of chars left in this line */ |
int lleft; /* number of chars left in this buffer */ |
char *nextc; /* next char in buffer */ |
char *buf; /* input buffer */ |
struct strpush *strpush; /* for pushing strings at this level */ |
struct strpush basestrpush; /* so pushing one is fast */ |
}; |
|
|
int plinno = 1; /* input line number */ |
MKINIT int parsenleft; /* copy of parsefile->nleft */ |
MKINIT int parselleft; /* copy of parsefile->lleft */ |
char *parsenextc; /* copy of parsefile->nextc */ |
MKINIT struct parsefile basepf; /* top level input file */ |
char basebuf[BUFSIZ]; /* buffer for top level input file */ |
struct parsefile *parsefile = &basepf; /* current input file */ |
int init_editline = 0; /* editline library initialized? */ |
int whichprompt; /* 1 == PS1, 2 == PS2 */ |
|
#ifndef SMALL |
EditLine *el; /* cookie for editline package */ |
#endif |
|
STATIC void pushfile (void); |
static int preadfd (void); |
|
#ifdef mkinit |
INCLUDE "input.h" |
INCLUDE "error.h" |
|
INIT { |
extern char basebuf[]; |
|
basepf.nextc = basepf.buf = basebuf; |
} |
|
RESET { |
if (exception != EXSHELLPROC) |
parselleft = parsenleft = 0; /* clear input buffer */ |
popallfiles(); |
} |
|
SHELLPROC { |
popallfiles(); |
} |
#endif |
|
|
/* |
* Read a line from the script. |
*/ |
|
char * |
pfgets(line, len) |
char *line; |
int len; |
{ |
char *p = line; |
int nleft = len; |
int c; |
|
while (--nleft > 0) { |
c = pgetc_macro(); |
if (c == PEOF) { |
if (p == line) |
return NULL; |
break; |
} |
*p++ = c; |
if (c == '\n') |
break; |
} |
*p = '\0'; |
return line; |
} |
|
|
|
/* |
* Read a character from the script, returning PEOF on end of file. |
* Nul characters in the input are silently discarded. |
*/ |
|
int |
pgetc() |
{ |
return pgetc_macro(); |
} |
|
|
static int |
preadfd() |
{ |
int nr; |
char *buf = parsefile->buf; |
parsenextc = buf; |
|
retry: |
#ifndef SMALL |
if (parsefile->fd == 0 && el) { |
const char *rl_cp; |
|
rl_cp = el_gets(el, &nr); |
if (rl_cp == NULL) |
nr = 0; |
else { |
/* XXX - BUFSIZE should redesign so not necessary */ |
(void) strcpy(buf, rl_cp); |
} |
} else |
#endif |
|
#ifdef HETIO |
nr = hetio_read_input(parsefile->fd); |
if (nr == -255) |
#endif |
nr = read(parsefile->fd, buf, BUFSIZ - 1); |
|
|
if (nr <= 0) { |
if (nr < 0) { |
if (errno == EINTR) |
goto retry; |
if (parsefile->fd == 0 && errno == EWOULDBLOCK) { |
int flags = fcntl(0, F_GETFL, 0); |
if (flags >= 0 && flags & O_NONBLOCK) { |
flags &=~ O_NONBLOCK; |
if (fcntl(0, F_SETFL, flags) >= 0) { |
out2str("sh: turning off NDELAY mode\n"); |
goto retry; |
} |
} |
} |
} |
nr = -1; |
} |
return nr; |
} |
|
/* |
* Refill the input buffer and return the next input character: |
* |
* 1) If a string was pushed back on the input, pop it; |
* 2) If an EOF was pushed back (parsenleft == EOF_NLEFT) or we are reading |
* from a string so we can't refill the buffer, return EOF. |
* 3) If the is more stuff in this buffer, use it else call read to fill it. |
* 4) Process input up to the next newline, deleting nul characters. |
*/ |
|
int |
preadbuffer() |
{ |
char *p, *q; |
int more; |
int something; |
char savec; |
|
if (parsefile->strpush) { |
popstring(); |
if (--parsenleft >= 0) |
return (*parsenextc++); |
} |
if (parsenleft == EOF_NLEFT || parsefile->buf == NULL) |
return PEOF; |
flushout(&output); |
flushout(&errout); |
|
again: |
if (parselleft <= 0) { |
if ((parselleft = preadfd()) == -1) { |
parselleft = parsenleft = EOF_NLEFT; |
return PEOF; |
} |
} |
|
q = p = parsenextc; |
|
/* delete nul characters */ |
something = 0; |
for (more = 1; more;) { |
switch (*p) { |
case '\0': |
p++; /* Skip nul */ |
goto check; |
|
case '\t': |
case ' ': |
break; |
|
case '\n': |
parsenleft = q - parsenextc; |
more = 0; /* Stop processing here */ |
break; |
|
default: |
something = 1; |
break; |
} |
|
*q++ = *p++; |
check: |
if (--parselleft <= 0) { |
parsenleft = q - parsenextc - 1; |
if (parsenleft < 0) |
goto again; |
*q = '\0'; |
more = 0; |
} |
} |
|
savec = *q; |
*q = '\0'; |
|
#ifndef SMALL |
if (parsefile->fd == 0 && hist && something) { |
HistEvent he; |
INTOFF; |
history(hist, &he, whichprompt == 1? H_ENTER : H_APPEND, |
parsenextc); |
INTON; |
} |
#endif |
|
if (vflag) { |
out2str(parsenextc); |
flushout(out2); |
} |
|
*q = savec; |
|
return *parsenextc++; |
} |
|
/* |
* Undo the last call to pgetc. Only one character may be pushed back. |
* PEOF may be pushed back. |
*/ |
|
void |
pungetc() { |
parsenleft++; |
parsenextc--; |
} |
|
/* |
* Push a string back onto the input at this current parsefile level. |
* We handle aliases this way. |
*/ |
void |
pushstring(s, len, ap) |
char *s; |
int len; |
void *ap; |
{ |
struct strpush *sp; |
|
INTOFF; |
/*dprintf("*** calling pushstring: %s, %d\n", s, len);*/ |
if (parsefile->strpush) { |
sp = ckmalloc(sizeof (struct strpush)); |
sp->prev = parsefile->strpush; |
parsefile->strpush = sp; |
} else |
sp = parsefile->strpush = &(parsefile->basestrpush); |
sp->prevstring = parsenextc; |
sp->prevnleft = parsenleft; |
sp->prevlleft = parselleft; |
sp->ap = (struct alias *)ap; |
if (ap) |
((struct alias *)ap)->flag |= ALIASINUSE; |
parsenextc = s; |
parsenleft = len; |
INTON; |
} |
|
void |
popstring() |
{ |
struct strpush *sp = parsefile->strpush; |
|
INTOFF; |
parsenextc = sp->prevstring; |
parsenleft = sp->prevnleft; |
parselleft = sp->prevlleft; |
/*dprintf("*** calling popstring: restoring to '%s'\n", parsenextc);*/ |
if (sp->ap) |
sp->ap->flag &= ~ALIASINUSE; |
parsefile->strpush = sp->prev; |
if (sp != &(parsefile->basestrpush)) |
ckfree(sp); |
INTON; |
} |
|
/* |
* Set the input to take input from a file. If push is set, push the |
* old input onto the stack first. |
*/ |
|
void |
setinputfile(fname, push) |
const char *fname; |
int push; |
{ |
int fd; |
int fd2; |
|
INTOFF; |
if ((fd = open(fname, O_RDONLY)) < 0) |
error("Can't open %s", fname); |
if (fd < 10) { |
fd2 = copyfd(fd, 10); |
close(fd); |
if (fd2 < 0) |
error("Out of file descriptors"); |
fd = fd2; |
} |
setinputfd(fd, push); |
INTON; |
} |
|
|
/* |
* Like setinputfile, but takes an open file descriptor. Call this with |
* interrupts off. |
*/ |
|
void |
setinputfd(fd, push) |
int fd, push; |
{ |
(void) fcntl(fd, F_SETFD, FD_CLOEXEC); |
if (push) { |
pushfile(); |
parsefile->buf = ckmalloc(BUFSIZ); |
} |
if (parsefile->fd > 0) |
close(parsefile->fd); |
parsefile->fd = fd; |
if (parsefile->buf == NULL) |
parsefile->buf = ckmalloc(BUFSIZ); |
parselleft = parsenleft = 0; |
plinno = 1; |
} |
|
|
/* |
* Like setinputfile, but takes input from a string. |
*/ |
|
void |
setinputstring(string, push) |
char *string; |
int push; |
{ |
INTOFF; |
if (push) |
pushfile(); |
parsenextc = string; |
parselleft = parsenleft = strlen(string); |
parsefile->buf = NULL; |
plinno = 1; |
INTON; |
} |
|
|
|
/* |
* To handle the "." command, a stack of input files is used. Pushfile |
* adds a new entry to the stack and popfile restores the previous level. |
*/ |
|
STATIC void |
pushfile() { |
struct parsefile *pf; |
|
parsefile->nleft = parsenleft; |
parsefile->lleft = parselleft; |
parsefile->nextc = parsenextc; |
parsefile->linno = plinno; |
pf = (struct parsefile *)ckmalloc(sizeof (struct parsefile)); |
pf->prev = parsefile; |
pf->fd = -1; |
pf->strpush = NULL; |
pf->basestrpush.prev = NULL; |
parsefile = pf; |
} |
|
|
void |
popfile() { |
struct parsefile *pf = parsefile; |
|
INTOFF; |
if (pf->fd >= 0) |
close(pf->fd); |
if (pf->buf) |
ckfree(pf->buf); |
while (pf->strpush) |
popstring(); |
parsefile = pf->prev; |
ckfree(pf); |
parsenleft = parsefile->nleft; |
parselleft = parsefile->lleft; |
parsenextc = parsefile->nextc; |
plinno = parsefile->linno; |
INTON; |
} |
|
|
/* |
* Return to top level. |
*/ |
|
void |
popallfiles() { |
while (parsefile != &basepf) |
popfile(); |
} |
|
|
|
/* |
* Close the file(s) that the shell is reading commands from. Called |
* after a fork is done. |
*/ |
|
void |
closescript() { |
popallfiles(); |
if (parsefile->fd > 0) { |
close(parsefile->fd); |
parsefile->fd = 0; |
} |
} |