/* * 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 #include #include #include #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<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 = ""; 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 = ""; 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! ( 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]); }