diff options
Diffstat (limited to 'Src/exec.c')
-rw-r--r-- | Src/exec.c | 2965 |
1 files changed, 2965 insertions, 0 deletions
diff --git a/Src/exec.c b/Src/exec.c new file mode 100644 index 000000000..1b355d028 --- /dev/null +++ b/Src/exec.c @@ -0,0 +1,2965 @@ +/* + * exec.c - command execution + * + * This file is part of zsh, the Z shell. + * + * Copyright (c) 1992-1997 Paul Falstad + * All rights reserved. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and to distribute modified versions of this software for any + * purpose, provided that the above copyright notice and the following + * two paragraphs appear in all copies of this software. + * + * In no event shall Paul Falstad or the Zsh Development Group be liable + * to any party for direct, indirect, special, incidental, or consequential + * damages arising out of the use of this software and its documentation, + * even if Paul Falstad and the Zsh Development Group have been advised of + * the possibility of such damage. + * + * Paul Falstad and the Zsh Development Group specifically disclaim any + * warranties, including, but not limited to, the implied warranties of + * merchantability and fitness for a particular purpose. The software + * provided hereunder is on an "as is" basis, and Paul Falstad and the + * Zsh Development Group have no obligation to provide maintenance, + * support, updates, enhancements, or modifications. + * + */ + +#include "zsh.mdh" +#include "exec.pro" + +/* used to suppress ERREXIT and trapping of SIGZERR, SIGEXIT. */ + +/**/ +int noerrexit; + +/* suppress error messages */ + +/**/ +int noerrs; + +/* do not save history on exec and exit */ + +/**/ +int nohistsave; + +/* error/break flag */ + +/**/ +int errflag; + +/* Status of return from a trap */ + +/**/ +int trapreturn; + +/* != 0 if this is a subshell */ + +/**/ +int subsh; + +/* != 0 if we have a return pending */ + +/**/ +int retflag; + +/**/ +long lastval2; + +/* The table of file descriptors. A table element is zero if the * + * corresponding fd is not used by the shell. It is greater than * + * 1 if the fd is used by a <(...) or >(...) substitution and 1 if * + * it is an internal file descriptor which must be closed before * + * executing an external command. The first ten elements of the * + * table is not used. A table element is set by movefd and cleard * + * by zclose. */ + +/**/ +char *fdtable; + +/* The allocated size of fdtable */ + +/**/ +int fdtable_size; + +/* The highest fd that marked with nonzero in fdtable */ + +/**/ +int max_zsh_fd; + +/* input fd from the coprocess */ + +/**/ +int coprocin; + +/* output fd from the coprocess */ + +/**/ +int coprocout; + +/* != 0 if the line editor is active */ + +/**/ +int zleactive; + +/* pid of process undergoing 'process substitution' */ + +/**/ +pid_t cmdoutpid; + +/* exit status of process undergoing 'process substitution' */ + +/**/ +int cmdoutval; + +/* Stack to save some variables before executing a signal handler function */ + +/**/ +struct execstack *exstack; + +#define execerr() if (!forked) { lastval = 1; return; } else _exit(1) + +static LinkList args; +static int doneps4; + +/* parse string into a list */ + +/**/ +List +parse_string(char *s) +{ + List l; + + lexsave(); + inpush(s, 0, NULL); + strinbeg(); + stophist = 2; + l = parse_list(); + strinend(); + inpop(); + lexrestore(); + return l; +} + +#ifdef HAVE_GETRLIMIT + +/* the resource limits for the shell and its children */ + +/**/ +struct rlimit current_limits[RLIM_NLIMITS], limits[RLIM_NLIMITS]; + +/**/ +int +zsetlimit(int limnum, char *nam) +{ + if (limits[limnum].rlim_max != current_limits[limnum].rlim_max || + limits[limnum].rlim_cur != current_limits[limnum].rlim_cur) { + if (setrlimit(limnum, limits + limnum)) { + if (nam) + zwarnnam(nam, "setrlimit failed: %e", NULL, errno); + return -1; + } + current_limits[limnum] = limits[limnum]; + } + return 0; +} + +/**/ +int +setlimits(char *nam) +{ + int limnum; + int ret = 0; + + for (limnum = 0; limnum < RLIM_NLIMITS; limnum++) + if (zsetlimit(limnum, nam)) + ret++; + return ret; +} + +#endif /* HAVE_GETRLIMIT */ + +/* fork and set limits */ + +/**/ +static pid_t +zfork(void) +{ + pid_t pid; + + if (thisjob >= MAXJOB - 1) { + zerr("job table full", NULL, 0); + return -1; + } + pid = fork(); + if (pid == -1) { + zerr("fork failed: %e", NULL, errno); + return -1; + } +#ifdef HAVE_GETRLIMIT + if (!pid) + /* set resource limits for the child process */ + setlimits(NULL); +#endif + return pid; +} + + +/**/ +int list_pipe = 0, simple_pline = 0; + +static pid_t list_pipe_pid; +static int nowait, pline_level = 0; +static int list_pipe_child = 0, list_pipe_job; +static char list_pipe_text[JOBTEXTSIZE]; + +/* execute a current shell command */ + +/**/ +static int +execcursh(Cmd cmd) +{ + if (!list_pipe) + deletejob(jobtab + thisjob); + execlist(cmd->u.list, 1, cmd->flags & CFLAG_EXEC); + cmd->u.list = NULL; + return lastval; +} + +/* execve after handling $_ and #! */ + +#define POUNDBANGLIMIT 64 + +/**/ +static int +zexecve(char *pth, char **argv) +{ + int eno; + static char buf[PATH_MAX * 2]; + char **eep; + + unmetafy(pth, NULL); + for (eep = argv; *eep; eep++) + if (*eep != pth) + unmetafy(*eep, NULL); + for (eep = environ; *eep; eep++) + if (**eep == '_' && (*eep)[1] == '=') + break; + buf[0] = '_'; + buf[1] = '='; + if (*pth == '/') + strcpy(buf + 2, pth); + else + sprintf(buf + 2, "%s/%s", pwd, pth); + if (!*eep) + eep[1] = NULL; + *eep = buf; + execve(pth, argv, environ); + + /* If the execve returns (which in general shouldn't happen), * + * then check for an errno equal to ENOEXEC. This errno is set * + * if the process file has the appropriate access permission, * + * but has an invalid magic number in its header. */ + if ((eno = errno) == ENOEXEC) { + char execvebuf[POUNDBANGLIMIT + 1], *ptr, *ptr2, *argv0; + int fd, ct, t0; + + if ((fd = open(pth, O_RDONLY|O_NOCTTY)) >= 0) { + argv0 = *argv; + *argv = pth; + ct = read(fd, execvebuf, POUNDBANGLIMIT); + close(fd); + if (ct > 0) { + if (execvebuf[0] == '#') { + if (execvebuf[1] == '!') { + for (t0 = 0; t0 != ct; t0++) + if (execvebuf[t0] == '\n') + execvebuf[t0] = '\0'; + execvebuf[POUNDBANGLIMIT] = '\0'; + for (ptr = execvebuf + 2; *ptr && *ptr == ' '; ptr++); + for (ptr2 = ptr; *ptr && *ptr != ' '; ptr++); + if (*ptr) { + *ptr = '\0'; + argv[-2] = ptr2; + argv[-1] = ptr + 1; + execve(ptr2, argv - 2, environ); + } else { + argv[-1] = ptr2; + execve(ptr2, argv - 1, environ); + } + } else { + argv[-1] = "sh"; + execve("/bin/sh", argv - 1, environ); + } + } else { + for (t0 = 0; t0 != ct; t0++) + if (!execvebuf[t0]) + break; + if (t0 == ct) { + argv[-1] = "sh"; + execve("/bin/sh", argv - 1, environ); + } + } + } else + eno = errno; + *argv = argv0; + } else + eno = errno; + } + /* restore the original arguments and path but do not bother with * + * null characters as these cannot be passed to external commands * + * anyway. So the result is truncated at the first null char. */ + pth = metafy(pth, -1, META_NOALLOC); + for (eep = argv; *eep; eep++) + if (*eep != pth) + (void) metafy(*eep, -1, META_NOALLOC); + return eno; +} + +#define MAXCMDLEN (PATH_MAX*4) + +/* test whether we really want to believe the error number */ + +/**/ +static int +isgooderr(int e, char *dir) +{ + /* + * Maybe the directory was unreadable, or maybe it wasn't + * even a directory. + */ + return ((e != EACCES || !access(dir, X_OK)) && + e != ENOENT && e != ENOTDIR); +} + +/* execute an external command */ + +/**/ +void +execute(Cmdnam not_used_yet, int dash) +{ + Cmdnam cn; + static LinkList exargs; + char buf[MAXCMDLEN], buf2[MAXCMDLEN]; + char *s, *z, *arg0; + char **argv, **pp; + int eno = 0, ee; + + arg0 = (char *) peekfirst(args); + if (isset(RESTRICTED) && strchr(arg0, '/')) { + zerr("%s: restricted", arg0, 0); + _exit(1); + } + + /* If the parameter STTY is set in the command's environment, * + * we first run the stty command with the value of this * + * parameter as it arguments. */ + if (!exargs && (s = zgetenv("STTY")) && isatty(0)) { + char *t; + + exargs = args; /* this prevents infinite recursion */ + args = NULL; + t = tricat("stty", " ", s); + execstring(t, 1, 0); + zsfree(t); + args = exargs; + exargs = NULL; + } + + cn = (Cmdnam) cmdnamtab->getnode(cmdnamtab, arg0); + + /* If ARGV0 is in the commands environment, we use * + * that as argv[0] for this external command */ + if (unset(RESTRICTED) && (z = zgetenv("ARGV0"))) { + setdata(firstnode(args), (void *) ztrdup(z)); + delenv(z - 6); + } else if (dash) { + /* Else if the pre-command `-' was given, we add `-' * + * to the front of argv[0] for this command. */ + sprintf(buf2, "-%s", arg0); + setdata(firstnode(args), (void *) ztrdup(buf2)); + } + + argv = makecline(args); + child_unblock(); + if ((int) strlen(arg0) >= PATH_MAX) { + zerr("command too long: %s", arg0, 0); + _exit(1); + } + for (s = arg0; *s; s++) + if (*s == '/') { + errno = zexecve(arg0, argv); + if (arg0 == s || unset(PATHDIRS) || + (arg0[0] == '.' && (arg0 + 1 == s || + (arg0[1] == '.' && arg0 + 2 == s)))) { + zerr("%e: %s", arg0, errno); + _exit(1); + } + break; + } + + if (cn) { + char nn[PATH_MAX], *dptr; + + if (cn->flags & HASHED) + strcpy(nn, cn->u.cmd); + else { + for (pp = path; pp < cn->u.name; pp++) + if (!**pp || (**pp == '.' && (*pp)[1] == '\0')) { + ee = zexecve(arg0, argv); + if (isgooderr(ee, *pp)) + eno = ee; + } else if (**pp != '/') { + z = buf; + strucpy(&z, *pp); + *z++ = '/'; + strcpy(z, arg0); + ee = zexecve(buf, argv); + if (isgooderr(ee, *pp)) + eno = ee; + } + strcpy(nn, cn->u.name ? *(cn->u.name) : ""); + strcat(nn, "/"); + strcat(nn, cn->nam); + } + ee = zexecve(nn, argv); + + if ((dptr = strrchr(nn, '/'))) + *dptr = '\0'; + if (isgooderr(ee, *nn ? nn : "/")) + eno = ee; + } + for (pp = path; *pp; pp++) + if (!(*pp)[0] || ((*pp)[0] == '.' && !(*pp)[1])) { + ee = zexecve(arg0, argv); + if (isgooderr(ee, *pp)) + eno = ee; + } else { + z = buf; + strucpy(&z, *pp); + *z++ = '/'; + strcpy(z, arg0); + ee = zexecve(buf, argv); + if (isgooderr(ee, *pp)) + eno = ee; + } + if (eno) + zerr("%e: %s", arg0, eno); + else + zerr("command not found: %s", arg0, 0); + _exit(1); +} + +#define try(X) { if (iscom(X)) return ztrdup(X); } + +/* get the full pathname of an external command */ + +/**/ +char * +findcmd(char *arg0) +{ + char **pp; + char *z, *s, buf[MAXCMDLEN]; + Cmdnam cn; + + cn = (Cmdnam) cmdnamtab->getnode(cmdnamtab, arg0); + if (!cn && isset(HASHCMDS)) + cn = hashcmd(arg0, path); + if ((int) strlen(arg0) > PATH_MAX) + return NULL; + for (s = arg0; *s; s++) + if (*s == '/') { + try(arg0); + if (arg0 == s || unset(PATHDIRS)) { + return NULL; + } + break; + } + if (cn) { + char nn[PATH_MAX]; + + if (cn->flags & HASHED) + strcpy(nn, cn->u.cmd); + else { + for (pp = path; pp < cn->u.name; pp++) + if (**pp != '/') { + z = buf; + if (**pp) { + strucpy(&z, *pp); + *z++ = '/'; + } + strcpy(z, arg0); + try(buf); + } + strcpy(nn, cn->u.name ? *(cn->u.name) : ""); + strcat(nn, "/"); + strcat(nn, cn->nam); + } + try(nn); + } + for (pp = path; *pp; pp++) { + z = buf; + if (**pp) { + strucpy(&z, *pp); + *z++ = '/'; + } + strcpy(z, arg0); + try(buf); + } + return NULL; +} + +/**/ +int +iscom(char *s) +{ + struct stat statbuf; + char *us = unmeta(s); + + return (access(us, X_OK) == 0 && stat(us, &statbuf) >= 0 && + S_ISREG(statbuf.st_mode)); +} + +/**/ +int +isreallycom(Cmdnam cn) +{ + char fullnam[MAXCMDLEN]; + + strcpy(fullnam, cn->u.name ? *(cn->u.name) : ""); + strcat(fullnam, "/"); + strcat(fullnam, cn->nam); + return iscom(fullnam); +} + +/**/ +int +isrelative(char *s) +{ + if (*s != '/') + return 1; + for (; *s; s++) + if (*s == '.' && s[-1] == '/' && + (s[1] == '/' || s[1] == '\0' || + (s[1] == '.' && (s[2] == '/' || s[2] == '\0')))) + return 1; + return 0; +} + +/**/ +Cmdnam +hashcmd(char *arg0, char **pp) +{ + Cmdnam cn; + char *s, buf[PATH_MAX]; + char **pq; + + for (; *pp; pp++) + if (**pp == '/') { + s = buf; + strucpy(&s, *pp); + *s++ = '/'; + if ((s - buf) + strlen(arg0) >= PATH_MAX) + continue; + strcpy(s, arg0); + if (iscom(buf)) + break; + } + + if (!*pp) + return NULL; + + cn = (Cmdnam) zcalloc(sizeof *cn); + cn->flags = 0; + cn->u.name = pp; + cmdnamtab->addnode(cmdnamtab, ztrdup(arg0), cn); + + if (isset(HASHDIRS)) { + for (pq = pathchecked; pq <= pp; pq++) + hashdir(pq); + pathchecked = pp + 1; + } + + return cn; +} + +/* execute a string */ + +/**/ +void +execstring(char *s, int dont_change_job, int exiting) +{ + List list; + + pushheap(); + if ((list = parse_string(s))) + execlist(list, dont_change_job, exiting); + popheap(); +} + +/* Main routine for executing a list. * + * exiting means that the (sub)shell we are in is a definite goner * + * after the current list is finished, so we may be able to exec the * + * last command directly instead of forking. If dont_change_job is * + * nonzero, then restore the current job number after executing the * + * list. */ + +/**/ +void +execlist(List list, int dont_change_job, int exiting) +{ + Sublist slist; + static int donetrap; + int ret, cj; + int old_pline_level, old_list_pipe; + + cj = thisjob; + old_pline_level = pline_level; + old_list_pipe = list_pipe; + + if (sourcelevel && unset(SHINSTDIN)) + pline_level = list_pipe = 0; + + /* Loop over all sets of comands separated by newline, * + * semi-colon or ampersand (`sublists'). */ + while (list && list != &dummy_list && !breaks && !retflag) { + /* Reset donetrap: this ensures that a trap is only * + * called once for each sublist that fails. */ + donetrap = 0; + simplifyright(list); + slist = list->left; + + /* Loop through code followed by &&, ||, or end of sublist. */ + while (slist) { + switch (slist->type) { + case END: + /* End of sublist; just execute, ignoring status. */ + execpline(slist, list->type, !list->right && exiting); + goto sublist_done; + break; + case ANDNEXT: + /* If the return code is non-zero, we skip pipelines until * + * we find a sublist followed by ORNEXT. */ + if ((ret = execpline(slist, Z_SYNC, 0))) { + while ((slist = slist->right)) + if (slist->type == ORNEXT) + break; + if (!slist) { + /* We've skipped to the end of the list, not executing * + * the final pipeline, so don't perform error handling * + * for this sublist. */ + donetrap = 1; + goto sublist_done; + } + } + break; + case ORNEXT: + /* If the return code is zero, we skip pipelines until * + * we find a sublist followed by ANDNEXT. */ + if (!(ret = execpline(slist, Z_SYNC, 0))) { + while ((slist = slist->right)) + if (slist->type == ANDNEXT) + break; + if (!slist) { + /* We've skipped to the end of the list, not executing * + * the final pipeline, so don't perform error handling * + * for this sublist. */ + donetrap = 1; + goto sublist_done; + } + } + break; + } + slist = slist->right; + } +sublist_done: + + if (sigtrapped[SIGDEBUG]) + dotrap(SIGDEBUG); + + /* Check whether we are suppressing traps/errexit * + * (typically in init scripts) and if we haven't * + * already performed them for this sublist. */ + if (!noerrexit && !donetrap) { + if (sigtrapped[SIGZERR] && lastval) { + dotrap(SIGZERR); + donetrap = 1; + } + if (lastval && isset(ERREXIT)) { + if (sigtrapped[SIGEXIT]) + dotrap(SIGEXIT); + if (mypid != getpid()) + _exit(lastval); + else + exit(lastval); + } + } + + list = list->right; + } + + pline_level = old_pline_level; + list_pipe = old_list_pipe; + if (dont_change_job) + thisjob = cj; +} + +/* Execute a pipeline. * + * last1 is a flag that this command is the last command in a shell * + * that is about to exit, so we can exec instead of forking. It gets * + * passed all the way down to execcmd() which actually makes the * + * decision. A 0 is always passed if the command is not the last in * + * the pipeline. This function assumes that the sublist is not NULL. * + * If last1 is zero but the command is at the end of a pipeline, we * + * pass 2 down to execcmd(). * + */ + +/**/ +static int +execpline(Sublist l, int how, int last1) +{ + int ipipe[2], opipe[2]; + int pj, newjob; + int old_simple_pline = simple_pline; + static int lastwj; + + if (!l->left) + return lastval = (l->flags & PFLAG_NOT) != 0; + + pj = thisjob; + ipipe[0] = ipipe[1] = opipe[0] = opipe[1] = 0; + child_block(); + + /* get free entry in job table and initialize it */ + if ((thisjob = newjob = initjob()) == -1) + return 1; + if (how & Z_TIMED) + jobtab[thisjob].stat |= STAT_TIMED; + + if (l->flags & PFLAG_COPROC) { + how = Z_ASYNC; + if (coprocin >= 0) { + zclose(coprocin); + zclose(coprocout); + } + mpipe(ipipe); + mpipe(opipe); + coprocin = ipipe[0]; + coprocout = opipe[1]; + fdtable[coprocin] = fdtable[coprocout] = 0; + } + if (!pline_level++) { + list_pipe_job = newjob; + nowait = 0; + } + list_pipe_pid = lastwj = 0; + if (pline_level == 1) + simple_pline = (l->left->type == END); + execpline2(l->left, how, opipe[0], ipipe[1], last1); + pline_level--; + if (how & Z_ASYNC) { + lastwj = newjob; + jobtab[thisjob].stat |= STAT_NOSTTY; + if (l->flags & PFLAG_COPROC) + zclose(ipipe[1]); + if (how & Z_DISOWN) { + deletejob(jobtab + thisjob); + thisjob = -1; + } + else + spawnjob(); + child_unblock(); + return 0; + } else { + if (newjob != lastwj) { + Job jn = jobtab + newjob; + + if (newjob == list_pipe_job && list_pipe_child) + _exit(0); + + lastwj = thisjob = newjob; + + if (list_pipe) + jn->stat |= STAT_NOPRINT; + + if (nowait) { + if(!pline_level) { + struct process *pn, *qn; + + curjob = newjob; + addproc(list_pipe_pid, list_pipe_text); + + for (pn = jobtab[jn->other].procs; pn; pn = pn->next) + if (WIFSTOPPED(pn->status)) + break; + + if (pn) { + for (qn = jn->procs; qn->next; qn = qn->next); + qn->status = pn->status; + } + + jn->stat &= ~(STAT_DONE | STAT_NOPRINT); + jn->stat |= STAT_STOPPED | STAT_CHANGED; + printjob(jn, !!isset(LONGLISTJOBS), 1); + } + else + deletejob(jn); + } + + for (; !nowait;) { + if (list_pipe_child) { + jn->stat |= STAT_NOPRINT; + makerunning(jn); + } + if (!(jn->stat & STAT_LOCKED)) + waitjobs(); + + if (list_pipe_child && + jn->stat & STAT_DONE && + lastval2 & 0200) + killpg(mypgrp, lastval2 & ~0200); + if ((list_pipe || last1) && !list_pipe_child && + jn->stat & STAT_STOPPED) { + pid_t pid; + int synch[2]; + + pipe(synch); + + if ((pid = fork()) == -1) { + trashzle(); + close(synch[0]); + close(synch[1]); + putc('\n', stderr); + fprintf(stderr, "zsh: job can't be suspended\n"); + fflush(stderr); + makerunning(jn); + killjb(jn, SIGCONT); + thisjob = newjob; + } + else if (pid) { + char dummy; + + list_pipe_pid = pid; + nowait = errflag = 1; + breaks = loops; + close(synch[1]); + read(synch[0], &dummy, 1); + close(synch[0]); + jobtab[list_pipe_job].other = newjob; + jobtab[list_pipe_job].stat |= STAT_SUPERJOB; + jn->stat |= STAT_SUBJOB | STAT_NOPRINT; + jn->other = pid; + killpg(jobtab[list_pipe_job].gleader, SIGSTOP); + break; + } + else { + close(synch[0]); + entersubsh(Z_ASYNC, 0, 0); + setpgrp(0L, mypgrp = jobtab[list_pipe_job].gleader); + close(synch[1]); + kill(getpid(), SIGSTOP); + list_pipe = 0; + list_pipe_child = 1; + break; + } + } + else if (subsh && jn->stat & STAT_STOPPED) + thisjob = newjob; + else + break; + } + child_unblock(); + + if (list_pipe && (lastval & 0200) && pj >= 0 && + (!(jn->stat & STAT_INUSE) || (jn->stat & STAT_DONE))) { + jn = jobtab + pj; + jn->stat |= STAT_NOPRINT; + killjb(jobtab + pj, lastval & ~0200); + } + if (list_pipe_child || (list_pipe && (jn->stat & STAT_DONE))) + deletejob(jn); + thisjob = pj; + + } + if (l->flags & PFLAG_NOT) + lastval = !lastval; + } + if (!pline_level) + simple_pline = old_simple_pline; + return lastval; +} + +static int subsh_close = -1; + +/* execute pipeline. This function assumes the `pline' is not NULL. */ + +/**/ +static void +execpline2(Pline pline, int how, int input, int output, int last1) +{ + pid_t pid; + int pipes[2]; + int oldlineno; + + if (breaks || retflag) + return; + + oldlineno = lineno; + lineno = pline->left->lineno; + + if (pline_level == 1) + strcpy(list_pipe_text, getjobtext((void *) pline->left)); + if (pline->type == END) { + execcmd(pline->left, input, output, how, last1 ? 1 : 2); + pline->left = NULL; + } else { + int old_list_pipe = list_pipe; + + mpipe(pipes); + + /* if we are doing "foo | bar" where foo is a current * + * shell command, do foo in a subshell and do the * + * rest of the pipeline in the current shell. */ + if (pline->left->type >= CURSH && (how & Z_SYNC)) { + int synch[2]; + + pipe(synch); + if ((pid = fork()) == -1) { + close(synch[0]); + close(synch[1]); + zerr("fork failed: %e", NULL, errno); + } else if (pid) { + char dummy, *text; + + text = getjobtext((void *) pline->left); + addproc(pid, text); + close(synch[1]); + read(synch[0], &dummy, 1); + close(synch[0]); + } else { + zclose(pipes[0]); + close(synch[0]); + entersubsh(how, 2, 0); + close(synch[1]); + execcmd(pline->left, input, pipes[1], how, 0); + _exit(lastval); + } + } else { + /* otherwise just do the pipeline normally. */ + subsh_close = pipes[0]; + execcmd(pline->left, input, pipes[1], how, 0); + } + pline->left = NULL; + zclose(pipes[1]); + if (pline->right) { + /* if another execpline() is invoked because the command is * + * a list it must know that we're already in a pipeline */ + list_pipe = 1; + execpline2(pline->right, how, pipes[0], output, last1); + list_pipe = old_list_pipe; + zclose(pipes[0]); + subsh_close = -1; + } + } + + lineno = oldlineno; +} + +/* make the argv array */ + +/**/ +static char ** +makecline(LinkList list) +{ + LinkNode node; + char **argv, **ptr; + + /* A bigger argv is necessary for executing scripts */ + ptr = + argv = 2 + (char **) ncalloc((countlinknodes(list) + 4) * sizeof(char *)); + if (isset(XTRACE)) { + if (!doneps4) + fprintf(stderr, "%s", (prompt4) ? prompt4 : ""); + + for (node = firstnode(list); node; incnode(node)) { + *ptr++ = (char *)getdata(node); + zputs(getdata(node), stderr); + if (nextnode(node)) + fputc(' ', stderr); + } + fputc('\n', stderr); + fflush(stderr); + } else { + for (node = firstnode(list); node; incnode(node)) + *ptr++ = (char *)getdata(node); + } + *ptr = NULL; + return (argv); +} + +/**/ +void +untokenize(char *s) +{ + for (; *s; s++) + if (itok(*s)) + if (*s == Nularg) + chuck(s--); + else + *s = ztokens[*s - Pound]; +} + +/* Open a file for writing redicection */ + +/**/ +static int +clobber_open(struct redir *f) +{ + struct stat buf; + int fd, oerrno; + + /* If clobbering, just open. */ + if (isset(CLOBBER) || IS_CLOBBER_REDIR(f->type)) + return open(unmeta(f->name), + O_WRONLY | O_CREAT | O_TRUNC | O_NOCTTY, 0666); + + /* If not clobbering, attempt to create file exclusively. */ + if ((fd = open(unmeta(f->name), + O_WRONLY | O_CREAT | O_EXCL | O_NOCTTY, 0666)) >= 0) + return fd; + + /* If that fails, we are still allowed to open non-regular files. * + * Try opening, and if it's a regular file then close it again * + * because we weren't supposed to open it. */ + oerrno = errno; + if ((fd = open(unmeta(f->name), O_WRONLY | O_NOCTTY)) != -1) { + if(!fstat(fd, &buf) && !S_ISREG(buf.st_mode)) + return fd; + close(fd); + } + errno = oerrno; + return -1; +} + +/* size of buffer for tee and cat processes */ +#define TCBUFSIZE 4092 + +/* close an multio (success) */ + +/**/ +static void +closemn(struct multio **mfds, int fd) +{ + struct multio *mn = mfds[fd]; + char buf[TCBUFSIZE]; + int len, i; + + if (fd < 0 || !mfds[fd] || mfds[fd]->ct < 2) + return; + if (zfork()) { + for (i = 0; i < mn->ct; i++) + zclose(mn->fds[i]); + zclose(mn->pipe); + mn->ct = 1; + mn->fds[0] = fd; + return; + } + /* pid == 0 */ + closeallelse(mn); + if (mn->rflag) { + /* tee process */ + while ((len = read(mn->pipe, buf, TCBUFSIZE)) > 0) + for (i = 0; i < mn->ct; i++) + write(mn->fds[i], buf, len); + } else { + /* cat process */ + for (i = 0; i < mn->ct; i++) + while ((len = read(mn->fds[i], buf, TCBUFSIZE)) > 0) + write(mn->pipe, buf, len); + } + _exit(0); +} + +/* close all the mnodes (failure) */ + +/**/ +static void +closemnodes(struct multio **mfds) +{ + int i, j; + + for (i = 0; i < 10; i++) + if (mfds[i]) { + for (j = 0; j < mfds[i]->ct; j++) + zclose(mfds[i]->fds[j]); + mfds[i] = NULL; + } +} + +/**/ +static void +closeallelse(struct multio *mn) +{ + int i, j; + + for (i = 0; i < OPEN_MAX; i++) + if (mn->pipe != i) { + for (j = 0; j < mn->ct; j++) + if (mn->fds[j] == i) + break; + if (j == mn->ct) + zclose(i); + } +} + +/* A multio is a list of fds associated with a certain fd. * + * Thus if you do "foo >bar >ble", the multio for fd 1 will have * + * two fds, the result of open("bar",...), and the result of * + * open("ble",....). */ + +/* Add a fd to an multio. fd1 must be < 10, and may be in any state. * + * fd2 must be open, and is `consumed' by this function. Note that * + * fd1 == fd2 is possible, and indicates that fd1 was really closed. * + * We effectively do `fd2 = movefd(fd2)' at the beginning of this * + * function, but in most cases we can avoid an extra dup by delaying * + * the movefd: we only >need< to move it if we're actually doing a * + * multiple redirection. */ + +/**/ +static void +addfd(int forked, int *save, struct multio **mfds, int fd1, int fd2, int rflag) +{ + int pipes[2]; + + if (!mfds[fd1] || unset(MULTIOS)) { + if(!mfds[fd1]) { /* starting a new multio */ + mfds[fd1] = (struct multio *) alloc(sizeof(struct multio)); + if (!forked && save[fd1] == -2) + save[fd1] = (fd1 == fd2) ? -1 : movefd(fd1); + } + redup(fd2, fd1); + mfds[fd1]->ct = 1; + mfds[fd1]->fds[0] = fd1; + mfds[fd1]->rflag = rflag; + } else { + if (mfds[fd1]->rflag != rflag) { + zerr("file mode mismatch on fd %d", NULL, fd1); + return; + } + if (mfds[fd1]->ct == 1) { /* split the stream */ + mfds[fd1]->fds[0] = movefd(fd1); + mfds[fd1]->fds[1] = movefd(fd2); + mpipe(pipes); + mfds[fd1]->pipe = pipes[1 - rflag]; + redup(pipes[rflag], fd1); + mfds[fd1]->ct = 2; + } else { /* add another fd to an already split stream */ + if(!(mfds[fd1]->ct % MULTIOUNIT)) { + int new = sizeof(struct multio) + sizeof(int) * mfds[fd1]->ct; + int old = new - sizeof(int) * MULTIOUNIT; + mfds[fd1] = hrealloc((char *)mfds[fd1], old, new); + } + mfds[fd1]->fds[mfds[fd1]->ct++] = movefd(fd2); + } + } + if (subsh_close >= 0 && !fdtable[subsh_close]) + subsh_close = -1; +} + +/**/ +static void +addvars(LinkList l, int export) +{ + Varasg v; + LinkList vl; + int xtr; + char **arr, **ptr; + + xtr = isset(XTRACE); + if (xtr && nonempty(l)) { + fprintf(stderr, "%s", prompt4 ? prompt4 : ""); + doneps4 = 1; + } + + while (nonempty(l)) { + v = (Varasg) ugetnode(l); + singsub(&v->name); + if (errflag) + return; + untokenize(v->name); + if (xtr) + fprintf(stderr, "%s=", v->name); + if (v->type == PM_SCALAR) { + vl = newlinklist(); + addlinknode(vl, v->str); + } else + vl = v->arr; + prefork(vl, v->type == PM_SCALAR ? 7 : 3); + if (errflag) + return; + if (isset(GLOBASSIGN) || v->type != PM_SCALAR) + globlist(vl); + if (errflag) + return; + if (v->type == PM_SCALAR && (empty(vl) || !nextnode(firstnode(vl)))) { + Param pm; + char *val; + int allexp; + + if (empty(vl)) + val = ztrdup(""); + else { + untokenize(peekfirst(vl)); + val = ztrdup(ugetnode(vl)); + } + if (xtr) + fprintf(stderr, "%s ", val); + if (export) { + if (export < 0) { + /* We are going to fork so do not bother freeing this */ + pm = (Param) paramtab->removenode(paramtab, v->name); + if (isset(RESTRICTED) && (pm->flags & PM_RESTRICTED)) { + zerr("%s: restricted", pm->nam, 0); + zsfree(val); + return; + } + } + allexp = opts[ALLEXPORT]; + opts[ALLEXPORT] = 1; + pm = setsparam(v->name, val); + opts[ALLEXPORT] = allexp; + } else + pm = setsparam(v->name, val); + if (errflag) + return; + continue; + } + ptr = arr = (char **) zalloc(sizeof(char **) * (countlinknodes(vl) + 1)); + + while (nonempty(vl)) + *ptr++ = ztrdup((char *) ugetnode(vl)); + + *ptr = NULL; + if (xtr) { + fprintf(stderr, "( "); + for (ptr = arr; *ptr; ptr++) + fprintf(stderr, "%s ", *ptr); + fprintf(stderr, ") "); + } + setaparam(v->name, arr); + if (errflag) + return; + } +} + +/**/ +static void +execcmd(Cmd cmd, int input, int output, int how, int last1) +{ + HashNode hn = NULL; + LinkNode node; + Redir fn; + struct multio *mfds[10]; + char *text; + int save[10]; + int fil, dfil, is_cursh, type, i; + int nullexec = 0, assign = 0, forked = 0; + int is_shfunc = 0, is_builtin = 0, is_exec = 0; + /* Various flags to the command. */ + int cflags = 0, checked = 0; + + doneps4 = 0; + args = cmd->args; + type = cmd->type; + + for (i = 0; i < 10; i++) { + save[i] = -2; + mfds[i] = NULL; + } + + /* If the command begins with `%', then assume it is a * + * reference to a job in the job table. */ + if (type == SIMPLE && nonempty(args) && + *(char *)peekfirst(args) == '%') { + pushnode(args, dupstring((how & Z_DISOWN) + ? "disown" : (how & Z_ASYNC) ? "bg" : "fg")); + how = Z_SYNC; + } + + /* If AUTORESUME is set, the command is SIMPLE, and doesn't have * + * any redirections, then check if it matches as a prefix of a * + * job currently in the job table. If it does, then we treat it * + * as a command to resume this job. */ + if (isset(AUTORESUME) && type == SIMPLE && (how & Z_SYNC) && + nonempty(args) && empty(cmd->redir) && !input && + !nextnode(firstnode(args))) { + if (unset(NOTIFY)) + scanjobs(); + if (findjobnam(peekfirst(args)) != -1) + pushnode(args, dupstring("fg")); + } + + /* Check if it's a builtin needing automatic MAGIC_EQUALS_SUBST * + * handling. Things like typeset need this. We can't detect the * + * command if it contains some tokens (e.g. x=ex; ${x}port), so this * + * only works in simple cases. has_token() is called to make sure * + * this really is a simple case. */ + if (type == SIMPLE) { + while (nonempty(args)) { + char *cmdarg = (char *) peekfirst(args); + checked = !has_token(cmdarg); + if (!checked) + break; + if (!(cflags & (BINF_BUILTIN | BINF_COMMAND)) && + (hn = shfunctab->getnode(shfunctab, cmdarg))) { + is_shfunc = 1; + break; + } + if (!(hn = builtintab->getnode(builtintab, cmdarg))) { + checked = !(cflags & BINF_BUILTIN); + break; + } + if (!(hn->flags & BINF_PREFIX)) { + is_builtin = 1; +#ifdef DYNAMIC + /* autoload the builtin if necessary */ + if (!((Builtin) hn)->handlerfunc) { + load_module(((Builtin) hn)->optstr); + hn = builtintab->getnode(builtintab, cmdarg); + } +#endif + assign = (hn->flags & BINF_MAGICEQUALS); + break; + } + cflags &= ~BINF_BUILTIN & ~BINF_COMMAND; + cflags |= hn->flags; + uremnode(args, firstnode(args)); + hn = NULL; + checked = 0; + if ((cflags & BINF_COMMAND) && unset(POSIXBUILTINS)) + break; + } + } + + /* Do prefork substitutions */ + prefork(args, assign ? 2 : isset(MAGICEQUALSUBST)); + + if (type == SIMPLE) { + int unglobbed = 0; + + for (;;) { + char *cmdarg; + + if (!(cflags & BINF_NOGLOB)) + while (!checked && !errflag && nonempty(args) && + has_token((char *) peekfirst(args))) + glob(args, firstnode(args)); + else if (!unglobbed) { + for (node = firstnode(args); node; incnode(node)) + untokenize((char *) getdata(node)); + unglobbed = 1; + } + + /* Current shell should not fork unless the * + * exec occurs at the end of a pipeline. */ + if ((cflags & BINF_EXEC) && last1 == 2) + cmd->flags |= CFLAG_EXEC; + + /* Empty command */ + if (empty(args)) { + if (nonempty(cmd->redir)) { + if (cmd->flags & CFLAG_EXEC) { + /* Was this "exec < foobar"? */ + nullexec = 1; + break; + } else if (!nullcmd || !*nullcmd || + (cflags & BINF_PREFIX)) { + zerr("redirection with no command", NULL, 0); + errflag = lastval = 1; + return; + } else if (readnullcmd && *readnullcmd && + ((Redir) peekfirst(cmd->redir))->type == READ && + !nextnode(firstnode(cmd->redir))) { + addlinknode(args, dupstring(readnullcmd)); + } else + addlinknode(args, dupstring(nullcmd)); + } else if ((cflags & BINF_PREFIX) && (cflags & BINF_COMMAND)) { + lastval = 0; + return; + } else { + cmdoutval = 0; + addvars(cmd->vars, 0); + if (errflag) + lastval = errflag; + else + lastval = cmdoutval; + if (isset(XTRACE)) { + fputc('\n', stderr); + fflush(stderr); + } + return; + } + } else if (isset(RESTRICTED) && (cflags & BINF_EXEC) && + (cmd->flags & CFLAG_EXEC)) { + zerrnam("exec", "%s: restricted", (char *) getdata(firstnode(args)), 0); + lastval = 1; + return; + } + + if (errflag || checked || + (unset(POSIXBUILTINS) && (cflags & BINF_COMMAND))) + break; + + cmdarg = (char *) peekfirst(args); + if (!(cflags & (BINF_BUILTIN | BINF_COMMAND)) && + (hn = shfunctab->getnode(shfunctab, cmdarg))) { + is_shfunc = 1; + break; + } + if (!(hn = builtintab->getnode(builtintab, cmdarg))) { + if (cflags & BINF_BUILTIN) { + zerr("no such builtin: %s", cmdarg, 0); + errflag = lastval = 1; + return; + } + break; + } + if (!(hn->flags & BINF_PREFIX)) { + is_builtin = 1; +#ifdef DYNAMIC + /* autoload the builtin if necessary */ + if (!((Builtin) hn)->handlerfunc) + load_module(((Builtin) hn)->optstr); +#endif + break; + } + cflags &= ~BINF_BUILTIN & ~BINF_COMMAND; + cflags |= hn->flags; + uremnode(args, firstnode(args)); + hn = NULL; + } + } + + if (errflag) { + lastval = 1; + return; + } + + /* Get the text associated with this command. */ + if (jobbing || (how & Z_TIMED)) + text = getjobtext((void *) cmd); + else + text = NULL; + + /* Set up special parameter $_ */ + zsfree(underscore); + if (nonempty(args) + && (underscore = ztrdup((char *) getdata(lastnode(args))))) + untokenize(underscore); + else + underscore = ztrdup(""); + + /* Warn about "rm *" */ + if (type == SIMPLE && interact && unset(RMSTARSILENT) + && isset(SHINSTDIN) && nonempty(args) && nextnode(firstnode(args)) + && !strcmp(peekfirst(args), "rm")) { + LinkNode node, next; + + for (node = nextnode(firstnode(args)); node && !errflag; node = next) { + char *s = (char *) getdata(node); + int l = strlen(s); + + next = nextnode(node); + if (s[0] == Star && !s[1]) { + if (!checkrmall(pwd)) + uremnode(args, node); + } else if (l > 2 && s[l - 2] == '/' && s[l - 1] == Star) { + char t = s[l - 2]; + + s[l - 2] = 0; + if (!checkrmall(s)) + uremnode(args, node); + s[l - 2] = t; + } + } + if (!nextnode(firstnode(args))) + errflag = 1; + } + + if (errflag) { + lastval = 1; + return; + } + + if (type == SIMPLE && !nullexec) { + char *s; + char trycd = (isset(AUTOCD) && isset(SHINSTDIN) + && empty(cmd->redir) && !empty(args) + && !nextnode(firstnode(args)) + && *(char *)peekfirst(args)); + + DPUTS(empty(args), "BUG: empty(args) in exec.c"); + if (!hn) { + /* Resolve external commands */ + char *cmdarg = (char *) peekfirst(args); + + hn = cmdnamtab->getnode(cmdnamtab, cmdarg); + if (hn && trycd && !isreallycom((Cmdnam)hn)) { + cmdnamtab->removenode(cmdnamtab, cmdarg); + cmdnamtab->freenode(hn); + hn = NULL; + } + if (!hn && isset(HASHCMDS) && strcmp(cmdarg, "..")) { + for (s = cmdarg; *s && *s != '/'; s++); + if (!*s) + hn = (HashNode) hashcmd(cmdarg, pathchecked); + } + } + + /* If no command found yet, see if it * + * is a directory we should AUTOCD to. */ + if (!hn && trycd && (s = cancd(peekfirst(args)))) { + peekfirst(args) = (void *) s; + pushnode(args, dupstring("cd")); + if ((hn = builtintab->getnode(builtintab, "cd"))) + is_builtin = 1; + } + } + + /* This is nonzero if the command is a current shell procedure? */ + is_cursh = (is_builtin || is_shfunc || (type >= CURSH) || nullexec); + + /************************************************************************** + * Do we need to fork? We need to fork if: * + * 1) The command is supposed to run in the background. (or) * + * 2) There is no `exec' flag, and either: * + * a) This is a builtin or shell function with output piped somewhere. * + * b) This is an external command and we can't do a `fake exec'. * + * * + * A `fake exec' is possible if we have all the following conditions: * + * 1) last1 flag is 1. This indicates that the current shell will not * + * be needed after the current command. This is typically the case * + * when when the command is the last stage in a subshell, or is the * + * last command after the option `-c'. * + * 2) We are not trapping EXIT or ZERR. * + * 3) We don't have any files to delete. * + * * + * The condition above for a `fake exec' will also work for a current * + * shell command such as a builtin, but doesn't really buy us anything * + * (doesn't save us a process), since it is already running in the * + * current shell. * + **************************************************************************/ + + if ((how & Z_ASYNC) || (!(cmd->flags & CFLAG_EXEC) && + (((is_builtin || is_shfunc) && output) || + (!is_cursh && (last1 != 1 || sigtrapped[SIGZERR] || + sigtrapped[SIGEXIT] || havefiles()))))) { + + pid_t pid; + int synch[2]; + char dummy; + + child_block(); + pipe(synch); + + if ((pid = zfork()) == -1) { + close(synch[0]); + close(synch[1]); + return; + } if (pid) { + close(synch[1]); + read(synch[0], &dummy, 1); + close(synch[0]); +#ifdef PATH_DEV_FD + closem(2); +#endif + if (how & Z_ASYNC) { + lastpid = (long) pid; + } else if (!jobtab[thisjob].stty_in_env && nonempty(cmd->vars)) { + /* search for STTY=... */ + while (nonempty(cmd->vars)) + if (!strcmp(((Varasg) ugetnode(cmd->vars))->name, "STTY")) { + jobtab[thisjob].stty_in_env = 1; + break; + } + } + addproc(pid, text); + return; + } + /* pid == 0 */ + close(synch[0]); + entersubsh(how, type != SUBSH && !(how & Z_ASYNC) ? 2 : 1, 0); + close(synch[1]); + forked = 1; + if (sigtrapped[SIGINT] & ZSIG_IGNORED) + holdintr(); +#ifdef HAVE_NICE + /* Check if we should run background jobs at a lower priority. */ + if ((how & Z_ASYNC) && isset(BGNICE)) + nice(5); +#endif /* HAVE_NICE */ + + } else if (is_cursh) { + /* This is a current shell procedure that didn't need to fork. * + * This includes current shell procedures that are being exec'ed, * + * as well as null execs. */ + jobtab[thisjob].stat |= STAT_CURSH; + } else { + /* This is an exec (real or fake) for an external command. * + * Note that any form of exec means that the subshell is fake * + * (but we may be in a subshell already). */ + is_exec = 1; + } + + if (!(cflags & BINF_NOGLOB)) + globlist(args); + if (errflag) { + lastval = 1; + goto err; + } + + /* Add pipeline input/output to mnodes */ + if (input) + addfd(forked, save, mfds, 0, input, 0); + if (output) + addfd(forked, save, mfds, 1, output, 1); + + /* Do process substitutions */ + spawnpipes(cmd->redir); + + /* Do io redirections */ + while (nonempty(cmd->redir)) { + fn = (Redir) ugetnode(cmd->redir); + DPUTS(fn->type == HEREDOC || fn->type == HEREDOCDASH, + "BUG: unexpanded here document"); + if (fn->type == INPIPE) { + if (fn->fd2 == -1) { + closemnodes(mfds); + fixfds(save); + execerr(); + } + addfd(forked, save, mfds, fn->fd1, fn->fd2, 0); + } else if (fn->type == OUTPIPE) { + if (fn->fd2 == -1) { + closemnodes(mfds); + fixfds(save); + execerr(); + } + addfd(forked, save, mfds, fn->fd1, fn->fd2, 1); + } else { + if (fn->type != HERESTR && xpandredir(fn, cmd->redir)) + continue; + if (errflag) { + closemnodes(mfds); + fixfds(save); + execerr(); + } + if (isset(RESTRICTED) && IS_WRITE_FILE(fn->type)) { + zerr("writing redirection not allowed in restricted mode", NULL, 0); + execerr(); + } + if (unset(EXECOPT)) + continue; + switch(fn->type) { + case HERESTR: + fil = getherestr(fn); + if (fil == -1) { + closemnodes(mfds); + fixfds(save); + if (errno != EINTR) + zerr("%e", NULL, errno); + execerr(); + } + addfd(forked, save, mfds, fn->fd1, fil, 0); + break; + case READ: + case READWRITE: + if (fn->type == READ) + fil = open(unmeta(fn->name), O_RDONLY | O_NOCTTY); + else + fil = open(unmeta(fn->name), + O_RDWR | O_CREAT | O_NOCTTY, 0666); + if (fil == -1) { + closemnodes(mfds); + fixfds(save); + if (errno != EINTR) + zerr("%e: %s", fn->name, errno); + execerr(); + } + addfd(forked, save, mfds, fn->fd1, fil, 0); + /* If this is 'exec < file', read from stdin, * + * not terminal, unless `file' is a terminal. */ + if (nullexec && fn->fd1 == 0 && isset(SHINSTDIN) && interact) + init_io(); + break; + case CLOSE: + if (!forked && fn->fd1 < 10 && save[fn->fd1] == -2) + save[fn->fd1] = movefd(fn->fd1); + closemn(mfds, fn->fd1); + zclose(fn->fd1); + break; + case MERGEIN: + case MERGEOUT: + if(fn->fd2 < 10) + closemn(mfds, fn->fd2); + fil = dup(fn->fd2); + if (fil == -1) { + char fdstr[4]; + + closemnodes(mfds); + fixfds(save); + sprintf(fdstr, "%d", fn->fd2); + zerr("%s: %e", fdstr, errno); + execerr(); + } + addfd(forked, save, mfds, fn->fd1, fil, fn->type == MERGEOUT); + break; + default: + if (IS_APPEND_REDIR(fn->type)) + fil = open(unmeta(fn->name), + (unset(CLOBBER) && !IS_CLOBBER_REDIR(fn->type)) ? + O_WRONLY | O_APPEND | O_NOCTTY : + O_WRONLY | O_APPEND | O_CREAT | O_NOCTTY, 0666); + else + fil = clobber_open(fn); + if(fil != -1 && IS_ERROR_REDIR(fn->type)) + dfil = dup(fil); + else + dfil = 0; + if (fil == -1 || dfil == -1) { + if(fil != -1) + close(fil); + closemnodes(mfds); + fixfds(save); + if (errno != EINTR) + zerr("%e: %s", fn->name, errno); + execerr(); + } + addfd(forked, save, mfds, fn->fd1, fil, 1); + if(IS_ERROR_REDIR(fn->type)) + addfd(forked, save, mfds, 2, dfil, 1); + break; + } + } + } + + /* We are done with redirection. close the mnodes, * + * spawning tee/cat processes as necessary. */ + for (i = 0; i < 10; i++) + closemn(mfds, i); + + if (nullexec) { + for (i = 0; i < 10; i++) + if (save[i] != -2) + zclose(save[i]); + /* + * Here we specifically *don't* restore the original fd's + * before returning. + */ + return; + } + + if (isset(EXECOPT) && !errflag) { + /* + * We delay the entersubsh() to here when we are exec'ing + * the current shell (including a fake exec to run a builtin then + * exit) in case there is an error return. + */ + if (is_exec) + entersubsh(how, type != SUBSH ? 2 : 1, 1); + if (type >= CURSH) { + static int (*func[]) _((Cmd)) = { + execcursh, exectime, execfuncdef, execfor, execwhile, + execrepeat, execif, execcase, execselect, execcond, + execarith, execautofn + }; + + if (last1 == 1) + cmd->flags |= CFLAG_EXEC; + lastval = (func[type - CURSH]) (cmd); + } else if (is_builtin || is_shfunc) { + LinkList restorelist = 0, removelist = 0; + /* builtin or shell function */ + + if (!forked && ((cflags & BINF_COMMAND) || + (unset(POSIXBUILTINS) && !assign) || + (isset(POSIXBUILTINS) && !is_shfunc && + !(hn->flags & BINF_PSPECIAL)))) + save_params(cmd, &restorelist, &removelist); + + if (cmd->vars) { + /* Export this if the command is a shell function, + * but not if it's a builtin. + */ + addvars(cmd->vars, is_shfunc); + if (errflag) { + restore_params(restorelist, removelist); + lastval = 1; + fixfds(save); + return; + } + } + + if (is_shfunc) { + /* It's a shell function */ +#ifdef PATH_DEV_FD + int i; + + for (i = 10; i <= max_zsh_fd; i++) + if (fdtable[i] > 1) + fdtable[i]++; +#endif + if (subsh_close >= 0) + zclose(subsh_close); + subsh_close = -1; + execshfunc(cmd, (Shfunc) hn); +#ifdef PATH_DEV_FD + for (i = 10; i <= max_zsh_fd; i++) + if (fdtable[i] > 1) + if (--(fdtable[i]) <= 2) + zclose(i); +#endif + } else { + /* It's a builtin */ + if (forked) + closem(1); + lastval = execbuiltin(args, (Builtin) hn); +#ifdef PATH_DEV_FD + closem(2); +#endif + if (isset(PRINTEXITVALUE) && isset(SHINSTDIN) && lastval && !subsh) { + fprintf(stderr, "zsh: exit %ld\n", (long)lastval); + } + fflush(stdout); + if (save[1] == -2) { + if (ferror(stdout)) { + zerr("write error: %e", NULL, errno); + clearerr(stdout); + errflag = 0; + } + } else + clearerr(stdout); + } + + if (cmd->flags & CFLAG_EXEC) { + if (subsh) + _exit(lastval); + + /* If we are exec'ing a command, and we are not in a subshell, * + * then check if we should save the history file. */ + if (isset(RCS) && interact && !nohistsave) + savehistfile(getsparam("HISTFILE"), 1, isset(APPENDHISTORY) ? 3 : 0); + exit(lastval); + } + + restore_params(restorelist, removelist); + + } else { + if (cmd->flags & CFLAG_EXEC) { + setiparam("SHLVL", --shlvl); + /* If we are exec'ing a command, and we are not * + * in a subshell, then save the history file. */ + if (!subsh && isset(RCS) && interact && !nohistsave) + savehistfile(getsparam("HISTFILE"), 1, isset(APPENDHISTORY) ? 3 : 0); + } + if (type == SIMPLE) { + if (cmd->vars) { + addvars(cmd->vars, -1); + if (errflag) + _exit(1); + } + closem(1); + if (coprocin) + zclose(coprocin); + if (coprocout) + zclose(coprocout); +#ifdef HAVE_GETRLIMIT + if (!forked) + setlimits(NULL); +#endif + execute((Cmdnam) hn, cflags & BINF_DASH); + } else { /* ( ... ) */ + DPUTS(cmd->vars && nonempty(cmd->vars), + "BUG: assigment before complex command"); + list_pipe = 0; + if (subsh_close >= 0) + zclose(subsh_close); + subsh_close = -1; + /* If we're forked (and we should be), no need to return */ + DPUTS(last1 != 1 && !forked, "BUG: not exiting?"); + execlist(cmd->u.list, 0, 1); + } + } + } + + err: + if (forked) + _exit(lastval); + fixfds(save); +} + +/* Arrange to have variables restored. */ + +/**/ +static void +save_params(Cmd cmd, LinkList *restore_p, LinkList *remove_p) +{ + Param pm; + LinkNode node; + char *s; + + MUSTUSEHEAP("save_params()"); + + *restore_p = newlinklist(); + *remove_p = newlinklist(); + + for (node = firstnode(cmd->vars); node; incnode(node)) { + s = ((Varasg) getdata(node))->name; + if ((pm = (Param) paramtab->getnode(paramtab, s))) { + if (!(pm->flags & PM_SPECIAL)) { + paramtab->removenode(paramtab, s); + } else if (!(pm->flags & PM_READONLY) && + (unset(RESTRICTED) || !(pm->flags & PM_RESTRICTED))) { + Param tpm = (Param) alloc(sizeof *tpm); + + tpm->nam = s; + tpm->flags = pm->flags; + switch (PM_TYPE(pm->flags)) { + case PM_SCALAR: + tpm->u.str = ztrdup(pm->gets.cfn(pm)); + break; + case PM_INTEGER: + tpm->u.val = pm->gets.ifn(pm); + break; + case PM_ARRAY: + PERMALLOC { + tpm->u.arr = arrdup(pm->gets.afn(pm)); + } LASTALLOC; + break; + } + pm = tpm; + } + addlinknode(*remove_p, s); + addlinknode(*restore_p, pm); + } else { + addlinknode(*remove_p, s); + } + } +} + +/* Restore saved parameters after executing a shfunc or builtin */ + +/**/ +static void +restore_params(LinkList restorelist, LinkList removelist) +{ + Param pm; + char *s; + + if (removelist) { + /* remove temporary parameters */ + while ((s = (char *) ugetnode(removelist))) { + if ((pm = (Param) paramtab->getnode(paramtab, s)) && + !(pm->flags & PM_SPECIAL)) { + pm->flags &= ~PM_READONLY; + unsetparam_pm(pm, 0, 0); + } + } + } + + if (restorelist) { + /* restore saved parameters */ + while ((pm = (Param) ugetnode(restorelist))) { + if (pm->flags & PM_SPECIAL) { + Param tpm = (Param) paramtab->getnode(paramtab, pm->nam); + + DPUTS(!tpm || PM_TYPE(pm->flags) != PM_TYPE(tpm->flags) || + !(pm->flags & PM_SPECIAL), + "BUG: in restoring special parameters"); + tpm->flags = pm->flags; + switch (PM_TYPE(pm->flags)) { + case PM_SCALAR: + tpm->sets.cfn(tpm, pm->u.str); + break; + case PM_INTEGER: + tpm->sets.ifn(tpm, pm->u.val); + break; + case PM_ARRAY: + tpm->sets.afn(tpm, pm->u.arr); + break; + } + } else + paramtab->addnode(paramtab, pm->nam, pm); + if (pm->flags & PM_EXPORTED) + pm->env = addenv(pm->nam, getsparam(pm->nam)); + } + } +} + +/* restore fds after redirecting a builtin */ + +/**/ +static void +fixfds(int *save) +{ + int old_errno = errno; + int i; + + for (i = 0; i != 10; i++) + if (save[i] != -2) + redup(save[i], i); + errno = old_errno; +} + +/**/ +static void +entersubsh(int how, int cl, int fake) +{ + int sig; + + if (cl != 2) + for (sig = 0; sig < VSIGCOUNT; sig++) + if (!(sigtrapped[sig] & ZSIG_FUNC)) + unsettrap(sig); + if (unset(MONITOR)) { + if (how & Z_ASYNC) { + settrap(SIGINT, NULL); + settrap(SIGQUIT, NULL); + if (isatty(0)) { + close(0); + if (open("/dev/null", O_RDWR | O_NOCTTY)) { + zerr("can't open /dev/null: %e", NULL, errno); + _exit(1); + } + } + } + } else if (thisjob != -1 && cl) { + if (jobtab[list_pipe_job].gleader && (list_pipe || list_pipe_child)) { + if (kill(jobtab[list_pipe_job].gleader, 0) == -1 || + setpgrp(0L, jobtab[list_pipe_job].gleader) == -1) { + jobtab[list_pipe_job].gleader = + jobtab[thisjob].gleader = mypgrp; + setpgrp(0L, mypgrp); + + if (how & Z_SYNC) + attachtty(jobtab[thisjob].gleader); + } + } + else if (!jobtab[thisjob].gleader || + (setpgrp(0L, jobtab[thisjob].gleader) == -1)) { + jobtab[thisjob].gleader = getpid(); + if (list_pipe_job != thisjob && + !jobtab[list_pipe_job].gleader) + jobtab[list_pipe_job].gleader = jobtab[thisjob].gleader; + setpgrp(0L, jobtab[thisjob].gleader); + if (how & Z_SYNC) + attachtty(jobtab[thisjob].gleader); + } + } + if (!fake) + subsh = 1; + if (SHTTY != -1) { + zclose(SHTTY); + SHTTY = -1; + } + if (isset(MONITOR)) { + signal_default(SIGTTOU); + signal_default(SIGTTIN); + signal_default(SIGTSTP); + } + if (interact) { + signal_default(SIGTERM); + if (!(sigtrapped[SIGINT] & ZSIG_IGNORED)) + signal_default(SIGINT); + } + if (!(sigtrapped[SIGQUIT] & ZSIG_IGNORED)) + signal_default(SIGQUIT); + opts[MONITOR] = opts[USEZLE] = 0; + zleactive = 0; + if (cl) + clearjobtab(); + times(&shtms); +} + +/* close internal shell fds */ + +/**/ +void +closem(int how) +{ + int i; + + for (i = 10; i <= max_zsh_fd; i++) + if (fdtable[i] && (!how || fdtable[i] == how)) + zclose(i); +} + +/* convert here document into a here string */ + +/**/ +char * +gethere(char *str, int typ) +{ + char *buf; + int bsiz, qt = 0, strip = 0; + char *s, *t, *bptr, c; + + for (s = str; *s; s++) + if (INULL(*s)) { + *s = Nularg; + qt = 1; + } + untokenize(str); + if (typ == HEREDOCDASH) { + strip = 1; + while (*str == '\t') + str++; + } + bptr = buf = zalloc(bsiz = 256); + for (;;) { + t = bptr; + + while ((c = hgetc()) == '\t' && strip) + ; + for (;;) { + if (bptr == buf + bsiz) { + buf = realloc(buf, 2 * bsiz); + t = buf + bsiz - (bptr - t); + bptr = buf + bsiz; + bsiz *= 2; + } + if (lexstop || c == '\n') + break; + *bptr++ = c; + c = hgetc(); + } + *bptr = '\0'; + if (!strcmp(t, str)) + break; + if (lexstop) { + t = bptr; + break; + } + *bptr++ = '\n'; + } + if (t > buf && t[-1] == '\n') + t--; + *t = '\0'; + if (!qt) + parsestr(buf); + s = dupstring(buf); + zfree(buf, bsiz); + return s; +} + +/* open here string fd */ + +/**/ +static int +getherestr(struct redir *fn) +{ + char *s, *t; + int fd, len; + + t = fn->name; + singsub(&t); + untokenize(t); + unmetafy(t, &len); + t[len++] = '\n'; + s = gettempname(); + if (!s || (fd = open(s, O_CREAT|O_WRONLY|O_EXCL|O_NOCTTY, 0600)) == -1) + return -1; + write(fd, t, len); + close(fd); + fd = open(s, O_RDONLY | O_NOCTTY); + unlink(s); + return fd; +} + +/* $(...) */ + +/**/ +LinkList +getoutput(char *cmd, int qt) +{ + List list; + int pipes[2]; + pid_t pid; + Cmd c; + Redir r; + + if (!(list = parse_string(cmd))) + return NULL; + if (list != &dummy_list && !list->right && !list->left->flags && + list->left->type == END && list->left->left->type == END && + (c = list->left->left->left)->type == SIMPLE && empty(c->args) && + empty(c->vars) && nonempty(c->redir) && + !nextnode(firstnode(c->redir)) && + (r = (Redir) getdata(firstnode(c->redir)))->fd1 == 0 && + r->type == READ) { + /* $(< word) */ + int stream; + char *s = r->name; + + singsub(&s); + if (errflag) + return NULL; + untokenize(s); + if ((stream = open(unmeta(s), O_RDONLY | O_NOCTTY)) == -1) { + zerr("%e: %s", s, errno); + return NULL; + } + return readoutput(stream, qt); + } + + mpipe(pipes); + child_block(); + cmdoutval = 0; + if ((cmdoutpid = pid = zfork()) == -1) { + /* fork error */ + zclose(pipes[0]); + zclose(pipes[1]); + errflag = 1; + cmdoutpid = 0; + child_unblock(); + return NULL; + } else if (pid) { + LinkList retval; + + zclose(pipes[1]); + retval = readoutput(pipes[0], qt); + fdtable[pipes[0]] = 0; + child_suspend(0); /* unblocks */ + lastval = cmdoutval; + return retval; + } + + /* pid == 0 */ + child_unblock(); + zclose(pipes[0]); + redup(pipes[1], 1); + opts[MONITOR] = 0; + entersubsh(Z_SYNC, 1, 0); + execlist(list, 0, 1); + close(1); + _exit(lastval); + zerr("exit returned in child!!", NULL, 0); + kill(getpid(), SIGKILL); + return NULL; +} + +/* read output of command substitution */ + +/**/ +static LinkList +readoutput(int in, int qt) +{ + LinkList ret; + char *buf, *ptr; + int bsiz, c, cnt = 0; + FILE *fin; + + fin = fdopen(in, "r"); + ret = newlinklist(); + ptr = buf = (char *) ncalloc(bsiz = 64); + while ((c = fgetc(fin)) != EOF || errno == EINTR) { + if (c == EOF) { + errno = 0; + clearerr(fin); + continue; + } + if (imeta(c)) { + *ptr++ = Meta; + c ^= 32; + cnt++; + } + if (++cnt >= bsiz) { + char *pp = (char *) ncalloc(bsiz *= 2); + + memcpy(pp, buf, cnt - 1); + ptr = (buf = pp) + cnt - 1; + } + *ptr++ = c; + } + fclose(fin); + while (cnt && ptr[-1] == '\n') + ptr--, cnt--; + *ptr = '\0'; + if (qt) { + if (!cnt) { + *ptr++ = Nularg; + *ptr = '\0'; + } + addlinknode(ret, buf); + } else { + char **words = spacesplit(buf, 0); + + while (*words) { + if (isset(GLOBSUBST)) + tokenize(*words); + addlinknode(ret, *words++); + } + } + return ret; +} + +/**/ +static List +parsecmd(char *cmd) +{ + char *str; + List list; + + for (str = cmd + 2; *str && *str != Outpar; str++); + if (!*str || cmd[1] != Inpar) { + zerr("oops.", NULL, 0); + return NULL; + } + *str = '\0'; + if (str[1] || !(list = parse_string(cmd + 2))) { + zerr("parse error in process substitution", NULL, 0); + return NULL; + } + return list; +} + +/* =(...) */ + +/**/ +char * +getoutputfile(char *cmd) +{ + pid_t pid; + char *nam; + List list; + int fd; + + if (thisjob == -1) + return NULL; + if (!(list = parsecmd(cmd))) + return NULL; + if (!(nam = gettempname())) + return NULL; + + nam = ztrdup(nam); + PERMALLOC { + if (!jobtab[thisjob].filelist) + jobtab[thisjob].filelist = newlinklist(); + addlinknode(jobtab[thisjob].filelist, nam); + } LASTALLOC; + child_block(); + fd = open(nam, O_WRONLY | O_CREAT | O_EXCL | O_NOCTTY, 0600); + + if (fd < 0 || (cmdoutpid = pid = zfork()) == -1) { + /* fork or open error */ + child_unblock(); + return nam; + } else if (pid) { + int os; + + close(fd); + os = jobtab[thisjob].stat; + waitforpid(pid); + cmdoutval = 0; + jobtab[thisjob].stat = os; + return nam; + } + + /* pid == 0 */ + redup(fd, 1); + opts[MONITOR] = 0; + entersubsh(Z_SYNC, 1, 0); + execlist(list, 0, 1); + close(1); + _exit(lastval); + zerr("exit returned in child!!", NULL, 0); + kill(getpid(), SIGKILL); + return NULL; +} + +#if !defined(PATH_DEV_FD) && defined(HAVE_FIFOS) +/* get a temporary named pipe */ + +static char * +namedpipe(void) +{ + char *tnam = gettempname(); + +# ifdef HAVE_MKFIFO + if (mkfifo(tnam, 0600) < 0) +# else + if (mknod(tnam, 0010600, 0) < 0) +# endif + return NULL; + return tnam; +} +#endif /* ! PATH_DEV_FD && HAVE_FIFOS */ + +/* <(...) or >(...) */ + +/**/ +char * +getproc(char *cmd) +{ +#if !defined(HAVE_FIFOS) && !defined(PATH_DEV_FD) + zerr("doesn't look like your system supports FIFOs.", NULL, 0); + return NULL; +#else + List list; + int out = *cmd == Inang; + char *pnam; +#ifndef PATH_DEV_FD + int fd; +#else + int pipes[2]; +#endif + + if (thisjob == -1) + return NULL; +#ifndef PATH_DEV_FD + if (!(pnam = namedpipe())) + return NULL; +#else + pnam = ncalloc(strlen(PATH_DEV_FD) + 6); +#endif + if (!(list = parsecmd(cmd))) + return NULL; +#ifndef PATH_DEV_FD + PERMALLOC { + if (!jobtab[thisjob].filelist) + jobtab[thisjob].filelist = newlinklist(); + addlinknode(jobtab[thisjob].filelist, ztrdup(pnam)); + } LASTALLOC; + if (zfork()) { +#else + mpipe(pipes); + if (zfork()) { + sprintf(pnam, "%s/%d", PATH_DEV_FD, pipes[!out]); + zclose(pipes[out]); + fdtable[pipes[!out]] = 2; +#endif + return pnam; + } +#ifndef PATH_DEV_FD + closem(0); + fd = open(pnam, out ? O_WRONLY | O_NOCTTY : O_RDONLY | O_NOCTTY); + if (fd == -1) { + zerr("can't open %s: %e", pnam, errno); + _exit(1); + } + entersubsh(Z_ASYNC, 1, 0); + redup(fd, out); +#else + entersubsh(Z_ASYNC, 1, 0); + redup(pipes[out], out); + closem(0); /* this closes pipes[!out] as well */ +#endif + execlist(list, 0, 1); + zclose(out); + _exit(lastval); + return NULL; +#endif /* HAVE_FIFOS and PATH_DEV_FD not defined */ +} + +/* > >(...) or < <(...) (does not use named pipes) */ + +/**/ +static int +getpipe(char *cmd) +{ + List list; + int pipes[2], out = *cmd == Inang; + + if (!(list = parsecmd(cmd))) + return -1; + mpipe(pipes); + if (zfork()) { + zclose(pipes[out]); + return pipes[!out]; + } + entersubsh(Z_ASYNC, 1, 0); + redup(pipes[out], out); + closem(0); /* this closes pipes[!out] as well */ + execlist(list, 0, 1); + _exit(lastval); + return 0; +} + +/* open pipes with fds >= 10 */ + +/**/ +static void +mpipe(int *pp) +{ + pipe(pp); + pp[0] = movefd(pp[0]); + pp[1] = movefd(pp[1]); +} + +/* Do process substitution with redirection */ + +/**/ +static void +spawnpipes(LinkList l) +{ + LinkNode n; + Redir f; + char *str; + + n = firstnode(l); + for (; n; incnode(n)) { + f = (Redir) getdata(n); + if (f->type == OUTPIPE || f->type == INPIPE) { + str = f->name; + f->fd2 = getpipe(str); + } + } +} + +/* evaluate a [[ ... ]] */ + +/**/ +static int +execcond(Cmd cmd) +{ + return !evalcond(cmd->u.cond); +} + +/* evaluate a ((...)) arithmetic command */ + +/**/ +static int +execarith(Cmd cmd) +{ + char *e; + long val = 0; + + while ((e = (char *) ugetnode(cmd->args))) + val = matheval(e); + errflag = 0; + return !val; +} + +/* perform time ... command */ + +/**/ +static int +exectime(Cmd cmd) +{ + int jb; + + jb = thisjob; + if (!cmd->u.pline) { + shelltime(); + return 0; + } + execpline(cmd->u.pline, Z_TIMED|Z_SYNC, 0); + thisjob = jb; + return lastval; +} + +/* Define a shell function */ + +/**/ +static int +execfuncdef(Cmd cmd) +{ + Shfunc shf; + char *s; + int signum; + + PERMALLOC { + while ((s = (char *) ugetnode(cmd->args))) { + shf = (Shfunc) zalloc(sizeof *shf); + shf->funcdef = (List) dupstruct(cmd->u.list); + shf->flags = 0; + + /* is this shell function a signal trap? */ + if (!strncmp(s, "TRAP", 4) && (signum = getsignum(s + 4)) != -1) { + if (settrap(signum, shf->funcdef)) { + freestruct(shf->funcdef); + zfree(shf, sizeof *shf); + LASTALLOC_RETURN 1; + } + sigtrapped[signum] |= ZSIG_FUNC; + } + shfunctab->addnode(shfunctab, ztrdup(s), shf); + } + } LASTALLOC; + if(isset(HISTNOFUNCTIONS)) + remhist(); + return 0; +} + +/* Main entry point to execute a shell function. */ + +/**/ +static void +execshfunc(Cmd cmd, Shfunc shf) +{ + LinkList last_file_list = NULL; + + if (errflag) + return; + + if (!list_pipe) { + /* Without this deletejob the process table * + * would be filled by a recursive function. */ + last_file_list = jobtab[thisjob].filelist; + jobtab[thisjob].filelist = NULL; + deletejob(jobtab + thisjob); + } + + doshfunc(shf->funcdef, cmd->args, shf->flags, 0); + + if (!list_pipe) + deletefilelist(last_file_list); +} + +/* Function to execute the special type of command that represents an * + * autoloaded shell function. The command structure tells us which * + * function it is. This function is actually called as part of the * + * execution of the autoloaded function itself, so when the function * + * has been autoloaded, its list is just run with no frills. */ + +/**/ +static int +execautofn(Cmd cmd) +{ + Shfunc shf = cmd->u.autofn->shf; + List l = getfpfunc(shf->nam); + if(l == &dummy_list) { + zerr("%s: function definition file not found", shf->nam, 0); + return 1; + } + if(isset(KSHAUTOLOAD)) { + VARARR(char, n, strlen(shf->nam) + 1); + strcpy(n, shf->nam); + execlist(l, 1, 0); + shf = (Shfunc) shfunctab->getnode(shfunctab, n); + if(!shf || (shf->flags & PM_UNDEFINED)) { + zerr("%s: function not defined by file", n, 0); + return 1; + } + } else { + freestruct(shf->funcdef); + PERMALLOC { + shf->funcdef = dupstruct(stripkshdef(l, shf->nam)); + } LASTALLOC; + shf->flags &= ~PM_UNDEFINED; + } + HEAPALLOC { + execlist(dupstruct(shf->funcdef), 1, 0); + } LASTALLOC; + return lastval; +} + +/* execute a shell function */ + +/**/ +void +doshfunc(List list, LinkList doshargs, int flags, int noreturnval) +/* If noreturnval is nonzero, then reset the current return * + * value (lastval) to its value before the shell function * + * was executed. */ +{ + char **tab, **x, *oargv0 = NULL; + int xexittr, newexittr, oldzoptind, oldlastval; + char *ou; + void *xexitfn, *newexitfn; + char saveopts[OPT_SIZE]; + int obreaks = breaks; + + HEAPALLOC { + pushheap(); + if (trapreturn < 0) + trapreturn--; + oldlastval = lastval; + xexittr = sigtrapped[SIGEXIT]; + if (xexittr & ZSIG_FUNC) + xexitfn = shfunctab->removenode(shfunctab, "TRAPEXIT"); + else + xexitfn = sigfuncs[SIGEXIT]; + sigtrapped[SIGEXIT] = 0; + sigfuncs[SIGEXIT] = NULL; + tab = pparams; + oldzoptind = zoptind; + zoptind = 1; + + /* We need to save the current options even if LOCALOPTIONS is * + * not currently set. That's because if it gets set in the * + * function we need to restore the original options on exit. */ + memcpy(saveopts, opts, sizeof(opts)); + + if (flags & PM_TAGGED) + opts[XTRACE] = 1; + opts[PRINTEXITVALUE] = 0; + if (doshargs) { + LinkNode node; + + node = doshargs->first; + pparams = x = (char **) zcalloc(((sizeof *x) * (1 + countlinknodes(doshargs)))); + if (isset(FUNCTIONARGZERO)) { + oargv0 = argzero; + argzero = ztrdup((char *) node->dat); + } + node = node->next; + for (; node; node = node->next, x++) + *x = ztrdup((char *) node->dat); + } else { + pparams = (char **) zcalloc(sizeof *pparams); + if (isset(FUNCTIONARGZERO)) { + oargv0 = argzero; + argzero = ztrdup(argzero); + } + } + startparamscope(); + ou = underscore; + underscore = ztrdup(underscore); + execlist(dupstruct(list), 1, 0); + zsfree(underscore); + underscore = ou; + endparamscope(); + + if (retflag) { + retflag = 0; + breaks = obreaks; + } + freearray(pparams); + if (oargv0) { + zsfree(argzero); + argzero = oargv0; + } + zoptind = oldzoptind; + pparams = tab; + + if (isset(LOCALOPTIONS)) { + /* restore all shell options except PRIVILEGED and RESTRICTED */ + saveopts[PRIVILEGED] = opts[PRIVILEGED]; + saveopts[RESTRICTED] = opts[RESTRICTED]; + memcpy(opts, saveopts, sizeof(opts)); + } else { + /* just restore a couple. */ + opts[XTRACE] = saveopts[XTRACE]; + opts[PRINTEXITVALUE] = saveopts[PRINTEXITVALUE]; + opts[LOCALOPTIONS] = saveopts[LOCALOPTIONS]; + } + + /* + * The trap '...' EXIT runs in the environment of the caller, + * so remember it here but run it after resetting the + * traps for the parent. + */ + newexittr = sigtrapped[SIGEXIT]; + newexitfn = sigfuncs[SIGEXIT]; + if (newexittr & ZSIG_FUNC) + shfunctab->removenode(shfunctab, "TRAPEXIT"); + + sigtrapped[SIGEXIT] = xexittr; + if (xexittr & ZSIG_FUNC) { + shfunctab->addnode(shfunctab, ztrdup("TRAPEXIT"), xexitfn); + sigfuncs[SIGEXIT] = ((Shfunc) xexitfn)->funcdef; + } else + sigfuncs[SIGEXIT] = (List) xexitfn; + + if (newexitfn) { + dotrapargs(SIGEXIT, &newexittr, newexitfn); + freestruct(newexitfn); + } + + if (trapreturn < -1) + trapreturn++; + if (noreturnval) + lastval = oldlastval; + popheap(); + } LASTALLOC; +} + +/* Search fpath for an undefined function. Finds the file, and returns the * + * list of its contents. */ + +/**/ +static List +getfpfunc(char *s) +{ + char **pp, buf[PATH_MAX]; + off_t len; + char *d; + List r; + int fd; + + pp = fpath; + for (; *pp; pp++) { + if (strlen(*pp) + strlen(s) + 1 >= PATH_MAX) + continue; + if (**pp) + sprintf(buf, "%s/%s", *pp, s); + else + strcpy(buf, s); + unmetafy(buf, NULL); + if (!access(buf, R_OK) && (fd = open(buf, O_RDONLY | O_NOCTTY)) != -1) { + if ((len = lseek(fd, 0, 2)) != -1) { + lseek(fd, 0, 0); + d = (char *) zcalloc(len + 1); + if (read(fd, d, len) == len) { + close(fd); + d = metafy(d, len, META_REALLOC); + HEAPALLOC { + r = parse_string(d); + } LASTALLOC; + zfree(d, len + 1); + return r; + } else { + zfree(d, len + 1); + close(fd); + } + } else { + close(fd); + } + } + } + return &dummy_list; +} + +/* Handle the most common type of ksh-style autoloading, when doing a * + * zsh-style autoload. Given the list read from an autoload file, and the * + * name of the function being defined, check to see if the file consists * + * entirely of a single definition for that function. If so, use the * + * contents of that definition. Otherwise, use the entire file. */ + +/**/ +static List +stripkshdef(List l, char *name) +{ + Sublist s; + Pline p; + Cmd c; + if(!l) + return NULL; + if(l->type != Z_SYNC || l->right) + return l; + s = l->left; + if(s->flags || s->right) + return l; + p = s->left; + if(p->right) + return l; + c = p->left; + if(c->type != FUNCDEF || c->flags || + nonempty(c->redir) || nonempty(c->vars) || + empty(c->args) || lastnode(c->args) != firstnode(c->args) || + strcmp(name, peekfirst(c->args))) + return l; + return c->u.list; +} + +/* check to see if AUTOCD applies here */ + +/**/ +static char * +cancd(char *s) +{ + int nocdpath = s[0] == '.' && + (s[1] == '/' || !s[1] || (s[1] == '.' && (s[2] == '/' || !s[1]))); + char *t; + + if (*s != '/') { + char sbuf[PATH_MAX], **cp; + + if (cancd2(s)) + return s; + if (access(unmeta(s), X_OK) == 0) + return NULL; + if (!nocdpath) + for (cp = cdpath; *cp; cp++) { + if (strlen(*cp) + strlen(s) + 1 >= PATH_MAX) + continue; + if (**cp) + sprintf(sbuf, "%s/%s", *cp, s); + else + strcpy(sbuf, s); + if (cancd2(sbuf)) { + doprintdir = -1; + return dupstring(sbuf); + } + } + if ((t = cd_able_vars(s))) { + if (cancd2(t)) { + doprintdir = -1; + return t; + } + } + return NULL; + } + return cancd2(s) ? s : NULL; +} + +/**/ +static int +cancd2(char *s) +{ + struct stat buf; + char *us = unmeta(s); + + return !(access(us, X_OK) || stat(us, &buf) || !S_ISDIR(buf.st_mode)); +} + +/**/ +void +execsave(void) +{ + struct execstack *es; + + es = (struct execstack *) malloc(sizeof(struct execstack)); + es->args = args; + es->list_pipe_pid = list_pipe_pid; + es->nowait = nowait; + es->pline_level = pline_level; + es->list_pipe_child = list_pipe_child; + es->list_pipe_job = list_pipe_job; + strcpy(es->list_pipe_text, list_pipe_text); + es->lastval = lastval; + es->noeval = noeval; + es->badcshglob = badcshglob; + es->cmdoutpid = cmdoutpid; + es->cmdoutval = cmdoutval; + es->trapreturn = trapreturn; + es->noerrs = noerrs; + es->subsh_close = subsh_close; + es->underscore = underscore; + underscore = ztrdup(underscore); + es->next = exstack; + exstack = es; + noerrs = cmdoutpid = 0; +} + +/**/ +void +execrestore(void) +{ + struct execstack *en; + + DPUTS(!exstack, "BUG: execrestore() without execsave()"); + args = exstack->args; + list_pipe_pid = exstack->list_pipe_pid; + nowait = exstack->nowait; + pline_level = exstack->pline_level; + list_pipe_child = exstack->list_pipe_child; + list_pipe_job = exstack->list_pipe_job; + strcpy(list_pipe_text, exstack->list_pipe_text); + lastval = exstack->lastval; + noeval = exstack->noeval; + badcshglob = exstack->badcshglob; + cmdoutpid = exstack->cmdoutpid; + cmdoutval = exstack->cmdoutval; + trapreturn = exstack->trapreturn; + noerrs = exstack->noerrs; + subsh_close = exstack->subsh_close; + zsfree(underscore); + underscore = exstack->underscore; + en = exstack->next; + free(exstack); + exstack = en; +} |