/*
* Copyright 1989 by Rayan S. Zachariassen, all rights reserved.
* This will be free software, but only when it is finished.
*/
/*
* Runtime interpreter for the shell pseudo-code.
*/
#include "hostenv.h"
#include "listutils.h"
#ifdef MAILER
#include "sift.h" /* Include this BEFORE "mailer.h" ! */
#endif /* MAILER */
#include "mailer.h"
#include <stdio.h>
#include <ctype.h>
#include <fcntl.h>
#include <sys/file.h>
#include "zsyslog.h"
/* #define static
#define register */
#define RUNIO(X) (/*fprintf(runiofp, "runio(%s,%d)\n", __FILE__, __LINE__), */runio(&X))
#include "interpret.h"
#include "io.h"
#include "shconfig.h"
#include "libc.h"
#include "libz.h"
#include "libsh.h"
extern struct sptree *spt_funclist;
extern int wait __((int *));
extern int v_record, v_changed;
int magic_number = 3; /* check id for precompiled script files */
long bin_magic = 1; /* Another check-id -- exec file st_ctime ? */
#if 0
#undef STATIC
#define STATIC /**/
#endif
STATIC int pipefd; /* read side fd of a pipe, or -1 */
STATIC struct osCmd *ib_command[20]; /* max # nested $() in stack */
STATIC int ibt = -1; /* top of ib_command stack, XXX reset somewh. */
STATIC const char *uBLANK = "";
/*
* Because the structure of the interpreter is to build up a command descriptor
* and then execute it, and the shell must remain relatively intact I/O-wise,
* we have to predetermine which I/O actions should be taken to set up the
* environment for a particular program or command. To do this, we have to
* know the effects of I/O-related system calls on the environment. The only
* possible effect is to create or destroy (or leave alone) one or more
* file descriptors. The algorithms used by the kernel to default file
* descriptors (e.g. in an open() call) are well specified, so we can simulate
* all that activity a-priori. This in turn is necessary in order to know
* how to undo these actions. The simulation is aided by an array we maintain
* of busy (i.e. in-use) file descriptors.
*/
STATIC short fds[MAXNFILE]; /* could be a bitmap */
#define FDBUSY(X) ((X) > (sizeof fds / sizeof fds[0]) ? abort(),0 : fds[X])
/*
* This routine is used to model the effects of I/O operations on the
* set of file descriptors.
*/
STATIC void freeio __((struct IOop *, int));
STATIC void
freeio(fioop, mark)
register struct IOop *fioop;
int mark;
{
struct IOop *nfioop;
int fd;
for (; fioop != NULL; fioop = nfioop) {
nfioop = fioop->next;
if (1) {
if (fioop->cmd == sIOdup)
fd = fioop->fd2;
else
fd = fioop->fd;
fds[fd] = (fioop->cmd != sIOclose);
if (isset('R'))
fprintf(stderr,
"fds[%d] = %d\n",
fd, (fioop->cmd != sIOclose));
}
if (fioop->opflags)
free((void *)fioop);
}
}
#define NO_DEQUOTE_AT_SIFTS
/* #define DEQUOTE_STICKY */
STATIC char *dequote __((const char *str, int len));
STATIC char *
dequote (str, len)
const char *str;
int len;
{
#ifndef NO_DEQUOTE_AT_SIFTS
const char *sp, *ep;
char *s;
#endif
char *s0;
#ifdef DEQUOTE_STICKY
memtypes stickytmp = stickymem;
stickymem = MEM_SHCMD;
/* fprintf(stderr,"dequote(\"%s\",%d) => ",str,len); */
s0 = tmalloc(len+1); /* All subsequent runs will be smaller,
AND they fit running in-place! */
#else
s0 = emalloc(len+1); /* All subsequent runs will be smaller,
AND they fit running in-place! */
#endif
#ifdef NO_DEQUOTE_AT_SIFTS
memcpy(s0, str, len);
s0[len] = 0;
#else
do {
sp = str;
ep = str + len -1;
if (len > 1 && *sp == *ep &&
(*sp == '"' || *sp == '\'')) {
++sp;
--ep;
len -= 2;
for (s = s0; len > 0; ++sp, --len, ++s) {
if (*sp == '\\' && sp[1] != 0) {
*s = *++sp;
--len;
} else
*s = *sp;
}
*s = 0;
} else {
if (s0 != sp) /* Don't copy, if 'in-place' */
memcpy(s0, sp, len);
s = s0;
if (len > 0)
s += len;
*s = 0;
}
ep = s-1;
str = s0;
len = strlen(str);
} while (ep > s0 && *s0 == *ep && (*s0 == '"' || *s0 == '\''));
#endif
/* fprintf(stderr,"\"%s\"\n",s0); */
#ifdef DEQUOTE_STICKY
stickymem = stickytmp;
#endif
return (s0);
}
extern void free_tregexp __((tregexp *prog));
STATIC void free_regexp __((regexp *prog));
STATIC void
free_regexp (prog)
regexp *prog;
{
if (prog != NULL) {
regfree(&prog->re);
free((void*)(prog->pattern));
free(prog);
}
}
STATIC const char * regsub __((regexp *, int));
STATIC const char *
regsub(prog, n)
regexp *prog;
int n;
{
if (prog == NULL || n < 0 || n > prog->re.re_nsub)
return (NULL);
return (prog->match[n]);
}
STATIC regexp *reg_comp __((const char *str, int slen));
STATIC regexp *
reg_comp (str, slen)
const char *str;
int slen;
{
const char *reg_stat;
regexp *prog;
char *s;
prog = (regexp *) malloc(sizeof(regexp));
if (prog == NULL) {
fprintf(stderr, "%s: regexp %s: No space\n",
progname, str);
return (NULL);
}
memset((void*)prog, 0, sizeof(regexp));
prog->pattern = s = emalloc(slen+1);
memcpy(s, str, slen+1);
s[slen] = 0; /* Just in case */
reg_stat = re_compile_pattern(prog->pattern, slen, &prog->re);
if (reg_stat != NULL) {
fprintf(stderr,"%s: regexp %s: %s\n",progname,str,reg_stat);
free((void*)(prog->pattern));
free(prog);
return (NULL);
}
return (prog);
}
STATIC int reg_exec __((regexp *, const char *));
STATIC int
reg_exec (prog, str)
regexp *prog;
const char *str;
{
int i;
int re_stat;
regmatch_t *pmatch;
if (prog == NULL) {
fprintf(stderr, "%s: regexp: NULL program\n", progname);
return (0);
}
#define DEBUG /* allow traceing here as happens with tregexp.c ! */
#ifdef DEBUG
if (D_compare) {
fprintf(stderr,
"%*sscomparing '%s' and ", 4*funclevel, " ", prog->pattern);
if (str != NULL)
fprintf(stderr, "'%s'\n", str);
else
fprintf(stderr, "(nil)\n");
}
#endif /* DEBUG */
#ifndef USE_ALLOCA
pmatch = (regmatch_t *)
emalloc((prog->re.re_nsub+1)*sizeof(regmatch_t));
#else
pmatch = (regmatch_t *)
alloca((prog->re.re_nsub+1)*sizeof(regmatch_t));
#endif
re_stat = regexec(&prog->re, str, prog->re.re_nsub+1, pmatch, 0);
if (re_stat == REG_NOMATCH) {
#ifndef USE_ALLOCA
free(pmatch);
#endif
return 0;
}
for (i=0; i<=prog->re.re_nsub; i++)
prog->match[i] = strnsave(str + pmatch[i].rm_so,
pmatch[i].rm_eo - pmatch[i].rm_so);
#ifdef DEBUG
if (D_matched) {
fprintf(stderr,
"%*ssmatched '%s' and ", 4*funclevel, " ", prog->pattern);
if (str != NULL)
fprintf(stderr, "'%s'\n", str);
else
fprintf(stderr, "(nil)\n");
}
#endif /* DEBUG */
#ifndef USE_ALLOCA
free(pmatch);
#endif
return 1;
}
/*
* Return the next available filedescriptor, simulating kernel lookup.
*/
STATIC int findfreefd __((void));
STATIC int
findfreefd()
{
register int fd;
for (fd = 0; fd <= (sizeof fds / sizeof fds[0]) ; ++fd)
if (fds[fd] == 0) {
#ifdef MAILER
/*
* This is supposed to compensate for random
* fopen's in the application, e.g. when caching
* file descriptors keeping a database open.
* This is a bit too expensive for my liking,
* and assumes these things are static as compared
* to shell code execution, so beware of subtle bugs.
*/
if (fcntl(fd, F_GETFL,0) >= 0)
continue;
#endif /* MAILER */
return fd;
}
fprintf(stderr, "%s: out of free filedescriptors (%d)!\n",
progname, fd);
abort(); /* Out of free file-descriptors! */
/* NOTREACHED */
return 0;
}
#ifdef MAILER
/*
* The mailer should call this routine before toplevel entry to the
* shell, and then be *VERY* careful about opening files in routines
* that may be called from within shell execution or between apply calls.
*/
int
setfreefd()
{
register int i;
#if 0
register int fd;
struct stat stbuf;
for (fd = 0; fd <= (sizeof fds / sizeof fds[0]) ; ++fd)
if (fstat(fd, &stbuf) == 0)
fds[i = fd] = 1;
else
fds[fd] = 0;
#else
i = -1;
#endif
return i;
}
#endif /* MAILER */
STATIC const char * ename __((OutputTokens));
STATIC const char *
ename(cmd)
OutputTokens cmd;
{
const char *s = NULL;
switch (cmd) {
case sBufferSet: s = "sBufferSet"; break;
case sBufferAppend: s = "sBufferAppend"; break;
case sBufferExpand: s = "sBufferExpand"; break;
case sBufferQuote: s = "sBufferQuote"; break;
case sBufferSetFromArgV: s = "sBufferSetFromArgV"; break;
case sArgVpush: s = "sArgVpush"; break;
case sArgList: s = "sArgList"; break;
case sVariablePush: s = "sVariablePush"; break;
case sVariablePop: s = "sVariablePop"; break;
case sVariableCdr: s = "sVariableCdr"; break;
case sVariableBuffer: s = "sVariableBuffer"; break;
case sVariableAppend: s = "sVariableAppend"; break;
case sVariableLoopAttach: s = "sVariableLoopAttach"; break;
case sCommandPush: s = "sCommandPush"; break;
case sCommandPop: s = "sCommandPop"; break;
case sCommandCarryBuffer: s = "sCommandCarryBuffer"; break;
case sIOopen: s = "sIOopen"; break;
case sIOopenString: s = "sIOopenString"; break;
case sIOopenPortal: s = "sIOopenPortal"; break;
case sIOopenPipe: s = "sIOopenPipe"; break;
case sIOintoBuffer: s = "sIOintoBuffer"; break;
case sIOclose: s = "sIOclose"; break;
case sIOdup: s = "sIOdup"; break;
case sIOsetIn: s = "sIOsetIn"; break;
case sIOsetInOut: s = "sIOsetInOut"; break;
case sIOsetOut: s = "sIOsetOut"; break;
case sIOsetAppend: s = "sIOsetAppend"; break;
case sIOsetDesc: s = "sIOsetDesc"; break;
case sIObufIn: s = "sIObufIn"; break;
case sIObufOut: s = "sIObufOut"; break;
case sIObufFree: s = "sIObufFree"; break;
case sIObufString: s = "sIObufString"; break;
case sAssign: s = "sAssign"; break;
case sAssignTemporary: s = "sAssignTemporary"; break;
case sFunction: s = "sFunction"; break;
case sParameter: s = "sParameter"; break;
case sJump: s = "sJump"; break;
case sBranchOrigin: s = "sBranchOrigin"; break;
case sJumpFork: s = "sJumpFork"; break;
case sJumpIfFailure: s = "sJumpIfFailure"; break;
case sJumpIfSuccess: s = "sJumpIfSuccess"; break;
case sJumpIfNilVariable: s = "sJumpIfNilVariable"; break;
case sJumpIfMatch: s = "sJumpIfMatch"; break;
case sJumpIfFindVarNil: s = "sJumpIfFindVarNil"; break;
case sJumpIfOrValueNil: s = "sJumpIfOrValueNil"; break;
case sJumpLoopBreak: s = "sJumpLoopBreak"; break;
case sJumpLoopContinue: s = "sJumpLoopContinue"; break;
case sLoopEnter: s = "sLoopEnter"; break;
case sLoopExit: s = "sLoopExit"; break;
case sLocalVariable: s = "sLocalVariable"; break;
case sScopePush: s = "sScopePush"; break;
case sScopePop: s = "sScopePop"; break;
case sDollarExpand: s = "sDollarExpand"; break;
case sNoOp: s = "sNoOp"; break;
case sPrintAndExit: s = "sPrintAndExit"; break;
case sBackground: s = "sBackground"; break;
case sSiftPush: s = "sSiftPush"; break;
case sSiftBody: s = "sSiftBody"; break;
case sSiftCompileRegexp: s = "sSiftCompileRegexp"; break;
case sSiftReevaluate: s = "sSiftReevaluate"; break;
case sSiftPop: s = "sSiftPop"; break;
case sSiftBufferAppend: s = "sSiftBufferAppend"; break;
case sJumpIfRegmatch: s = "sJumpIfRegmatch"; break;
case sTSiftPush: s = "sTSiftPush"; break;
case sTSiftBody: s = "sTSiftBody"; break;
case sTSiftCompileRegexp: s = "sTSiftCompileRegexp"; break;
case sTSiftReevaluate: s = "sTSiftReevaluate"; break;
case sTSiftPop: s = "sTSiftPop"; break;
/* case sTSiftBufferAppend: s = "sTSiftBufferAppend"; break; */
case sTJumpIfRegmatch: s = "sTJumpIfRegmatch"; break;
default: break;
}
return s;
}
/*
* Auxiliary routine (referenced by the INSERTIO macro) which links an
* I/O action into a linked list of same (in reverse order of execution).
* Since two of the parameters to the insertio() function are always the
* same, and the list of arguments so long, we also define a macro invocation
* for this function.
*/
#define INSERTIO(X,C,Y,FD1,FD2,PERM) insertio((X),(C),name,(Y),ioflags,(FD1),(FD2),(PERM))
STATIC struct IOop * insertio __((struct IOop **, struct osCmd *,
const char *, OutputTokens,
int, int, int, int));
STATIC struct IOop *
insertio(ioopp, command, name, cmd, ioflags, fd, fd2, opflags)
struct IOop **ioopp;
struct osCmd *command;
const char *name;
OutputTokens cmd;
int ioflags, fd, fd2, opflags;
{
register struct IOop *iotmp;
if (isset('R'))
fprintf(stderr,
"insertio(%p): %s %d %d\n",ioopp, ename(cmd), fd, fd2);
if (opflags)
iotmp = (struct IOop *)emalloc(sizeof (struct IOop));
else
iotmp = (struct IOop *)tmalloc(sizeof (struct IOop));
iotmp->command = command;
#if 1
iotmp->name = name;
#else
if (name == NULL)
iotmp->name = NULL;
else
iotmp->name = strsave(name);
#endif
iotmp->cmd = cmd;
iotmp->ioflags = ioflags;
iotmp->opflags = opflags;
iotmp->fd = fd;
iotmp->fd2 = fd2;
if (ioopp != NULL) {
iotmp->next = *ioopp;
*ioopp = iotmp;
} else
iotmp->next = NULL;
return iotmp;
}
/*
* When actions are executed in a particular order A,B,C, they sometimes
* need to be undone in the reverse order: undo C, undo B, undo A. To
* aid that, the APPENDIO() macro will do the obvious thing, in contrast
* with INSERTIO(). Due to the io op list reversal in runio, the APPENDIO's
* have to be done in reverse natural order. See below.
*/
#define APPENDIO(X,C,Y,FD1,FD2) appendio((X),(C),name,(Y),ioflags,(FD1),(FD2))
STATIC void appendio __((struct IOop **, struct osCmd *, const char *,
OutputTokens, int, int, int));
STATIC void
appendio(ioopp, command, name, cmd, ioflags, fd, fd2)
struct IOop **ioopp;
struct osCmd *command;
const char *name;
OutputTokens cmd;
int ioflags, fd, fd2;
{
register struct IOop *iotmp;
if (isset('R'))
fprintf(stderr,
"appendio(%x): %s %d %d\n", ioopp, ename(cmd), fd, fd2);
iotmp = *ioopp;
if (iotmp != NULL) {
for (; iotmp->next != NULL; iotmp = iotmp->next)
continue;
iotmp->next = insertio((struct IOop **)NULL, command, name,
cmd, ioflags, fd, fd2, 0);
} else
*ioopp = insertio((struct IOop **)NULL, command, name,
cmd, ioflags, fd, fd2, 0);
}
/*
* This routine is the basic component for building up I/O descriptor
* manipulation action lists (what a mouthful) that are later carried
* out by RUNIO(). It models the effects of I/O system calls on the
* set of available filedescriptors.
*/
STATIC void ioop __((OutputTokens, struct osCmd *, const char *,
int, int, const char *));
STATIC void
ioop(cmd, command, name, ioflags, defaultfd, arg1)
OutputTokens cmd;
struct osCmd *command;
const char *name, *arg1;
int ioflags, defaultfd;
{
int tofd, savefd, intobufflag;
if (isset('R'))
fprintf(stderr, "ioop(%x): %s\n", command, ename(cmd));
intobufflag = 0;
tofd = defaultfd;
if (cmd == sIOdup) {
defaultfd = atoi(arg1);
if (!FDBUSY(defaultfd)) {
fprintf(stderr, "%s: no fd %d!\n",
progname, defaultfd);
return;
}
}
if (FDBUSY(tofd)) {
/* save current to-fd somewhere else: save-fd */
savefd = findfreefd();
INSERTIO(&command->doio, command, sIOdup, tofd, savefd, 0);
fds[savefd] = 1;
} else
savefd = 0; /* shut up the compiler */
if (cmd == sIOopenPipe && !(ioflags & O_CREAT)) {
if ((defaultfd = pipefd) < 0) {
fprintf(stderr, "%s: no pipe!\n", progname);
abort(); /* No pipe on IOopenPipe when it should exist! */
}
} else {
if (cmd == sIOintoBuffer) {
cmd = sIOopenPipe;
command->iocmd = ioIntoBuffer;
intobufflag = 1;
} else if (cmd == sIOopenPipe)
command->iocmd = ioOpenPipe;
INSERTIO(&command->doio, command, cmd, defaultfd, tofd, 0);
defaultfd = findfreefd();
}
/* obey kernel semantics for fd return from open */
if ((cmd == sIOopen || cmd == sIOopenPipe
|| cmd == sIOopenPortal || cmd == sIOopenString)
&& defaultfd != tofd) {
/* this is always done for pipes, at least */
/* set fd2 in prev. open for later checking */
if (cmd == sIOopenPipe && (ioflags & O_CREAT)) {
/* pipefd is read end of the pipe */
pipefd = defaultfd;
fds[pipefd] = 1;
/* defaultfd is write end of pipe */
defaultfd = findfreefd();
command->doio->fd = defaultfd;
command->doio->fd2 = pipefd;
INSERTIO(&command->execio, command,sIOclose,pipefd,0,0);
} else if (cmd == sIOopenPipe) {
fds[pipefd] = 0;
pipefd = -1;
} else
command->doio->fd = defaultfd;
/* copy free fd to tofd */
INSERTIO(&command->doio, command, sIOdup, defaultfd, tofd, 0);
/* close free fd */
INSERTIO(&command->doio, command, sIOclose, defaultfd, 0, 0);
}
if (FDBUSY(tofd)) {
/* arrange to close save-fd inside fork/exec */
INSERTIO(&command->execio, command, sIOclose, savefd, 0, 0);
/*
* We need to restore original to-fd then close save-fd,
* but due to mechanics of these things we have to append
* them in reverse order here... don't get confused.
*/
/* in parent, need to close save-fd */
APPENDIO(&command->undoio, command, sIOclose, savefd, 0);
/* but first need to restore original to-fd */
if (tofd <= 2
&& (siofds[tofd] == NULL
|| siofds[tofd]->_sb_refcnt == 0)) {
APPENDIO(&command->undoio, command, sIObufFree, tofd,0);
}
APPENDIO(&command->undoio, command, sIOdup, savefd,tofd);
if (tofd <= 2
&& (siofds[tofd] == NULL
|| siofds[tofd]->_sb_refcnt == 0)) {
APPENDIO(&command->undoio, command, sIObufFree, tofd,0);
}
}
fds[tofd] = (cmd != sIOclose);
/* remember this command said something about tofd */
if (tofd < ((sizeof command->fdmask) * 8))
command->fdmask |= (1<<tofd);
if (intobufflag) {
cmd = sIOintoBuffer;
intobufflag = 0;
/*
* After forking the command we want to
* sit and wait on the output down in RUNIO.
*/
INSERTIO(&command->undoio, command, cmd, pipefd, 0, 0);
/* and then we have to close it of course */
INSERTIO(&command->undoio, command, sIOclose, pipefd, 0, 0);
/*
* we can't release pipefd (and fds[pipefd])
* until after all the other doio's have been
* taken care of... but we don't know when
* that will be except for at next CommandPop.
*/
}
if (isset('R'))
fprintf(stderr, "end(%x)\n", command);
}
/*
* The interpreter interface to the execute() routine.
*/
STATIC void runcommand __((struct osCmd *, struct osCmd *, int *, const char *));
STATIC void
runcommand(c, pc, retcodep, cmdname)
struct osCmd *c, *pc;
int *retcodep;
const char *cmdname;
{
int ioflags;
GCVARS1;
GCPRO1(c->argv);
if (c->argv) { /* from tconc into list */
if (!LIST(c->argv)) *((int*)0) = 0; /* ZAP! */
c->argv = copycell(car(c->argv));
cdr(c->argv) = NULL;
}
c->buffer = NULL;
c->bufferp = &(c->buffer);
/* in a backquote and output hasn't been explicitly redirected */
if (ibt >= 0 && c->argv != NULL && !(c->fdmask & (1<<1))) {
/*
* If the current command isn't a builtin and stdout not
* yet assigned, set up stdout as a pipe and read it
* back at backquote commandpop. This will work even if
* we're dealing with a sequence, e.g. `a | b ; c | d`
* and b is the current (non-builtin) command, because multiple
* calls to readstring() just append to the buffer.
* If any of b or d are builtins, the output will be
* that of the later builtin.
*/
/* XX: is this necessary*/
ib_command[ibt]->doio = ib_command[ibt]->execio = NULL;
if (c->shcmdp != NULL || c->sfdp != NULL) {
/* builtin function */
const char *name = "<builtin cmd>";
ioflags = 0;
INSERTIO(&c->doio, c, sIObufOut, 1, 1, 0);
INSERTIO(&ib_command[ibt]->undoio,
ib_command[ibt], sIObufIn, 0, 1, 1);
INSERTIO(&ib_command[ibt]->undoio,
ib_command[ibt], sIObufString, 0, 0, 1);
INSERTIO(&ib_command[ibt]->undoio,
ib_command[ibt], sIObufFree, 0, 0, 1);
} else {
/* ok, ok, create the silly pipe! */
register struct IOop *iop;
if (c->doio) {
RUNIO(c->doio);
freeio(c->doio, 1);
c->doio = NULL;
}
ioop(sIOintoBuffer, ib_command[ibt], NULL,
O_CREAT|O_WRONLY|O_TRUNC, 1, NULL);
/* Now copy doio and execio to current command */
/* first doio */
#if 0
for (iop = ib_command[ibt]->doio; iop != NULL; iop = iop->next)
if (iop->next == NULL)
break;
if (iop != NULL) {
iop->next = c->doio;
#endif
c->doio = ib_command[ibt]->doio;
ib_command[ibt]->doio = NULL;
#if 0
}
#endif
/* then execio */
for (iop = ib_command[ibt]->execio; iop != NULL; iop = iop->next)
if (iop->next == NULL)
break;
if (iop != NULL) {
iop->next = c->execio;
c->execio = ib_command[ibt]->execio;
ib_command[ibt]->execio = NULL;
}
}
--ibt;
}
/* run the command and undo any temporary variables */
if (retcodep != NULL) {
const char *name = "<builtin cmd>";
if (c->shcmdp != NULL)
name = c->shcmdp->name;
*retcodep = execute(c, pc, *retcodep, name);
if (*retcodep != 0 && isset('e'))
trapexit(*retcodep);
}
/* else we are ignoring execution of this command */
/*
* We don't need to explicitly free the argv/envold/io
* lists since that is taken care of by the setlevel()
*/
if (c->doio)
freeio(c->doio, 1);
if (c->undoio)
freeio(c->undoio, 1);
if (c->execio)
freeio(c->execio, 0);
UNGCPRO1;
}
/*
* Variable assignment routine. This is used directly by the interpreter
* and indirectly through v_set() by most everything else. All new variables
* are created in the global (but non-exported) scope unless variables are
* automatically exported. The prior value of a variable is sometimes stashed
* away for later restoration. This is only done when a command descriptor
* is passed, since it is used to undo the effect of temporary variable
* assignments on the command line.
*/
void
assign(sl_lhs, sl_rhs, command)
conscell *sl_lhs, *sl_rhs;
struct osCmd *command;
{
conscell *s = NULL, *l = NULL;
GCVARS4;
GCPRO4(s, l, sl_lhs, sl_rhs);
/*
* Add (lhs oldvalue) to a list "envold" kept in the command.
* Variable values are really (potentially) lists.
*/
s = v_find(sl_lhs->cstring);
l = copycell(sl_rhs); /* Copy it just in case.. */
/* that was: s_copy_tree(), but we don't need THAT! */
if (l)
sl_rhs = l;
if (s == NULL) {
/* We don't know this variable, we create it */
/* create the variable in the global but non-exported scope */
for (l = car(envarlist); cddr(l) != NULL; l = cdr(l))
continue; /* Scan scopes, stop at next to last */
/* l points at the next-to-last sublist of envarlist */
if (isset('a'))
l = cdr(l); /* or the list of exports */
cdr(sl_rhs) = car(l); /* the scope-list follows this value */
s = copycell(sl_lhs); /* Variable name here */
cdr(s) = sl_rhs; /* .. value follows varname */
car(l) = s; /* .. and anchor varname into scope */
l = NIL; /* A NIL-cell for old value.. */
} else {
/* The variable exists, we replace the data content */
cdr(sl_rhs) = cddr(s); /* Glue the chain:
s -> new_value -> old_tail */
l = cdr(s); /* Old value cell */
cdr(l) = NULL; /* ... disconnected */
cdr(s) = sl_rhs; /* ... and place the new value in */
#ifdef MAILER
if (v_accessed)
v_written(s);
#endif /* MAILER */
if (isset('a')) /* if "auto-export" set */
v_export(sl_lhs->cstring); /* ick! */
}
/* stash old value */
if (command != NULL) {
cdr(l) = command->envold; command->envold = l;
s = copycell(sl_lhs);
cdr(s) = command->envold; command->envold = s;
}
UNGCPRO4;
/* fvcache.namesymbol = 0; */
v_sync(sl_lhs->cstring);
if (isset('I')) {
fprintf(runiofp, "Assign %s = ", sl_lhs->cstring);
s_grind(sl_rhs, runiofp);
putc('\n', runiofp);
}
#ifdef MAILER
if (D_assign) {
fprintf(stderr, "%*s%s=", 4*funclevel, " ", sl_lhs->cstring);
s_grind(sl_rhs, stderr);
fputc('\n', stderr);
}
#endif /* MAILER */
}
/*
* Discard the definition of the function with the given name. If this was
* the last definition for the stored code table, free the table.
*/
STATIC void undefun __((const char *));
STATIC void
undefun(fname)
const char *fname;
{
struct spblk *spl;
spl = sp_lookup(symbol(fname), spt_funclist);
if (spl == NULL)
return;
xundefun(spl);
}
/*
* A version of undefun() we can call from inside sp_scan()
*/
int
xundefun(spl)
struct spblk *spl;
{
struct sslfuncdef *sfdp, **psfdp;
#ifdef MAILER
int idx;
regexp **rep, **repstart;
tregexp **trep, **trepstart;
#endif /* MAILER */
sfdp = (struct sslfuncdef *)spl->data;
if (sfdp != NULL) {
psfdp = &(sfdp->tabledesc->functions);
for (sfdp = *psfdp; sfdp != NULL;
psfdp = &sfdp->next, sfdp = *psfdp) {
if (strcmp(sfdp->name,
((struct sslfuncdef *)spl->data)->name) == 0) {
*psfdp = sfdp->next;
break;
}
}
if (sfdp->tabledesc->functions == NULL &&
sfdp->tabledesc->oktofree) {
#ifdef MAILER
repstart = sfdp->tabledesc->rearray;
if (repstart != NULL) {
idx = sfdp->tabledesc->rearray_idx;
rep = repstart;
while (rep - repstart < idx && *rep != NULL)
free_regexp(*rep++);
free((char *)sfdp->tabledesc->rearray);
}
trepstart = sfdp->tabledesc->trearray;
if (trepstart != NULL) {
idx = sfdp->tabledesc->trearray_idx;
trep = trepstart;
while (trep - trepstart < idx && *trep != NULL)
free_tregexp(*trep++);
free((void *)sfdp->tabledesc->trearray);
}
#endif /* MAILER */
free((void *)sfdp->tabledesc->table);
free((void *)sfdp->tabledesc);
}
free((void *)sfdp);
/*
* This should really be sp_delete, but if it is we won't
* be able to call undefun() inside an sp_scan...
*/
spl->data = NULL;
}
return 0;
}
/*
* Define an interpreted function. Sets up mutual links between the function
* descriptor and the code table descriptor.
*/
STATIC void defun __((struct codedesc *, const char *, const char *, const char *));
STATIC void
defun(cdp, fname, position, eofunc)
struct codedesc *cdp;
const char *fname;
const char *position, *eofunc;
{
struct sslfuncdef *sfdp;
undefun(fname);
sfdp = (struct sslfuncdef *)emalloc(sizeof (struct sslfuncdef));
/*
* The function name string is known to be part of the function
* pseudo-code and will not disappear unless the data block containing
* the function definition goes bye-bye.
*/
sfdp->name = fname;
sfdp->pos = position;
sfdp->eot = eofunc;
sfdp->tabledesc = cdp; /* will be set properly at end of interpret() */
sfdp->next = cdp->functions;
cdp->functions = sfdp;
sp_install(symbol(fname), (void *)sfdp, 0, spt_funclist);
}
/*
* This routine determines possible internal definitions of a function.
*/
void
functype(fname, shcmdpp, sfdpp)
register const char *fname;
struct shCmd **shcmdpp;
struct sslfuncdef **sfdpp;
{
spkey_t symid;
struct spblk *spl;
symid = symbol_lookup(fname);
/* is it a defined function? */
spl = NULL;
if (symid)
spl = sp_lookup(symid, spt_funclist);
if (sfdpp) {
if (spl != NULL)
*sfdpp = (struct sslfuncdef *)spl->data;
else
*sfdpp = NULL;
}
/* behaviour in execute() requires we continue, not return */
/* is it a builtin command? */
spl = NULL;
if (symid)
spl = sp_lookup(symid, spt_builtins);
if (shcmdpp != NULL) {
if (spl != NULL)
*shcmdpp = (struct shCmd *)spl->data;
else
*shcmdpp = NULL;
}
/* it must be a unix program */
}
/*
* Coalesces all the small buffers in command->buffer into a single buffer.
*/
STATIC void coalesce __((struct osCmd *));
STATIC void
coalesce(command)
struct osCmd *command;
{
conscell *s;
if (command->buffer == NULL)
return;
for (s = command->buffer; s != NULL; s = cdr(s))
if (LIST(s))
return;
command->buffer->flags |= QUOTEDSTRING; /* so result will be too */
if (cdr(command->buffer) != NULL)
/* this is only done for LARGE intoBuffer outputs */
command->buffer = s_catstring(command->buffer);
command->bufferp = &cdr(command->buffer);
}
/*
* Chop off any leading and trailing whitespace.
*/
STATIC void flushwhite __((struct osCmd *));
STATIC void
flushwhite(command)
struct osCmd *command;
{
register conscell *s, *p;
register char *cp;
if (command->buffer == NULL)
return;
if (ISELEMENT(command->buffer))
return;
for (s = command->buffer; s != NULL; s = cdr(s))
if (LIST(s))
return;
#if 0
/* strip leading whitespace */
s = command->buffer;
do {
for (cp = command->buffer->string;
*cp != '\0' && WHITESPACE(*cp); ++cp)
continue;
} while ((*cp == '\0') && (command->buffer = cdr(command->buffer)));
if (command->buffer == NULL) {
command->bufferp = &command->buffer;
return;
}
if (s->string != cp) {
command->buffer->string = strsave(cp);
}
#endif
/* strip trailing whitespace */
p = NULL;
do {
for (s = command->buffer; cdr(s) != p; s = cdr(s))
continue;
for (cp = s->string + s->slen - 1;
cp >= s->string; --cp)
if (*cp != '\n' /* !WHITESPACE(*cp) */)
break;
p = s;
} while ((cp < s->string) && (s != command->buffer));
if (cp < s->string) {
command->buffer = NULL;
command->bufferp = &command->buffer;
} else {
*++cp = '\0';
s->slen = cp - s->string;
cdr(s) = NULL;
command->bufferp = &cdr(s);
}
}
STATIC void tsetsubexps __((struct si_retab **, tregexp *));
STATIC void
tsetsubexps(sepp, tre)
struct si_retab **sepp;
tregexp *tre;
{
register struct si_retab *sep, *psep;
register unsigned int i;
for (sep = *sepp, psep = NULL; sep != NULL;
psep = sep, sep = sep->next) {
if (sep->trep == tre)
break;
}
if (sep == NULL) {
sep = (struct si_retab *)tmalloc(sizeof (struct si_retab));
for (i = 0; i < (sizeof sep->startp)/(sizeof sep->startp[0]); ++i)
sep->startp[i] = sep->endp[i] = NULL;
memset((char *)sep, 0, sizeof (struct si_retab));
sep->trep = tre;
sep->next = *sepp;
} else if (psep != NULL) {
psep->next = sep->next;
sep->next = *sepp;
}
*sepp = sep;
tre->startp = sep->startp;
tre->endp = sep->endp;
}
STATIC void setsubexps __((struct si_retab **, regexp *));
STATIC void
setsubexps(sepp, prog)
struct si_retab **sepp;
regexp *prog;
{
register struct si_retab *sep, *psep;
for (sep = *sepp, psep = NULL; sep != NULL;
psep = sep, sep = sep->next)
if (sep->rep == prog)
break;
if (sep == NULL) {
sep = (struct si_retab *)tmalloc(sizeof (struct si_retab));
memset((char *)sep, 0, sizeof (struct si_retab));
if (prog != NULL)
sep->match = (const char **) tmalloc((prog->re.re_nsub+1) *
sizeof(char *));
else
sep->match = NULL;
sep->rep = prog;
sep->next = *sepp;
} else if (psep != NULL) {
psep->next = sep->next;
sep->next = *sepp;
}
*sepp = sep;
if (prog != NULL)
prog->match = sep->match;
}
#if 0
/*
* This is a cache structure for v_find() which has appreciable locality.
* Every time an assignment is made or a scope pushed/popped, this cache
* is invalidated.
*/
struct {
int namesymbol;
conscell *location;
} fvcache = { 0, NULL };
#endif
struct loopinfo {
int brk; /* relative pc address if we want to break */
int cont; /* relative pc address if we want to continue */
short cmdindex; /* index of active command at this place */
short varindex; /* index of active loop variable */
};
STATIC token822 *tscanstring __((const char *));
STATIC
token822 *
tscanstring(s)
const char *s;
{
const char *cp;
int len;
token822 *t;
/* fprintf(stderr,"tscanstring('%s') ", s); */
t = HDR_SCANNER(s);
if (t != NULL && t->t_next == NULL && t->t_type == String) {
/* we need to de-quote the quoted-string */
char *bp;
const char *buf;
len = TOKENLEN(t);
buf = bp = (char *)tmalloc(len+3);
#ifdef NO_DEQUOTE_AT_SIFTS
*bp++ = '"';
#endif
for (cp = t->t_pname; (cp - t->t_pname) < len ; ++cp) {
#if 0 /* NOTE: No \-dequotation processing ! */
if ((*cp == '\\') && ((cp - t->t_pname) < len-1))
*bp++ = *++cp;
else
#endif
*bp++ = *cp;
}
#ifdef NO_DEQUOTE_AT_SIFTS
*bp++ = '"';
#endif
*bp = '\0';
/* fprintf(stderr,"dequote -> '%s'", buf); */
t = HDR_SCANNER(buf);
}
/* fprintf(stderr,"\n"); */
return t;
}
/* This is a last-line defence on utter stupidity -- doing recursive
* evaluation on something without proper terminating condition does
* quickly lead to awfull crash with a system whose stack is demolished,
* and rather difficult to debug... Trust me, I know -- Matti Aarnio */
STATIC int max_interpreter_recursions = 40;
/*
* Interpret Shell pseudo-code generated from S/SL description.
*
* Memory allocation and freeing in this code is a bit funny. To avoid
* a complicated reference-count or other GC scheme, we maintain a stack
* in memory (type MEM_SHCMD). We unravel the stack as we pop commands.
* That takes care of scratch allocations, which is everything except
* variable names and values. We have to be careful about freeing that
* data (which is malloc()'ed) whenever we discard it.
*/
struct codedesc *
interpret(Vcode, Veocode, Ventry, caller, retcodep, cdp)
const void *Vcode, *Veocode, *Ventry;
struct osCmd *caller;
int *retcodep;
struct codedesc *cdp;
{
register const char *code = Vcode, *eocode = Veocode, *entry = Ventry;
register const char *pc;
register OutputTokens cmd;
register struct osCmd *command;
register int commandIndex, variableIndex, i;
const char *arg1 = NULL, *name, *cmdname;
const char *origlevel;
int *iname;
int argi1 = 0, dye, childpid, dollar, ioflags;
memtypes stickytmp, origstickymem;
int defaultfd, quote, quoted, nloop, ignore_level;
#define LOOPMAXDEPTH 30
struct loopinfo loop[LOOPMAXDEPTH]; /* max # nested loops */
conscell *variable = NULL, *l = NULL, *margin = NULL;
conscell *d = NULL, *tmp = NULL;
struct osCmd *prevcommand = NULL;
struct osCmd *pcommand = NULL;
#define COMMANDMAXDEPTH 30
struct osCmd commandStack[COMMANDMAXDEPTH];
#define VARMAXDEPTH 30
conscell *varmeter = NULL;
conscell *varmchain = NULL;
#ifdef MAILER
struct siftinfo sift[30];
int nsift = -1;
regexp *re = NULL;
tregexp *tre = NULL;
#endif /* MAILER */
GCVARS6;
zoptind = 0; /* for zgetopts() */
commandIndex = -1;
command = &commandStack[++commandIndex];
command->buffer = NULL;
command->bufferp = &command->buffer;
command->flag = 0;
command->rval = command->argv = command->envold = NULL;
variableIndex = -1;
variable = NULL;
ioflags = 0;
defaultfd = 0;
dye = 0;
dollar = 0;
quote = 0;
varmeter = NULL;
fds[0] = fds[1] = fds[2] = 1; /* XX: this isn't recursive, eh?? */
margin = car(envarlist);/* so we can be sure to pop scopes on exit */
#define MAGIC_LARGE_IGNORE_LEVEL 123435 /* >> any valid ignore level */
ignore_level = MAGIC_LARGE_IGNORE_LEVEL;
#define MAGIC_LARGE_ADDRESS 9827432 /* >> any valid address */
loop[0].brk = loop[0].cont = MAGIC_LARGE_ADDRESS;
loop[0].varindex = -1;
loop[0].cmdindex = 0; /* this is important to unravel on "return" */
nloop = 0;
origstickymem = stickymem;
origlevel = getlevel(MEM_SHCMD);
stickymem = MEM_SHCMD;
/* shut up the compiler */
stickytmp = MEM_SHCMD;
d = NULL;
argi1 = 0;
if (entry == NULL)
pipefd = -1;
if (cdp == NULL) {
cdp = (struct codedesc *)emalloc(sizeof (struct codedesc));
cdp->table = code;
cdp->eotable = eocode;
cdp->functions = NULL;
#ifdef MAILER
cdp->rearray = NULL;
cdp->rearray_size = 0;
cdp->rearray_idx = -1;
cdp->trearray = NULL;
cdp->trearray_size = 0;
cdp->trearray_idx = -1;
#endif /* MAILER */
cdp->oktofree = 0;
}
#ifdef MAILER
++funclevel;
if (funclevel > max_interpreter_recursions) {
fprintf(stderr,"zmailer: interpret: recursed more than %d levels deep on invocation! script termination condition error ?\n",max_interpreter_recursions);
zsyslog((LOG_EMERG,"zmailer: interpret: recursed more than %d levels deep on invocation! script termination condition error ?\n",max_interpreter_recursions));
abort(); /* excessively deep recursion - *.cf -script termination condition error ? */
}
#endif /* MAILER */
GCPRO6(varmchain, variable, l, margin, d, tmp);
/* Initialize syntax for regular expressions */
(void) re_set_syntax(RE_CONTEXT_INDEP_ANCHORS |
RE_CONTEXT_INDEP_OPS |
RE_CONTEXT_INVALID_OPS |
RE_NO_BK_PARENS |
RE_NO_BK_VBAR |
RE_DOT_NOT_NULL);
#ifdef DEBUGxx
fprintf(stderr,"%s:%d &command->buffer=%p\n",__FILE__,__LINE__,
&command->buffer);
#endif
GCPRO4STORE(command->,
command->argv, command->rval,
command->envold, command->buffer);
/* funcall tracing could be done here */
/* if (caller != NULL) grindef("ARGV = ", caller->argv); */
if (isset('R'))
fds[FILENO(stderr)] = 1;
for (pc = (entry == NULL ? code : entry) ; pc < eocode; ++pc) {
if (sprung) {
trapped();
if (interrupted)
break;
}
cmd = (OutputTokens)(*pc & 0xFF);
if (isset('I'))
fprintf(runiofp, "'%d\t%s\n", pc - code,
TOKEN_NAME(cmd));
switch (TOKEN_NARGS(cmd)) {
case 0:
arg1 = NULL;
argi1 = 0;
break;
case 1:
argi1 = 0;
arg1 = (const char*) ++pc;
while (*pc != '\0')
++pc;
break;
case -1:
arg1 = NULL;
argi1 = (*++pc) & 0xFF;
argi1 <<= 8;
argi1 |= (*++pc) & 0xFF;
argi1 <<= 8;
argi1 |= (*++pc) & 0xFF;
argi1 <<= 8;
argi1 |= (*++pc) & 0xFF;
break;
}
switch (cmd) {
case sBufferSetFromArgV:
dollar = 1;
quote = 1;
arg1 = "@";
/* this gives "$@" */
/* FALL THROUGH */
case sBufferSet:
/* The buffer points at a linked list of strings */
command->buffer = NULL;
command->bufferp = &command->buffer;
/* FALL THROUGH */
case sBufferAppend:
if (dollar) {
dollar = 0;
d = v_expand(arg1, caller, *retcodep);
if (d == NULL) {
if (isset('u')) {
fprintf(stderr,
"%s: parameter not set\n",
arg1);
ignore_level = commandIndex;
}
d = conststring(uBLANK,0);
} else {
d = s_copy_chain(d);
}
if (!quote && STRING(d) && *(d->string) == '\0')
break;
} else if (*arg1 != '\0' || quote) {
int slen = strlen(arg1);
#if 0
/* XXX: If we can be sure of input
(arg1) being on stabile storage..
Command-line interpreter (shell)
is not stabile storage, but script
interpreter is! */
d = conststring(arg1,slen);
#else
d = newstring(dupnstr(arg1,slen),slen);
#endif
} else
break; /* it is a null string! */
#ifdef DEBUGxx
fprintf(stderr,"%s:%d &command->buffer = %p\n",__FILE__,__LINE__,&command->buffer);
#endif
*command->bufferp = d;
for (tmp = d; cdr(tmp) != NULL; tmp = cdr(tmp))
continue;
command->bufferp = &cdr(tmp);
if (quote) {
while (d != NULL) {
if (STRING(d) && !ISDONTQUOTE(d))
d->flags |= QUOTEDSTRING;
d = cdr(d);
}
quote = 0;
}
if (isset('I'))
grindef("Buffer = ", command->buffer);
break;
case sBufferExpand:
if (command->buffer == NULL)
d = conststring(uBLANK,0);
else if (cdr(command->buffer))
d = s_catstring(command->buffer);
else
d = command->buffer;
quoted = ISQUOTED(d);
d = v_expand((const char*)d->string,caller,*retcodep);
if (d == NULL) {
if (isset('u')) {
fprintf(stderr,
"%s: parameter not set\n",
arg1);
ignore_level = commandIndex;
}
d = conststring(uBLANK,0);
} else {
d = s_copy_chain(d);
}
command->buffer = d;
while (d != NULL) {
if (quoted && STRING(d))
d->flags |= QUOTEDSTRING;
if (cdr(d) == NULL)
break;
}
if (d == NULL)
command->bufferp = &command->buffer;
else {
for (tmp = d; cdr(tmp) != NULL; tmp = cdr(tmp))
continue;
command->bufferp = &cdr(tmp);
}
if (isset('I'))
grindef("Expanded Buffer = ", command->buffer);
case sArgVpush:
if (command->buffer == NULL)
break;
d = expand(command->buffer,1);
if (command->argv == NULL && STRING(d)) {
/* what kind of command is this? */
functype(d->string,
&command->shcmdp, &command->sfdp);
if (command->sfdp != NULL)
command->shcmdp = NULL;
if (prevcommand != NULL) {
prevcommand->next = command;
prevcommand->reaperTop = reapableTop;
if (command->shcmdp == NULL
&& command->sfdp == NULL) {
/* create pipe for prev. cmd */
ioop(sIOopenPipe, prevcommand,
NULL, O_CREAT, 1, NULL);
/* --- */
RUNIO(prevcommand->doio);
freeio(prevcommand->doio, 1);
prevcommand->doio = NULL;
/* --- */
/* add inpipe to this command */
ioop(sIOopenPipe, command,
NULL, O_RDONLY, 0, NULL);
} else if (command->sfdp != NULL ||
(command->shcmdp != NULL &&
(command->shcmdp->sptr != NULL ||
prevcommand->shcmdp->sptr != NULL))){
/* create stringbuffer */
name = NULL;
INSERTIO(&prevcommand->doio,
prevcommand,
sIObufOut, 1, 1, 0);
/* --- */
RUNIO(prevcommand->doio);
freeio(prevcommand->doio, 1);
prevcommand->doio = NULL;
/* --- */
INSERTIO(&command->doio,
command,
sIObufIn, 0, 1, 0);
#if 1
if (nloop > 0 &&
loop[nloop].cmdindex ==
commandIndex-1)
INSERTIO(&commandStack[commandIndex-2].undoio,
&commandStack[commandIndex-2],
sIObufFree, 0, 0, 0);
else
#endif
INSERTIO(&command->undoio,
command,
sIObufFree,0,0,0);
}
/*
* ... else we're connecting two
* list-valued functions, which is ok.
*/
cmdname = NULL;
if (prevcommand->sfdp)
cmdname = prevcommand->sfdp->name;
if (prevcommand->shcmdp)
cmdname = prevcommand->shcmdp->name;
runcommand(prevcommand, caller,
ignore_level>commandIndex ?
retcodep : NULL, cmdname);
command->rval = prevcommand->rval;
if (command->prev == prevcommand) {
command->prev = NULL;
command->flag |= OSCMD_SKIPIT;
}
free((char *)prevcommand);
prevcommand = NULL;
/* X:shouldn't prevcommand be stacked?*/
}
}
GCPLABPRINTis(command->gcpro4);
if (command->argv == NULL) {
command->argv = ncons(d);
command->argv = ncons(command->argv);
} else {
cddar(command->argv) = d;
}
cdar(command->argv) = s_last(d);
if (command->iocmd == ioPipeLater) {
/* we saw an openPipe but it was too early...*/
command->iocmd = ioNil;
if (command->shcmdp != NULL
|| command->sfdp != NULL) {
/* set prevcommand in the CommandPop */
command->iocmd = ioPipeOutput;
} else {
command->reaperTop = reapableTop;
ioop(sIOopenPipe, command,
NULL, O_CREAT, 1, NULL);
}
}
if (isset('I'))
grindef("Argv = ", command->argv);
break;
case sArgList:
/* take the remaining arguments in caller->argv
and stick them in the local variable "argv" in
the current scope */
break;
case sVariableCdr:
if (variable != NULL)
car(variable) = cdar(variable);
if (varmeter) {
if (variable && car(variable)) {
car(varmeter) = caar(variable);
varmeter->flags = car(variable)->flags;
varmeter->slen = car(variable)->slen;
} else {
car(varmeter) = NULL;
varmeter->flags = 0;
}
}
if (isset('I'))
grindef("Variable = ", variable);
break;
case sVariablePush:
if (variableIndex >= 0) {
tmp = ncons(varmeter); cdr(tmp) = varmchain; varmchain = tmp;
tmp = ncons(variable); cdr(tmp) = varmchain; varmchain = tmp;
}
++variableIndex;
if (variableIndex >= VARMAXDEPTH) {
fprintf(stderr,"%s: interpret.c: varStack[] recursed once too many. Max depth: %d\n",
progname, VARMAXDEPTH);
abort(); /* varStack[] recursed too deep! */
}
if (command->buffer == NULL)
variable = NULL;
else {
variable = expand(command->buffer,2);
variable = ncons(variable);
}
varmeter = NULL;
if (isset('I'))
grindef("Variable = ", variable);
break;
case sVariablePop:
--variableIndex;
if (varmchain) {
variable = car(varmchain);
varmeter = cadr(varmchain);
varmchain = cddr(varmchain);
if (isset('I'))
grindef("Variable = ", variable);
}
else
variable = varmeter = NULL;
break;
case sVariableBuffer:
if (variable != NULL) {
command->buffer = ncons(car(variable));
} else
command->buffer = NIL;
if (isset('I'))
grindef("Buffer = ", command->buffer);
break;
case sVariableAppend:
if (command->buffer == NULL)
break;
d = expand(command->buffer,3);
if (variable == NULL)
variable = ncons(d);
else if (car(variable) == NULL)
car(variable) = d;
else
cdr(s_last(car(variable))) = d; /* XX */
if (isset('I'))
grindef("Variable = ", variable);
break;
case sVariableLoopAttach:
varmeter = cdaar(envarlist);
if (variable != NULL) {
car(varmeter) = caar(variable);
varmeter->flags = car(variable)->flags;
varmeter->slen = car(variable)->slen;
}
break;
case sCommandPush:
if (commandIndex > 0) {
if (command->iocmd == ioPipeLater) {
/* see above at end of ArgV */
command->iocmd = ioNil;
if (command->shcmdp != NULL
|| command->sfdp != NULL)
command->iocmd = ioPipeOutput;
else {
command->reaperTop =
reapableTop;
ioop(sIOopenPipe, command,
NULL, O_CREAT, 1, NULL);
}
}
if (command->doio != NULL) {
RUNIO(command->doio);
freeio(command->doio, 1);
command->doio = NULL;
if (prevcommand != NULL) /* gross */
prevcommand->doio = NULL;
}
}
pcommand = command;
command = &commandStack[++commandIndex];
if (commandIndex >= COMMANDMAXDEPTH) {
fprintf(stderr,"%s: interpret.c: commandStack[] recursed once too many. Max depth: %d\n",
progname, COMMANDMAXDEPTH);
abort(); /* commandStack[] recursed too deep! */
}
memset(command, 0, sizeof(*command));
command->bufferp = &command->buffer;
/* command->argv = command->envold =
command->rval = command->buffer = NULL;
command->doio = command->undoio = 0;
command->execio = command->fdmask = 0;
command->pgrp = 0;
command->flag = 0;
command->shcmdp = NULL;
command->sfdp = NULL;
command->next = NULL;
*/
command->iocmd = ioNil;
#ifdef DEBUGxx
fprintf(stderr,"%s:%d &command->buffer=%p\n",__FILE__,__LINE__,
&command->buffer);
#endif
GCPRO4STORE(command->,
command->argv, command->rval,
command->envold, command->buffer);
if (prevcommand != NULL) { /* in pipe */
command->memlevel = prevcommand->memlevel;
command->pgrpp = prevcommand->pgrpp;
} else {
command->memlevel = getlevel(MEM_SHCMD);
command->pgrpp = NULL;
}
command->prev = prevcommand;
if (commandIndex > 1) {
/*
* This value is inherited, NULL or not. It
* is reset by a null return from list-valued
* function, or by any string-valued function.
*/
command->rval = pcommand->rval;
command->pgrpp = pcommand->pgrpp;
command->fdmask = pcommand->fdmask;
/* mark child commands of `...` */
if (pcommand->iocmd == ioIntoBuffer) {
pcommand->reaperTop = reapableTop;
command->reaperTop = reapableTop;
} else if (pcommand->reaperTop > -1)
command->reaperTop =
pcommand->reaperTop;
else
command->reaperTop = -1;
} else {
command->reaperTop = -1;
if (command->prev != NULL)
command->rval = command->prev->rval;
else
command->rval = NULL;
}
break;
case sCommandPop:
if (pipefd >= 0) /* IOintoBuffer relies on this */
fds[pipefd] = 0;
if (command->iocmd == ioPipeOutput) {
/*
* This command pipes its output into
* another one.
*/
command->iocmd = ioNil;
prevcommand =
(struct osCmd *)emalloc(sizeof(struct osCmd));
*prevcommand = *command;
command->flag |= OSCMD_SKIPIT;
break;
}
argi1 = *retcodep;
if (command->iocmd != ioCarryBuffer) {
cmdname = NULL;
if (command->sfdp)
cmdname = command->sfdp->name;
if (command->shcmdp)
cmdname = command->shcmdp->name;
runcommand(command, caller,
ignore_level > commandIndex ?
retcodep : NULL, cmdname);
tmp = *(command->bufferp);
}
if (ignore_level == commandIndex)
ignore_level = MAGIC_LARGE_IGNORE_LEVEL;
if (command->shcmdp != NULL
&& (command->shcmdp->flag == SH_INTERNAL ||
(void*)command->shcmdp->rptr != NULL)) {
/* this was break, continue, return or exit */
/* any optional argument is now in *retcodep */
if (*command->shcmdp->name == 'b' || /*break*/
*command->shcmdp->name == 'c') { /*cont'*/
if (*retcodep > 0) {
*retcodep -= 1;
if (*retcodep <= nloop)
nloop -= *retcodep;
} else
*retcodep = 0;
if (nloop < 0)
goto nobreak;
if (*command->shcmdp->name == 'b')
pc = code + loop[nloop].brk;
else
pc = code + loop[nloop].cont;
--pc;
} else if (*command->shcmdp->name == 'e') {
if (cdar(command->argv))
trapexit(*retcodep);
else
trapexit(argi1);
}
#if 0
else {
std_printf("ix = %d\nrval = %x\n",
commandIndex, command->rval);
}
#endif
/* if not in a loop this is done for returns */
while (commandIndex > loop[nloop].cmdindex) {
pcommand = &commandStack[commandIndex];
if (pcommand->undoio) {
RUNIO(pcommand->undoio);
freeio(pcommand->undoio, 1);
}
/* this is done after getout, below */
/*setlevel(MEM_SHCMD, pcommand->memlevel);*/
#ifdef DEBUGxx
fprintf(stderr,"%s:%d &command->buffer=%p\n",__FILE__,__LINE__,
&command->buffer);
#endif
UNGCPROSTORE4( pcommand-> );
--commandIndex;
}
while (variableIndex > loop[nloop].varindex) {
if (varmeter) {
car(varmeter) = NULL;
varmeter->flags = 0;
}
if (--variableIndex < 0)
break;
if (varmchain) {
variable = car(varmchain);
varmeter = cadr(varmchain);
varmchain = cddr(varmchain);
} else
variable = varmeter = NULL;
}
if (isset('I'))
grindef("Variable = ", variable);
if (*command->shcmdp->name == 'b'
&& variableIndex >= 0
&& varmeter != NULL) {
car(varmeter) = NULL;
varmeter->flags = 0;
variableIndex--;
if (varmchain)
varmchain = cddr(varmchain);
}
if (*command->shcmdp->name == 'r') { /* ``r''eturn */
#if 0
std_printf("idx = %d\n", commandIndex);
if (commandIndex >= 0)
std_printf("rval %x\n",
command->rval);
#endif
goto getout;
} else {
command = &commandStack[commandIndex];
break;
}
}
nobreak:
if (command->prev != NULL
|| (command->flag & OSCMD_SKIPIT)) {
UNGCPROSTORE4( commandStack[commandIndex]. );
--commandIndex;
}
UNGCPROSTORE4( commandStack[commandIndex]. );
--commandIndex;
#ifdef DEBUGxx
fprintf(stderr,"%s:%d commandIndex=%d\n",__FILE__,__LINE__,commandIndex);
#endif
/*
* If we are returning data, we shouldn't free dtpr
* or do a setlevel(), since the value may be used in
* parent command. Eventually a setlevel() will be
* done anyway to reclaim space... we hope.
XXX: HERE! Must copy the output to PREVIOUS memory level, then discard
the recursed one! <mea@utu.fi> ( command->rval )
*/
if (command->memlevel && command->rval == NULL
&& command->buffer == NULL
&& command->iocmd != ioIntoBuffer) {
if (command->pgrp > 0)
jc_report(command->pgrp);
}
if (commandIndex > 0) {
pcommand = &commandStack[commandIndex];
while (command->argv != NULL &&
pcommand->flag & OSCMD_SKIPIT) {
UNGCPROSTORE4( commandStack[commandIndex]. );
pcommand = &commandStack[--commandIndex];
}
#ifdef DEBUGxx
fprintf(stderr,"%s:%d &pcommand->buffer=%p\n",__FILE__,__LINE__,
&pcommand->buffer);
#endif
pcommand->rval = command->rval;
if (command->buffer != NULL) {
GCVARS2;
GCPRO2(pcommand->buffer, command->buffer);
if (command->flag & OSCMD_QUOTEOUTPUT)
coalesce(command);
else
flushwhite(command);
*(pcommand->bufferp) = command->buffer;
pcommand->bufferp = command->bufferp;
UNGCPRO2;
}
command = pcommand;
if (isset('I'))
grindef("Command = ", command->argv);
}
break;
case sCommandCarryBuffer:
command->iocmd = ioCarryBuffer;
break;
case sIOopen:
case sIOopenPortal:
case sIOopenString:
if (command->buffer == NULL)
name = "";
else if (cdr(command->buffer))
name = (const char *) (s_catstring(command->buffer))->string;
else if (command->buffer)
/* always true, need else below */
name = (const char *) command->buffer->string;
else
/* FALL THROUGH */
case sIOopenPipe:
if (cmd == sIOopenPipe) {
if (ioflags & O_CREAT) {
if (command->argv == NULL) {
/* defer dealing with this */
command->iocmd = ioPipeLater;
break;
} else if (command->shcmdp != NULL
|| command->sfdp != NULL) {
command->iocmd = ioPipeOutput;
break;
}
} else if (prevcommand != NULL)
break;
name = NULL;
} else /* continuation of above fallthrough */
/* FALL THROUGH */
case sIOdup:
case sIOclose:
name = NULL;
if (cmd == sIOclose)
ioflags = 0;
ioop(cmd, command, name, ioflags, defaultfd, arg1);
break;
case sIOintoBuffer:
command->fdmask = 0;
if (quote) {
command->flag |= OSCMD_QUOTEOUTPUT;
quote = 0;
}
ib_command[++ibt] = command;
if (ibt > 20) {
fprintf(stderr,"interpret.c: Eccessive nested IOintoBuffer operations; over 20 of them in recursion!\n");
abort();
}
break;
case sIOsetIn:
defaultfd = 0;
ioflags = O_RDONLY;
break;
case sIOsetInOut:
defaultfd = 0;
ioflags = O_CREAT|O_RDWR;
break;
case sIOsetOut:
defaultfd = 1;
ioflags = O_CREAT|O_WRONLY|O_TRUNC;
break;
case sIOsetAppend:
defaultfd = 1;
ioflags = O_CREAT|O_WRONLY|O_APPEND;
break;
case sIOsetDesc:
defaultfd = atoi((const char *)arg1);
break;
case sParameter:
if (caller != NULL && caller->argv != NULL) {
int slen;
l = car(caller->argv);
if ((d = cdr(l)) != NULL) {
cdr(l) = cddr(l);
if (!LIST(d))
name = (const char *)d->string;
} else
name = "";
if (d != NULL && LIST(d)) {
d = copycell(d);
cdr(d) = NULL;
} else {
slen = strlen(name);
d = newstring(dupnstr(name,slen),slen);
}
/* create the variable in the current scope */
l = car(envarlist);
cdr(d) = car(l);
if (*arg1) {
slen = strlen(arg1);
#if 0
/* XXX: If we can be sure of input
(arg1) being on stabile storage..
Command-line interpreter (shell)
is not stabile storage, but script
interpreter is! */
car(l) = conststring(arg1,slen);
#else
car(l) = newstring(dupnstr(arg1,slen),slen);
#endif
} else
car(l) = conststring(uBLANK,0);
cdar(l) = d;
/* grindef("ARGV = ", caller->argv);
grindef("VARS = ", envarlist);
grindef("TMPO = ", l); */
} else {
fprintf(stderr, "parameter without call\n");
abort(); /* parameter without call! */
}
break;
case sAssign:
case sAssignTemporary:
if (command->argv == NULL ||
cdar(command->argv) == NULL)
break;
for (d = caar(command->argv); cdr(d) != NULL; )
d = cdr(d);
coalesce(command);
if (command->buffer == NULL)
command->buffer = conststring(uBLANK,0);
if (nsift >= 0)
v_accessed = sift[nsift].accessed;
if (cmd == sAssign)
assign(d, command->buffer, NULL);
else
assign(d, command->buffer, command);
/* do NOT reset *retcodep here */
command->bufferp = &command->buffer;
command->argv = NULL;
command->shcmdp = NULL;
if (isset('I'))
grindef("Argv = ", command->argv);
break;
case sFunction:
if (isset('I'))
fprintf(runiofp,
"defining '%s', entry@ %d, exit@ %d cdp@ %p\n",
command->buffer->string,
pc + 1 - code, argi1, cdp);
defun(cdp, command->buffer->string, pc+1,
code + argi1);
/* FALL THROUGH */
case sJump:
pc = code + argi1 - 1;
break;
case sBranchOrigin:
fprintf(stderr, "%s: unpatched branch at %d\n",
progname, pc - code);
break;
case sJumpFork:
if (command->doio != NULL) {
RUNIO(command->doio);
freeio(command->doio, 1);
command->doio = NULL;
}
if ((childpid = fork()) == 0) {
/* in child */
dye = 1;
eocode = code + argi1;
if (command->execio != NULL) {
RUNIO(command->execio);
freeio(command->execio, 0);
command->execio = NULL;
}
command->reaperTop = -1;
--ibt;
} else if (childpid > 0) {
/* in parent */
while (wait(NULL) != childpid)
continue;
pc = code + argi1 - 1;
} else if (childpid < 0) {
/* error */
}
break;
case sJumpIfFailure:
if (*retcodep > 0)
pc = code + argi1 - 1;
break;
case sJumpIfSuccess:
if (*retcodep == 0)
pc = code + argi1 - 1;
break;
case sJumpIfNilVariable:
if (variable == NULL || car(variable) == NULL)
pc = code + argi1 - 1;
break;
case sJumpIfMatch:
if (command->buffer == NULL)
break;
if (variable == NULL) {
variable = conststring(uBLANK,0);
variable = ncons(variable);
} else if (car(variable)->string == NULL)
break;
globchars['|'] = 1;
iname = NULL; name = NULL;
/* NOTE: Earlier we did GLOB processing on result!
Now we absolutely forbid it.. */
switch (squish(command->buffer,(char**)&name,&iname,-1)){
case -1:
if (STRING(command->buffer)
&& strcmp(command->buffer->string,
car(variable)->string) == 0)
pc = code + argi1 - 1;
break;
case 0:
if (strcmp(name, car(variable)->string) == 0)
pc = code + argi1 - 1;
break;
case 1:
i = 0;
do {
int fi = i;
while (iname[i] != 0
&& iname[i] != (u_int)'|')
++i;
if (glob_match(&iname[fi], &iname[i],
car(variable)->string)) {
pc = code + argi1 - 1;
break;
}
} while (iname[i++] != 0);
break;
}
if (iname) free(iname);
globchars['|'] = 0;
break;
case sJumpIfFindVarNil:
if (command->buffer == NULL)
d = conststring(uBLANK,0);
else if (cdr(command->buffer))
d = s_catstring(command->buffer);
else
d = command->buffer;
d = v_expand(d->string, caller, *retcodep);
if (d == NULL)
pc = code + argi1 - 1;
break;
case sJumpIfOrValueNil:
if (d == NULL
|| (cdr(d) != NULL && (d = cdr(d)) == NULL)
|| (STRING(d)
&& (d->string == NULL || *d->string == '\0'))
|| (LIST(d) && car(d) == NULL))
pc = code + argi1 - 1;
break;
case sJumpLoopBreak:
loop[nloop].brk = argi1;
break;
case sJumpLoopContinue:
loop[nloop].cont = argi1;
break;
case sLoopEnter:
++nloop;
if (nloop >= LOOPMAXDEPTH) {
fprintf(stderr,"%s: interpret.c: sLoopEnter once too many times, max depth: %d\n",
progname, LOOPMAXDEPTH);
abort(); /* sLoopEnter too deep! */
}
loop[nloop].cont = 0;
loop[nloop].brk = 0;
loop[nloop].cmdindex = commandIndex;
loop[nloop].varindex = variableIndex;
if (prevcommand != NULL) {
prevcommand->next = command;
prevcommand->reaperTop = reapableTop;
/* create stringbuffer */
name = NULL;
INSERTIO(&prevcommand->doio,
prevcommand,
sIObufOut, 1, 1, 0);
/* --- */
RUNIO(prevcommand->doio);
freeio(prevcommand->doio, 1);
prevcommand->doio = NULL;
/* --- */
INSERTIO(&command->doio,
command,
sIObufIn, 0, 1, 0);
INSERTIO(&command->undoio,
command,
sIObufFree, 0, 0, 0);
/*
* ... else we're connecting two
* list-valued functions, which is ok.
*/
cmdname = NULL;
if (prevcommand->sfdp)
cmdname = prevcommand->sfdp->name;
if (prevcommand->shcmdp)
cmdname = prevcommand->shcmdp->name;
runcommand(prevcommand, caller,
ignore_level > commandIndex ?
retcodep : NULL, cmdname);
command->rval = prevcommand->rval;
if (command->prev == prevcommand)
command->prev = NULL;
free((char *)prevcommand);
prevcommand = NULL;
/* X:shouldn't prevcommand be stacked?*/
}
break;
case sLoopExit:
if (nloop >= 0)
--nloop;
break;
case sLocalVariable:
/* create the variable in the current scope */
d = NIL;
{
int slen = strlen(arg1);
#if 0
/* XXX: If we can be sure of input
(arg1) being on stabile storage..
Command-line interpreter (shell)
is not stabile storage, but script
interpreter is! */
tmp = conststring(arg1,slen);
#else
tmp = newstring(dupnstr(arg1,slen),slen);
#endif
}
cdr(d) = caar(envarlist);
cdr(tmp) = d;
caar(envarlist) = tmp;
if (isset('I'))
grindef("Scopes = ", envarlist);
break;
case sScopePush:
d = NIL;
s_push(d, envarlist);
/* fvcache.namesymbol = 0; */
break;
case sScopePop:
d = car(envarlist);
car(envarlist) = cdar(envarlist);
cdr(d) = NULL;
/*s_free_tree(d);*/
/* fvcache.namesymbol = 0; */
break;
case sDollarExpand:
dollar = 1;
break;
case sBufferQuote:
quote = 1;
break;
case sBackground:
/*
* 1. tell child and pipeline commands to background.
* 2. find place to stash pid of this command for pgrp.
* 3. call hook when this command finishes to print
* pids of all commands and job number.
*/
command->pgrpp = &command->pgrp;
break;
case sPrintAndExit:
if (command->buffer == NULL)
d = conststring(uBLANK,0);
else if (cdr(command->buffer))
d = s_catstring(command->buffer);
else
d = command->buffer;
fprintf(stderr, "%s: %s\n", progname,
*(d->string) == '\0' ?
"parameter null or not set"
: d->string);
if (!isset('i'))
trapexit(1);
break;
#ifdef MAILER
/* String RegExpressions */
case sSiftPush:
v_record = 1;
if (nsift >= 0)
sift[nsift].program = re;
if (++nsift >= (sizeof sift / sizeof sift[0])) {
fprintf(stderr,"%s: sSiftPush more than %d times allowed by the interpret.c code\n",
progname, nsift-1);
abort(); /* Too deep SIFTs! */
}
sift[nsift].kind = 0;
sift[nsift].tlist = NULL; /* the other sift type */
sift[nsift].str = NULL; /* this sift type */
sift[nsift].label = pc+1 - code;
sift[nsift].subexps = NULL;
sift[nsift].count = 9999; /* Cut eternal loops */
v_accessed = NULL;
break;
case sSiftBody:
#ifndef DEQUOTE_STICKY
if (sift[nsift].str)
free((void*)sift[nsift].str);
#endif
sift[nsift].str = NULL;
#ifdef DEBUG
if (D_compare) {
fprintf(stderr,
"%*ssSiftBody '", 4*funclevel, " ");
if (command->buffer != NULL)
s_grind(command->buffer, stderr);
else
fprintf(stderr, "(nil)");
fprintf(stderr,"'\n");
}
#endif /* DEBUG */
if (command->buffer != NULL) {
if (cdr(command->buffer))
d = s_catstring(command->buffer);
else
d = command->buffer;
if (STRING(d)) {
sift[nsift].str = dequote(d->cstring, d->slen);
}
}
sift[nsift].accessed = v_accessed; /* nop 2nd time */
if (v_record == 0) /* we've been here before */
pc = code + loop[nloop].cont - 1;
else
v_record = 0;
v_changed = 0;
break;
case sSiftCompileRegexp:
if (argi1) {
re = cdp->rearray[argi1-1];
#if 0
std_printf("found %x at %d\n", re, argi1-1);
#endif
break;
}
re = NULL;
if (command->buffer != NULL) {
if (cdr(command->buffer)) {
/* stickytmp = stickymem;
stickymem = MEM_PERM; */
d = s_catstring(command->buffer);
/* stickymem = stickytmp; */
} else
d = command->buffer;
if (STRING(d))
re = reg_comp(d->string, d->slen);
}
if (re == NULL)
break;
if (cdp->rearray_size == 0) {
#define RECLICK 25
cdp->rearray_size = RECLICK;
cdp->rearray = (regexp **)
emalloc(RECLICK*sizeof(regexp *));
} else if (cdp->rearray_idx >= cdp->rearray_size-2) {
/* 1 spare */
cdp->rearray_size *= 2;
cdp->rearray =
(regexp **)erealloc((char*)cdp->rearray,
cdp->rearray_size *
sizeof(regexp *));
}
cdp->rearray[++cdp->rearray_idx] = re;
#if 0
std_printf("set %x at %d\n", re, cdp->rearray_idx);
#endif
/* NB! we are writing into the table */
++cdp->rearray_idx;
*(char*)(pc-3) = (cdp->rearray_idx >> 24) & 0xff;
*(char*)(pc-2) = (cdp->rearray_idx >> 16) & 0xff;
*(char*)(pc-1) = (cdp->rearray_idx >> 8) & 0xff;
*(char*)(pc ) = cdp->rearray_idx & 0xff;
--cdp->rearray_idx;
break;
case sSiftReevaluate:
if (v_changed)
/* jump to sift expression evaluation
followed by sSiftBody */
pc = sift[nsift].label - 1 + code;
break;
case sSiftPop:
#ifndef DEQUOTE_STICKY
if (sift[nsift].str)
free((void*)sift[nsift].str);
#endif
sift[nsift].str = NULL;
for (v_accessed = sift[nsift].accessed;
v_accessed != NULL;
v_accessed = sift[nsift].accessed) {
sift[nsift].accessed = v_accessed->next;
free((char *)v_accessed);
}
--nsift;
if (nsift >= 0) {
v_accessed = sift[nsift].accessed;
re = sift[nsift].program;
}
break;
case sSiftBufferAppend:
if (sift[nsift].kind == 0) { /* StringSift.. */
if (arg1 == NULL || re == NULL ||
!isdigit((*arg1)&0xFF))
break;
if (nsift > 0 && sift[nsift].subexps == NULL)
setsubexps(&sift[nsift-1].subexps, re);
else
setsubexps(&sift[nsift].subexps, re);
arg1 = regsub(re, atoi(arg1));
if (arg1 != NULL) {
int slen = strlen(arg1);
#if 0
/* XXX: If we can be sure of input
(arg1) being on stabile storage..
Command-line interpreter (shell)
is not stabile storage, but script
interpreter is! */
tmp = conststring(arg1,slen);
#else
tmp = newstring(dupnstr(arg1,slen),slen);
#endif
tmp->flags |= QUOTEDSTRING;
/* cdr(tmp) = command->buffer; */
*command->bufferp = tmp;
command->bufferp = &cdr(tmp);
}
} else { /* TokenSift */
char *s;
if (arg1 == NULL || tre == NULL ||
!isdigit((*arg1)&0xFF))
break;
if (nsift > 0 && sift[nsift].subexps == NULL)
tsetsubexps(&sift[nsift-1].subexps, tre);
else
tsetsubexps(&sift[nsift].subexps, tre);
s = tregsub(tre, atoi(arg1));
if (s != NULL) {
int slen = strlen(s);
tmp = newstring(s,slen);
tmp->flags |= QUOTEDSTRING;
/* cdr(tmp) = command->buffer; */
*command->bufferp = tmp;
command->bufferp = &cdr(tmp);
}
}
break;
case sJumpIfRegmatch:
stickytmp = stickymem;
#ifndef DEQUOTE_STICKY
stickymem = MEM_PERM;
if (sift[nsift].str == NULL)
sift[nsift].str = strdup("");
#else
stickymem = MEM_SHCMD;
if (sift[nsift].str == NULL)
sift[nsift].str = strsave("");
#endif
stickymem = stickytmp;
setsubexps(&sift[nsift].subexps, re);
if ((sift[nsift].count >= 0) &&
!reg_exec(re, sift[nsift].str))
pc = code + argi1 - 1;
sift[nsift].count -= 1;
break;
/* Token RegExpressions: */
case sTSiftPush:
v_record = 1;
if (nsift >= 0)
sift[nsift].program = (regexp *)tre;
if (++nsift >= (sizeof sift / sizeof sift[0])) {
fprintf(stderr,"%s: sTSiftPush more than %d times allowed by the interpret.c code\n",
progname, nsift-1);
abort(); /* Too deep TSIFT! */
}
sift[nsift].kind = 1;
sift[nsift].tlist = NULL;
sift[nsift].str = NULL; /* the other branch */
sift[nsift].label = pc+1 - code;
sift[nsift].subexps = NULL;
sift[nsift].count = 9999; /* Cut eternal loops */
v_accessed = NULL;
break;
case sTSiftBody:
/* we don't *need* to free tokens because they are
allocated off our MEM_SHCMD memory stack */
if (sift[nsift].tlist)
freeTokens(sift[nsift].tlist, MEM_SHCMD);
sift[nsift].tlist = NULL;
if (command->buffer != NULL) {
if (cdr(command->buffer))
d = s_catstring(command->buffer);
else
d = command->buffer;
if (STRING(d)) {
arg1 = (const char *) d->string;
sift[nsift].tlist = tscanstring(arg1);
}
}
sift[nsift].accessed = v_accessed; /* nop 2nd time */
if (v_record == 0) /* we've been here before */
pc = code + loop[nloop].cont - 1;
else
v_record = 0;
v_changed = 0;
break;
case sTSiftCompileRegexp:
if (argi1) {
tre = cdp->trearray[argi1-1];
#if 0
std_printf("found %x at %d\n", tre, argi1-1);
#endif
break;
}
tre = NULL;
if (command->buffer != NULL) {
if (cdr(command->buffer)) {
d = s_catstring(command->buffer);
} else
d = command->buffer;
if (STRING(d))
tre = tregcomp(d->string);
}
if (tre == NULL)
break;
if (cdp->trearray_size == 0) {
cdp->trearray_size = RECLICK;
cdp->trearray = (tregexp **)
emalloc(RECLICK*sizeof(tregexp *));
} else if (cdp->trearray_idx >= cdp->trearray_size-2) {
/* 1 spare */
cdp->trearray_size *= 2;
cdp->trearray =
(tregexp **)erealloc((char*)cdp->trearray,
cdp->trearray_size *
sizeof(tregexp *));
}
cdp->trearray[++cdp->trearray_idx] = tre;
#if 0
std_printf("set %x at %d\n", tre, cdp->trearray_idx);
#endif
/* NB! we are writing into the table */
++cdp->trearray_idx;
*(char*)(pc-3) = (cdp->trearray_idx >> 24) & 0xff;
*(char*)(pc-2) = (cdp->trearray_idx >> 16) & 0xff;
*(char*)(pc-1) = (cdp->trearray_idx >> 8) & 0xff;
*(char*)(pc ) = cdp->trearray_idx & 0xff;
--cdp->trearray_idx;
break;
case sTSiftReevaluate:
if (v_changed)
/* jump to sift expression evaluation
followed by sTSiftBody */
pc = sift[nsift].label-1 + code;
break;
case sTSiftPop:
/* see comment above about freeing tokens */
if (sift[nsift].tlist)
freeTokens(sift[nsift].tlist, MEM_SHCMD);
sift[nsift].tlist = NULL;
for (v_accessed = sift[nsift].accessed;
v_accessed != NULL;
v_accessed = sift[nsift].accessed) {
sift[nsift].accessed = v_accessed->next;
free((char *)v_accessed);
}
--nsift;
if (nsift >= 0) {
v_accessed = sift[nsift].accessed;
tre = (tregexp *)sift[nsift].program;
}
break;
#if 0 /* wrong place.. */
case sTSiftBufferAppend:
if (arg1 == NULL || tre == NULL || !isdigit(*arg1))
break;
if (nsift > 0 && sift[nsift].subexps == NULL)
tsetsubexps(&sift[nsift-1].subexps, tre);
else
tsetsubexps(&sift[nsift].subexps, tre);
if ((arg1 = tregsub(tre,atoi(arg1))) != NULL) {
int slen = strlen(arg1);
#if 0
tmp = coststring(arg1, slen);
#else
tmp = newstring(dupnstr(arg1,slen),slen);
#endif
tmp->flags |= QUOTEDSTRING;
/* cdr(tmp) = command->buffer; */
*command->bufferp = tmp;
command->bufferp = &cdr(tmp);
}
break;
#endif
case sTJumpIfRegmatch:
if (sift[nsift].tlist == NULL)
sift[nsift].tlist = makeToken(uBLANK, 0);
tsetsubexps(&sift[nsift].subexps, tre);
if ((sift[nsift].count >= 0) &&
!tregexec(tre, sift[nsift].tlist))
pc = code + argi1 - 1;
sift[nsift].count -= 1;
break;
#endif /* MAILER */
default:
fprintf(stderr,
"Hey, you forgot to update the interpreter!\n");
fprintf(stderr,
"Illegal command token %d\n", (int)cmd);
if (!isset('i'))
exit(1);
break;
}
}
getout:
if (dye /* I know this is misspelled */)
trapexit(0);
if (isset('I'))
grindef("Vars = ", envarlist);
/* null any loop variable values so we can free scopes below */
while (variableIndex > -1) {
if (varmeter) {
car(varmeter) = NULL;
varmeter->flags = 0;
}
if (varmchain) {
variable = car(varmchain);
varmeter = cadr(varmchain);
varmchain = cddr(varmchain);
} else
variable = varmeter = NULL;
if (isset('I'))
grindef("Variable = ", variable);
--variableIndex;
}
while (nsift >= 0) {
#ifndef DEQUOTE_STICKY
if (sift[nsift].str)
free((void*)sift[nsift].str);
#endif
sift[nsift].str = NULL;
/* we don't *need* to free tokens because they are
allocated off our MEM_SHCMD memory stack */
if (sift[nsift].tlist)
freeTokens(sift[nsift].tlist, MEM_SHCMD);
sift[nsift].tlist = NULL;
for (v_accessed = sift[nsift].accessed;
v_accessed != NULL;
v_accessed = sift[nsift].accessed) {
sift[nsift].accessed = v_accessed->next;
free((char *)v_accessed);
}
--nsift;
}
while (commandIndex > 0) {
pcommand = &commandStack[commandIndex];
if (pcommand->undoio) {
RUNIO(pcommand->undoio);
freeio(pcommand->undoio, 1);
}
/* The setlevel is done below */
/* XX: Now we free them in cases where the command
has no return value to be cared for ! */
if (command->rval == NULL || caller == NULL)
setlevel(MEM_SHCMD, pcommand->memlevel);
/* Can do without UNGCPROSTORE here, as we do
a larger scope UNGCPRO below.. */
UNGCPROSTORE4( commandStack[commandIndex]. );
--commandIndex;
}
setlevel(MEM_SHCMD, origlevel);
#ifdef MAILER
/*
* This is pretty dicey; we rely on setlevel() not changing the
* stuff that was just freed. We need to do this to avoid
* malloc'ing stuff unnecessarily. For example we usually just
* want to access the return value in the context of the caller.
*/
if (command->rval != NULL && caller != NULL) {
caller->rval = command->rval;
}
#endif
while (margin != car(envarlist)) {
d = car(envarlist);
car(envarlist) = cdr(d);
cdr(d) = NULL;
/*s_free_tree(d);*/
}
stickymem = origstickymem;
#ifdef MAILER
--funclevel;
#endif /* MAILER */
commandIndex = 0; /* Should be already, in fact.. */
UNGCPROSTORE4( commandStack[commandIndex]. );
if (cdp->functions == NULL) {
if (cdp->rearray != NULL) {
while (cdp->rearray_idx >= 0)
free_regexp(cdp->rearray[cdp->rearray_idx--]);
free((char *)cdp->rearray);
}
if (cdp->trearray != NULL) {
while (cdp->trearray_idx >= 0)
free((void *)cdp->trearray[cdp->trearray_idx--]);
free((void *)cdp->trearray);
}
free((void *)cdp->table);
free((void *)cdp);
UNGCPRO6;
return NULL;
}
cdp->oktofree = 1;
UNGCPRO6;
return cdp;
}
/*
* Doing an apply() outside the interpreter() is only a safe thing to do
* when outside the interpreter (...), i.e. at interactive prompt level.
* We want to call an arbitrary shell or builtin function with some number
* of (string) arguments.
*/
STATIC int fapply __((struct shCmd *, conscell *));
STATIC int
fapply(shcmdp, l)
struct shCmd *shcmdp;
conscell *l;
{
register conscell *ll;
int argc = 0;
#define FARGCMAX 30
const char *argv[FARGCMAX]; /* XX: argc never to exceed magic number */
if (shcmdp->sptr != NULL) {
argv[argc++] = shcmdp->name;
for (ll = car(l); ll != NULL && argc < FARGCMAX-1; ll = cdr(ll)) {
if (STRING(ll))
argv[argc++] = ll->string;
}
argv[argc] = NULL;
return (*(shcmdp->sptr))(argc, argv);
}
/* XX: we don't need to support this, yet */
/* else if (shcmdp->lptr != NULL) {
return -1;
} */
return -1;
}
int
lapply(fname, l)
const char *fname;
conscell *l;
{
int retcode = -123456;
struct sslfuncdef *sfdp;
struct spblk *spl;
struct osCmd avc;
conscell *ll, *tmp;
spkey_t spkey;
GCVARS4;
#ifdef DEBUG
if (D_functions) {
fprintf(stderr, "%*slapply('%s', (", 4*funclevel, " ", fname);
ll = l;
while (ll) {
fprintf(stderr,"'");
s_grind(ll, stderr);
ll = cdr(ll);
if (ll)
fprintf(stderr,"', ");
else
fprintf(stderr,"'");
}
fprintf(stderr, "))\n");
fflush(stderr);
}
#endif
spkey = symbol_lookup(fname);
spl = NULL;
if (!spkey)
return -1;
spl = sp_lookup(spkey, spt_funclist);
if (spl == NULL) {
spl = sp_lookup(spkey, spt_builtins);
if (spl == NULL)
return -1;
/* Non conscell input parameters to the target
function, no conscell creates while calling it. */
return fapply((struct shCmd *)spl->data, l);
}
sfdp = (struct sslfuncdef *)spl->data;
if (sfdp == NULL)
return -1;
avc = avcmd;
ll = tmp = NULL;
GCPRO4(ll, l, avc.argv, avc.rval);
if (l != NULL) {
int slen = strlen(fname);
ll = newstring(dupnstr(fname,slen),slen);
/* Sometimes could do without strdup(),
but often not... :-/ */
cdr(ll) = car(l);
car(l) = ll;
avc.argv = l;
}
interpret(sfdp->tabledesc->table,
sfdp->eot, sfdp->pos,
&avc, &retcode, sfdp->tabledesc);
UNGCPRO4;
if (return_valuep != NULL) {
*return_valuep = avc.rval;
}
avc.rval = NULL;
return retcode;
}
int
apply(argc, argv)
int argc;
const char *argv[];
{
conscell *args = NULL;
int rc;
GCVARS1;
/* if argc == 0, don't change avc.argv even if there are arguments */
if (argc > 1)
args = s_listify(argc-1, &argv[1]);
GCPRO1(args);
rc = lapply(argv[0], args);
UNGCPRO1;
return rc;
}
/*
* A cheap way of calling (e.g.) prompt-generating functions without
* having to set up argv lists and such. Same restrictions as apply().
*/
int
funcall(fname)
const char *fname;
{
const char *av[1];
av[0] = fname;
return apply(0, &av[0]);
}
syntax highlighted by Code2HTML, v. 0.9.1