681 lines
11 KiB
C
Executable file
681 lines
11 KiB
C
Executable file
#define Extern extern
|
|
#include <sys/types.h>
|
|
#include <signal.h>
|
|
#include <errno.h>
|
|
#include <setjmp.h>
|
|
#include "sh.h"
|
|
|
|
/* -------- io.c -------- */
|
|
/* #include "sh.h" */
|
|
|
|
/*
|
|
* shell IO
|
|
*/
|
|
|
|
static struct iobuf sharedbuf = {AFID_NOBUF};
|
|
static struct iobuf mainbuf = {AFID_NOBUF};
|
|
static unsigned bufid = AFID_ID; /* buffer id counter */
|
|
|
|
struct ioarg temparg = {0, 0, 0, AFID_NOBUF, 0};
|
|
|
|
_PROTOTYPE(static void readhere, (char **name, char *s, int ec ));
|
|
_PROTOTYPE(void pushio, (struct ioarg *argp, int (*fn)()));
|
|
_PROTOTYPE(static int xxchar, (struct ioarg *ap ));
|
|
_PROTOTYPE(void tempname, (char *tname ));
|
|
|
|
int
|
|
getc(ec)
|
|
register int ec;
|
|
{
|
|
register int c;
|
|
|
|
if(e.linep > elinep) {
|
|
while((c=readc()) != '\n' && c)
|
|
;
|
|
err("input line too long");
|
|
gflg++;
|
|
return(c);
|
|
}
|
|
c = readc();
|
|
if (ec != '\'' && e.iop->task != XGRAVE) {
|
|
if(c == '\\') {
|
|
c = readc();
|
|
if (c == '\n' && ec != '\"')
|
|
return(getc(ec));
|
|
c |= QUOTE;
|
|
}
|
|
}
|
|
return(c);
|
|
}
|
|
|
|
void
|
|
unget(c)
|
|
int c;
|
|
{
|
|
if (e.iop >= e.iobase)
|
|
e.iop->peekc = c;
|
|
}
|
|
|
|
int
|
|
eofc()
|
|
|
|
{
|
|
return e.iop < e.iobase || (e.iop->peekc == 0 && e.iop->prev == 0);
|
|
}
|
|
|
|
int
|
|
readc()
|
|
{
|
|
register c;
|
|
|
|
for (; e.iop >= e.iobase; e.iop--)
|
|
if ((c = e.iop->peekc) != '\0') {
|
|
e.iop->peekc = 0;
|
|
return(c);
|
|
}
|
|
else {
|
|
if (e.iop->prev != 0) {
|
|
if ((c = (*e.iop->iofn)(e.iop->argp, e.iop)) != '\0') {
|
|
if (c == -1) {
|
|
e.iop++;
|
|
continue;
|
|
}
|
|
if (e.iop == iostack)
|
|
ioecho(c);
|
|
return(e.iop->prev = c);
|
|
}
|
|
else if (e.iop->task == XIO && e.iop->prev != '\n') {
|
|
e.iop->prev = 0;
|
|
if (e.iop == iostack)
|
|
ioecho('\n');
|
|
return '\n';
|
|
}
|
|
}
|
|
if (e.iop->task == XIO) {
|
|
if (multiline)
|
|
return e.iop->prev = 0;
|
|
if (talking && e.iop == iostack+1)
|
|
prs(prompt->value);
|
|
}
|
|
}
|
|
if (e.iop >= iostack)
|
|
return(0);
|
|
leave();
|
|
/* NOTREACHED */
|
|
}
|
|
|
|
void
|
|
ioecho(c)
|
|
char c;
|
|
{
|
|
if (flag['v'])
|
|
write(2, &c, sizeof c);
|
|
}
|
|
|
|
void
|
|
pushio(argp, fn)
|
|
struct ioarg *argp;
|
|
int (*fn)();
|
|
{
|
|
if (++e.iop >= &iostack[NPUSH]) {
|
|
e.iop--;
|
|
err("Shell input nested too deeply");
|
|
gflg++;
|
|
return;
|
|
}
|
|
e.iop->iofn = fn;
|
|
|
|
if (argp->afid != AFID_NOBUF)
|
|
e.iop->argp = argp;
|
|
else {
|
|
e.iop->argp = ioargstack + (e.iop - iostack);
|
|
*e.iop->argp = *argp;
|
|
e.iop->argp->afbuf = e.iop == &iostack[0] ? &mainbuf : &sharedbuf;
|
|
if (isatty(e.iop->argp->afile) == 0 &&
|
|
(e.iop == &iostack[0] ||
|
|
lseek(e.iop->argp->afile, 0L, 1) != -1)) {
|
|
if (++bufid == AFID_NOBUF)
|
|
bufid = AFID_ID;
|
|
e.iop->argp->afid = bufid;
|
|
}
|
|
}
|
|
|
|
e.iop->prev = ~'\n';
|
|
e.iop->peekc = 0;
|
|
e.iop->xchar = 0;
|
|
e.iop->nlcount = 0;
|
|
if (fn == filechar || fn == linechar)
|
|
e.iop->task = XIO;
|
|
else if (fn == gravechar || fn == qgravechar)
|
|
e.iop->task = XGRAVE;
|
|
else
|
|
e.iop->task = XOTHER;
|
|
}
|
|
|
|
struct io *
|
|
setbase(ip)
|
|
struct io *ip;
|
|
{
|
|
register struct io *xp;
|
|
|
|
xp = e.iobase;
|
|
e.iobase = ip;
|
|
return(xp);
|
|
}
|
|
|
|
/*
|
|
* Input generating functions
|
|
*/
|
|
|
|
/*
|
|
* Produce the characters of a string, then a newline, then EOF.
|
|
*/
|
|
int
|
|
nlchar(ap)
|
|
register struct ioarg *ap;
|
|
{
|
|
register int c;
|
|
|
|
if (ap->aword == NULL)
|
|
return(0);
|
|
if ((c = *ap->aword++) == 0) {
|
|
ap->aword = NULL;
|
|
return('\n');
|
|
}
|
|
return(c);
|
|
}
|
|
|
|
/*
|
|
* Given a list of words, produce the characters
|
|
* in them, with a space after each word.
|
|
*/
|
|
int
|
|
wdchar(ap)
|
|
register struct ioarg *ap;
|
|
{
|
|
register char c;
|
|
register char **wl;
|
|
|
|
if ((wl = ap->awordlist) == NULL)
|
|
return(0);
|
|
if (*wl != NULL) {
|
|
if ((c = *(*wl)++) != 0)
|
|
return(c & 0177);
|
|
ap->awordlist++;
|
|
return(' ');
|
|
}
|
|
ap->awordlist = NULL;
|
|
return('\n');
|
|
}
|
|
|
|
/*
|
|
* Return the characters of a list of words,
|
|
* producing a space between them.
|
|
*/
|
|
int
|
|
dolchar(ap)
|
|
register struct ioarg *ap;
|
|
{
|
|
register char *wp;
|
|
|
|
if ((wp = *ap->awordlist++) != NULL) {
|
|
PUSHIO(aword, wp, *ap->awordlist == NULL? strchar: xxchar);
|
|
return(-1);
|
|
}
|
|
return(0);
|
|
}
|
|
|
|
static int
|
|
xxchar(ap)
|
|
register struct ioarg *ap;
|
|
{
|
|
register int c;
|
|
|
|
if (ap->aword == NULL)
|
|
return(0);
|
|
if ((c = *ap->aword++) == '\0') {
|
|
ap->aword = NULL;
|
|
return(' ');
|
|
}
|
|
return(c);
|
|
}
|
|
|
|
/*
|
|
* Produce the characters from a single word (string).
|
|
*/
|
|
int
|
|
strchar(ap)
|
|
register struct ioarg *ap;
|
|
{
|
|
register int c;
|
|
|
|
if (ap->aword == NULL || (c = *ap->aword++) == 0)
|
|
return(0);
|
|
return(c);
|
|
}
|
|
|
|
/*
|
|
* Produce quoted characters from a single word (string).
|
|
*/
|
|
int
|
|
qstrchar(ap)
|
|
register struct ioarg *ap;
|
|
{
|
|
register int c;
|
|
|
|
if (ap->aword == NULL || (c = *ap->aword++) == 0)
|
|
return(0);
|
|
return(c|QUOTE);
|
|
}
|
|
|
|
/*
|
|
* Return the characters from a file.
|
|
*/
|
|
int
|
|
filechar(ap)
|
|
register struct ioarg *ap;
|
|
{
|
|
register int i;
|
|
char c;
|
|
struct iobuf *bp = ap->afbuf;
|
|
|
|
if (ap->afid != AFID_NOBUF) {
|
|
if ((i = ap->afid != bp->id) || bp->bufp == bp->ebufp) {
|
|
if (i)
|
|
lseek(ap->afile, ap->afpos, 0);
|
|
do {
|
|
i = read(ap->afile, bp->buf, sizeof(bp->buf));
|
|
} while (i < 0 && errno == EINTR);
|
|
if (i <= 0) {
|
|
closef(ap->afile);
|
|
return 0;
|
|
}
|
|
bp->id = ap->afid;
|
|
bp->ebufp = (bp->bufp = bp->buf) + i;
|
|
}
|
|
ap->afpos++;
|
|
return *bp->bufp++ & 0177;
|
|
}
|
|
|
|
do {
|
|
i = read(ap->afile, &c, sizeof(c));
|
|
} while (i < 0 && errno == EINTR);
|
|
return(i == sizeof(c)? c&0177: (closef(ap->afile), 0));
|
|
}
|
|
|
|
/*
|
|
* Return the characters from a here temp file.
|
|
*/
|
|
int
|
|
herechar(ap)
|
|
register struct ioarg *ap;
|
|
{
|
|
char c;
|
|
|
|
|
|
if (read(ap->afile, &c, sizeof(c)) != sizeof(c)) {
|
|
close(ap->afile);
|
|
c = 0;
|
|
}
|
|
return (c);
|
|
|
|
}
|
|
|
|
/*
|
|
* Return the characters produced by a process (`...`).
|
|
* Quote them if required, and remove any trailing newline characters.
|
|
*/
|
|
int
|
|
gravechar(ap, iop)
|
|
struct ioarg *ap;
|
|
struct io *iop;
|
|
{
|
|
register int c;
|
|
|
|
if ((c = qgravechar(ap, iop)&~QUOTE) == '\n')
|
|
c = ' ';
|
|
return(c);
|
|
}
|
|
|
|
int
|
|
qgravechar(ap, iop)
|
|
register struct ioarg *ap;
|
|
struct io *iop;
|
|
{
|
|
register int c;
|
|
|
|
if (iop->xchar) {
|
|
if (iop->nlcount) {
|
|
iop->nlcount--;
|
|
return('\n'|QUOTE);
|
|
}
|
|
c = iop->xchar;
|
|
iop->xchar = 0;
|
|
} else if ((c = filechar(ap)) == '\n') {
|
|
iop->nlcount = 1;
|
|
while ((c = filechar(ap)) == '\n')
|
|
iop->nlcount++;
|
|
iop->xchar = c;
|
|
if (c == 0)
|
|
return(c);
|
|
iop->nlcount--;
|
|
c = '\n';
|
|
}
|
|
return(c!=0? c|QUOTE: 0);
|
|
}
|
|
|
|
/*
|
|
* Return a single command (usually the first line) from a file.
|
|
*/
|
|
int
|
|
linechar(ap)
|
|
register struct ioarg *ap;
|
|
{
|
|
register int c;
|
|
|
|
if ((c = filechar(ap)) == '\n') {
|
|
if (!multiline) {
|
|
closef(ap->afile);
|
|
ap->afile = -1; /* illegal value */
|
|
}
|
|
}
|
|
return(c);
|
|
}
|
|
|
|
void
|
|
prs(s)
|
|
register char *s;
|
|
{
|
|
if (*s)
|
|
write(2, s, strlen(s));
|
|
}
|
|
|
|
void
|
|
putc(c)
|
|
char c;
|
|
{
|
|
write(2, &c, sizeof c);
|
|
}
|
|
|
|
void
|
|
prn(u)
|
|
unsigned u;
|
|
{
|
|
prs(itoa(u, 0));
|
|
}
|
|
|
|
void
|
|
closef(i)
|
|
register int i;
|
|
{
|
|
if (i > 2)
|
|
close(i);
|
|
}
|
|
|
|
void
|
|
closeall()
|
|
{
|
|
register u;
|
|
|
|
for (u=NUFILE; u<NOFILE;)
|
|
close(u++);
|
|
}
|
|
|
|
/*
|
|
* remap fd into Shell's fd space
|
|
*/
|
|
int
|
|
remap(fd)
|
|
register int fd;
|
|
{
|
|
register int i;
|
|
int map[NOFILE];
|
|
|
|
if (fd < e.iofd) {
|
|
for (i=0; i<NOFILE; i++)
|
|
map[i] = 0;
|
|
do {
|
|
map[fd] = 1;
|
|
fd = dup(fd);
|
|
} while (fd >= 0 && fd < e.iofd);
|
|
for (i=0; i<NOFILE; i++)
|
|
if (map[i])
|
|
close(i);
|
|
if (fd < 0)
|
|
err("too many files open in shell");
|
|
}
|
|
return(fd);
|
|
}
|
|
|
|
int
|
|
openpipe(pv)
|
|
register int *pv;
|
|
{
|
|
register int i;
|
|
|
|
if ((i = pipe(pv)) < 0)
|
|
err("can't create pipe - try again");
|
|
return(i);
|
|
}
|
|
|
|
void
|
|
closepipe(pv)
|
|
register int *pv;
|
|
{
|
|
if (pv != NULL) {
|
|
close(*pv++);
|
|
close(*pv);
|
|
}
|
|
}
|
|
|
|
/* -------- here.c -------- */
|
|
/* #include "sh.h" */
|
|
|
|
/*
|
|
* here documents
|
|
*/
|
|
|
|
struct here {
|
|
char *h_tag;
|
|
int h_dosub;
|
|
struct ioword *h_iop;
|
|
struct here *h_next;
|
|
};
|
|
|
|
static struct here *inhere; /* list of hear docs while parsing */
|
|
static struct here *acthere; /* list of active here documents */
|
|
|
|
void
|
|
inithere()
|
|
{
|
|
inhere=acthere=(struct here*)0;
|
|
}
|
|
|
|
void
|
|
markhere(s, iop)
|
|
register char *s;
|
|
struct ioword *iop;
|
|
{
|
|
register struct here *h, *lh;
|
|
|
|
h = (struct here *) space(sizeof(struct here));
|
|
if (h == 0)
|
|
return;
|
|
h->h_tag = evalstr(s, DOSUB);
|
|
if (h->h_tag == 0)
|
|
return;
|
|
h->h_iop = iop;
|
|
iop->io_name = 0;
|
|
h->h_next = NULL;
|
|
if (inhere == 0)
|
|
inhere = h;
|
|
else
|
|
for (lh = inhere; lh!=NULL; lh = lh->h_next)
|
|
if (lh->h_next == 0) {
|
|
lh->h_next = h;
|
|
break;
|
|
}
|
|
iop->io_flag |= IOHERE|IOXHERE;
|
|
for (s = h->h_tag; *s; s++)
|
|
if (*s & QUOTE) {
|
|
iop->io_flag &= ~ IOXHERE;
|
|
*s &= ~ QUOTE;
|
|
}
|
|
h->h_dosub = iop->io_flag & IOXHERE;
|
|
}
|
|
|
|
void
|
|
gethere()
|
|
{
|
|
register struct here *h, *hp;
|
|
|
|
/* Scan here files first leaving inhere list in place */
|
|
for (hp = h = inhere; h != NULL; hp = h, h = h->h_next)
|
|
readhere(&h->h_iop->io_name, h->h_tag, h->h_dosub? 0: '\'');
|
|
|
|
/* Make inhere list active - keep list intact for scraphere */
|
|
if (hp != NULL) {
|
|
hp->h_next = acthere;
|
|
acthere = inhere;
|
|
inhere = NULL;
|
|
}
|
|
}
|
|
|
|
static void
|
|
readhere(name, s, ec)
|
|
char **name;
|
|
register char *s;
|
|
int ec;
|
|
{
|
|
int tf;
|
|
char tname[30];
|
|
register c;
|
|
jmp_buf ev;
|
|
char line [LINELIM+1];
|
|
char *next;
|
|
|
|
tempname(tname);
|
|
*name = strsave(tname, areanum);
|
|
tf = creat(tname, 0600);
|
|
if (tf < 0)
|
|
return;
|
|
if (newenv(setjmp(errpt = ev)) != 0)
|
|
unlink(tname);
|
|
else {
|
|
pushio(e.iop->argp, e.iop->iofn);
|
|
e.iobase = e.iop;
|
|
for (;;) {
|
|
if (talking && e.iop <= iostack)
|
|
prs(cprompt->value);
|
|
next = line;
|
|
while ((c = readc()) != '\n' && c) {
|
|
if (next >= &line[LINELIM]) {
|
|
c = 0;
|
|
break;
|
|
}
|
|
*next++ = c;
|
|
}
|
|
*next = 0;
|
|
if (strcmp(s, line) == 0 || c == 0)
|
|
break;
|
|
*next++ = '\n';
|
|
write (tf, line, (int)(next-line));
|
|
}
|
|
if (c == 0) {
|
|
prs("here document `"); prs(s); err("' unclosed");
|
|
}
|
|
quitenv();
|
|
}
|
|
close(tf);
|
|
}
|
|
|
|
/*
|
|
* open here temp file.
|
|
* if unquoted here, expand here temp file into second temp file.
|
|
*/
|
|
int
|
|
herein(hname, xdoll)
|
|
char *hname;
|
|
int xdoll;
|
|
{
|
|
register hf, tf;
|
|
|
|
if (hname == 0)
|
|
return(-1);
|
|
hf = open(hname, 0);
|
|
if (hf < 0)
|
|
return (-1);
|
|
if (xdoll) {
|
|
char c;
|
|
char tname[30];
|
|
jmp_buf ev;
|
|
|
|
tempname(tname);
|
|
if ((tf = creat(tname, 0600)) < 0)
|
|
return (-1);
|
|
if (newenv(setjmp(errpt = ev)) == 0) {
|
|
PUSHIO(afile, hf, herechar);
|
|
setbase(e.iop);
|
|
while ((c = subgetc(0, 0)) != 0) {
|
|
char c1 = c&~QUOTE;
|
|
|
|
if (c"E && !any(c1,"`$\\"))
|
|
write(tf,"\\",1);
|
|
write(tf, &c1, 1);
|
|
}
|
|
quitenv();
|
|
} else
|
|
unlink(tname);
|
|
close(tf);
|
|
tf = open(tname, 0);
|
|
unlink(tname);
|
|
return (tf);
|
|
} else
|
|
return (hf);
|
|
}
|
|
|
|
void
|
|
scraphere()
|
|
{
|
|
register struct here *h;
|
|
|
|
for (h = inhere; h != NULL; h = h->h_next) {
|
|
if (h->h_iop && h->h_iop->io_name)
|
|
unlink(h->h_iop->io_name);
|
|
}
|
|
inhere = NULL;
|
|
}
|
|
|
|
/* unlink here temp files before a freearea(area) */
|
|
void
|
|
freehere(area)
|
|
int area;
|
|
{
|
|
register struct here *h, *hl;
|
|
|
|
hl = NULL;
|
|
for (h = acthere; h != NULL; h = h->h_next)
|
|
if (getarea((char *) h) >= area) {
|
|
if (h->h_iop->io_name != NULL)
|
|
unlink(h->h_iop->io_name);
|
|
if (hl == NULL)
|
|
acthere = h->h_next;
|
|
else
|
|
hl->h_next = h->h_next;
|
|
} else
|
|
hl = h;
|
|
}
|
|
|
|
void
|
|
tempname(tname)
|
|
char *tname;
|
|
{
|
|
static int inc;
|
|
register char *cp, *lp;
|
|
|
|
for (cp = tname, lp = "/tmp/shtm"; (*cp = *lp++) != '\0'; cp++)
|
|
;
|
|
lp = putn(getpid()*1000 + inc++);
|
|
for (; (*cp = *lp++) != '\0'; cp++)
|
|
;
|
|
}
|