summaryrefslogtreecommitdiff
path: root/src/cmd/ksh93/sh/subshell.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/cmd/ksh93/sh/subshell.c')
-rw-r--r--src/cmd/ksh93/sh/subshell.c780
1 files changed, 780 insertions, 0 deletions
diff --git a/src/cmd/ksh93/sh/subshell.c b/src/cmd/ksh93/sh/subshell.c
new file mode 100644
index 0000000..c7c022b
--- /dev/null
+++ b/src/cmd/ksh93/sh/subshell.c
@@ -0,0 +1,780 @@
+/***********************************************************************
+* *
+* This software is part of the ast package *
+* Copyright (c) 1982-2012 AT&T Intellectual Property *
+* and is licensed under the *
+* Eclipse Public License, Version 1.0 *
+* by AT&T Intellectual Property *
+* *
+* A copy of the License is available at *
+* http://www.eclipse.org/org/documents/epl-v10.html *
+* (with md5 checksum b35adb5213ca9657e911e9befb180842) *
+* *
+* Information and Software Systems Research *
+* AT&T Research *
+* Florham Park NJ *
+* *
+* David Korn <dgk@research.att.com> *
+* *
+***********************************************************************/
+#pragma prototyped
+/*
+ * Create and manage subshells avoiding forks when possible
+ *
+ * David Korn
+ * AT&T Labs
+ *
+ */
+
+#include "defs.h"
+#include <ls.h>
+#include "io.h"
+#include "fault.h"
+#include "shnodes.h"
+#include "shlex.h"
+#include "jobs.h"
+#include "variables.h"
+#include "path.h"
+
+#ifndef PIPE_BUF
+# define PIPE_BUF 512
+#endif
+
+/*
+ * Note that the following structure must be the same
+ * size as the Dtlink_t structure
+ */
+struct Link
+{
+ struct Link *next;
+ Namval_t *child;
+ Dt_t *dict;
+ Namval_t *node;
+};
+
+/*
+ * The following structure is used for command substitution and (...)
+ */
+static struct subshell
+{
+ Shell_t *shp; /* shell interpreter */
+ struct subshell *prev; /* previous subshell data */
+ struct subshell *pipe; /* subshell where output goes to pipe on fork */
+ Dt_t *var; /* variable table at time of subshell */
+ struct Link *svar; /* save shell variable table */
+ Dt_t *sfun; /* function scope for subshell */
+ Dt_t *salias;/* alias scope for subshell */
+ Pathcomp_t *pathlist; /* for PATH variable */
+#if (ERROR_VERSION >= 20030214L)
+ struct Error_context_s *errcontext;
+#else
+ struct errorcontext *errcontext;
+#endif
+ Shopt_t options;/* save shell options */
+ pid_t subpid; /* child process id */
+ Sfio_t* saveout;/*saved standard output */
+ char *pwd; /* present working directory */
+ const char *shpwd; /* saved pointer to sh.pwd */
+ void *jobs; /* save job info */
+ mode_t mask; /* saved umask */
+ short tmpfd; /* saved tmp file descriptor */
+ short pipefd; /* read fd if pipe is created */
+ char jobcontrol;
+ char monitor;
+ unsigned char fdstatus;
+ int fdsaved; /* bit make for saved files */
+ int sig; /* signal for $$ */
+ pid_t bckpid;
+ pid_t cpid;
+ int coutpipe;
+ int cpipe;
+ int nofork;
+ int subdup;
+ char subshare;
+ char comsub;
+#if SHOPT_COSHELL
+ void *coshell;
+#endif /* SHOPT_COSHELL */
+} *subshell_data;
+
+static int subenv;
+
+
+/*
+ * This routine will turn the sftmp() file into a real /tmp file or pipe
+ * if the /tmp file create fails
+ */
+void sh_subtmpfile(Shell_t *shp)
+{
+ if(sfset(sfstdout,0,0)&SF_STRING)
+ {
+ register int fd;
+ register struct checkpt *pp = (struct checkpt*)shp->jmplist;
+ register struct subshell *sp = subshell_data->pipe;
+ /* save file descriptor 1 if open */
+ if((sp->tmpfd = fd = fcntl(1,F_DUPFD,10)) >= 0)
+ {
+ fcntl(fd,F_SETFD,FD_CLOEXEC);
+ shp->fdstatus[fd] = shp->fdstatus[1]|IOCLEX;
+ close(1);
+ }
+ else if(errno!=EBADF)
+ errormsg(SH_DICT,ERROR_system(1),e_toomany);
+ /* popping a discipline forces a /tmp file create */
+ sfdisc(sfstdout,SF_POPDISC);
+ if((fd=sffileno(sfstdout))<0)
+ {
+ /* unable to create the /tmp file so use a pipe */
+ int fds[3];
+ Sfoff_t off;
+ fds[2] = 0;
+ sh_pipe(fds);
+ sp->pipefd = fds[0];
+ sh_fcntl(sp->pipefd,F_SETFD,FD_CLOEXEC);
+ /* write the data to the pipe */
+ if(off = sftell(sfstdout))
+ write(fds[1],sfsetbuf(sfstdout,(Void_t*)sfstdout,0),(size_t)off);
+ sfclose(sfstdout);
+ if((sh_fcntl(fds[1],F_DUPFD, 1)) != 1)
+ errormsg(SH_DICT,ERROR_system(1),e_file+4);
+ sh_close(fds[1]);
+ }
+ else
+ {
+ shp->fdstatus[fd] = IOREAD|IOWRITE;
+ sfsync(sfstdout);
+ if(fd==1)
+ fcntl(1,F_SETFD,0);
+ else
+ {
+ sfsetfd(sfstdout,1);
+ shp->fdstatus[1] = shp->fdstatus[fd];
+ shp->fdstatus[fd] = IOCLOSE;
+ }
+ }
+ sh_iostream(shp,1);
+ sfset(sfstdout,SF_SHARE|SF_PUBLIC,1);
+ sfpool(sfstdout,shp->outpool,SF_WRITE);
+ if(pp && pp->olist && pp->olist->strm == sfstdout)
+ pp->olist->strm = 0;
+ }
+}
+
+
+/*
+ * This routine creates a temp file if necessary and creates a subshell.
+ * The parent routine longjmps back to sh_subshell()
+ * The child continues possibly with its standard output replaced by temp file
+ */
+void sh_subfork(void)
+{
+ register struct subshell *sp = subshell_data;
+ Shell_t *shp = sp->shp;
+ int curenv = shp->curenv;
+ pid_t pid;
+ char *trap = shp->st.trapcom[0];
+ if(trap)
+ trap = strdup(trap);
+ /* see whether inside $(...) */
+ if(sp->pipe)
+ sh_subtmpfile(shp);
+ shp->curenv = 0;
+ shp->savesig = -1;
+ if(pid = sh_fork(shp,FSHOWME,NIL(int*)))
+ {
+ shp->curenv = curenv;
+ /* this is the parent part of the fork */
+ if(sp->subpid==0)
+ sp->subpid = pid;
+ if(trap)
+ free((void*)trap);
+ siglongjmp(*shp->jmplist,SH_JMPSUB);
+ }
+ else
+ {
+ /* this is the child part of the fork */
+ /* setting subpid to 1 causes subshell to exit when reached */
+ sh_onstate(SH_FORKED);
+ sh_onstate(SH_NOLOG);
+ sh_offoption(SH_MONITOR);
+ sh_offstate(SH_MONITOR);
+ subshell_data = 0;
+ shp->subshell = 0;
+ shp->comsub = 0;
+ SH_SUBSHELLNOD->nvalue.s = 0;
+ sp->subpid=0;
+ shp->st.trapcom[0] = trap;
+ shp->savesig = 0;
+ }
+}
+
+int nv_subsaved(register Namval_t *np)
+{
+ register struct subshell *sp;
+ register struct Link *lp;
+ for(sp = (struct subshell*)subshell_data; sp; sp=sp->prev)
+ {
+ for(lp=sp->svar; lp; lp = lp->next)
+ {
+ if(lp->node==np)
+ return(1);
+ }
+ }
+ return(0);
+}
+
+/*
+ * This routine will make a copy of the given node in the
+ * layer created by the most recent subshell_fork if the
+ * node hasn't already been copied
+ */
+Namval_t *sh_assignok(register Namval_t *np,int add)
+{
+ register Namval_t *mp;
+ register struct Link *lp;
+ register struct subshell *sp = (struct subshell*)subshell_data;
+ Shell_t *shp = sp->shp;
+ Dt_t *dp= shp->var_tree;
+ Namval_t *mpnext;
+ Namarr_t *ap;
+ int save;
+ /* don't bother with this */
+ if(!sp->shpwd || np==SH_LEVELNOD || np==L_ARGNOD || np==SH_SUBSCRNOD || np==SH_NAMENOD)
+ return(np);
+ /* don't bother to save if in newer scope */
+ if(sp->var!=shp->var_tree && sp->var!=shp->var_base && shp->last_root==shp->var_tree)
+ return(np);
+ if((ap=nv_arrayptr(np)) && (mp=nv_opensub(np)))
+ {
+ shp->last_root = ap->table;
+ sh_assignok(mp,add);
+ if(!add || array_assoc(ap))
+ return(np);
+ }
+ for(lp=sp->svar; lp;lp = lp->next)
+ {
+ if(lp->node==np)
+ return(np);
+ }
+ /* first two pointers use linkage from np */
+ lp = (struct Link*)malloc(sizeof(*np)+2*sizeof(void*));
+ memset(lp,0, sizeof(*mp)+2*sizeof(void*));
+ lp->node = np;
+ if(!add && nv_isvtree(np))
+ {
+ Namval_t fake;
+ Dt_t *walk, *root=shp->var_tree;
+ char *name = nv_name(np);
+ int len = strlen(name);
+ fake.nvname = name;
+ mpnext = dtnext(root,&fake);
+ dp = root->walk?root->walk:root;
+ while(mp=mpnext)
+ {
+ walk = root->walk?root->walk:root;
+ mpnext = dtnext(root,mp);
+ if(memcmp(name,mp->nvname,len) || mp->nvname[len]!='.')
+ break;
+ nv_delete(mp,walk,NV_NOFREE);
+ *((Namval_t**)mp) = lp->child;
+ lp->child = mp;
+
+ }
+ }
+ lp->dict = dp;
+ mp = (Namval_t*)&lp->dict;
+ lp->next = subshell_data->svar;
+ subshell_data->svar = lp;
+ save = shp->subshell;
+ shp->subshell = 0;
+ mp->nvname = np->nvname;
+ if(nv_isattr(np,NV_NOFREE))
+ nv_onattr(mp,NV_IDENT);
+ nv_clone(np,mp,(add?(nv_isnull(np)?0:NV_NOFREE)|NV_ARRAY:NV_MOVE));
+ shp->subshell = save;
+ return(np);
+}
+
+/*
+ * restore the variables
+ */
+static void nv_restore(struct subshell *sp)
+{
+ register struct Link *lp, *lq;
+ register Namval_t *mp, *np;
+ const char *save = sp->shpwd;
+ Namval_t *mpnext;
+ int flags,nofree;
+ sp->shpwd = 0; /* make sure sh_assignok doesn't save with nv_unset() */
+ for(lp=sp->svar; lp; lp=lq)
+ {
+ np = (Namval_t*)&lp->dict;
+ lq = lp->next;
+ mp = lp->node;
+ if(!mp->nvname)
+ continue;
+ flags = 0;
+ if(nv_isattr(mp,NV_MINIMAL) && !nv_isattr(np,NV_EXPORT))
+ flags |= NV_MINIMAL;
+ if(nv_isarray(mp))
+ nv_putsub(mp,NIL(char*),ARRAY_SCAN);
+ nofree = mp->nvfun?mp->nvfun->nofree:0;
+ _nv_unset(mp,NV_RDONLY|NV_CLONE);
+ if(nv_isarray(np))
+ {
+ nv_clone(np,mp,NV_MOVE);
+ goto skip;
+ }
+ nv_setsize(mp,nv_size(np));
+ if(!(flags&NV_MINIMAL))
+ mp->nvenv = np->nvenv;
+ if(!nofree)
+ mp->nvfun = np->nvfun;
+ if(nv_isattr(np,NV_IDENT))
+ {
+ nv_offattr(np,NV_IDENT);
+ flags |= NV_NOFREE;
+ }
+ mp->nvflag = np->nvflag|(flags&NV_MINIMAL);
+ if(nv_cover(mp))
+ nv_putval(mp, nv_getval(np),np->nvflag|NV_NOFREE);
+ else
+ mp->nvalue.cp = np->nvalue.cp;
+ if(nofree && np->nvfun && !np->nvfun->nofree)
+ free((char*)np->nvfun);
+ np->nvfun = 0;
+ if(nv_isattr(mp,NV_EXPORT))
+ {
+ char *name = nv_name(mp);
+ sh_envput(sp->shp->env,mp);
+ if(*name=='_' && strcmp(name,"_AST_FEATURES")==0)
+ astconf(NiL, NiL, NiL);
+ }
+ else if(nv_isattr(np,NV_EXPORT))
+ env_delete(sp->shp->env,nv_name(mp));
+ nv_onattr(mp,flags);
+ skip:
+ for(mp=lp->child; mp; mp=mpnext)
+ {
+ mpnext = *((Namval_t**)mp);
+ dtinsert(lp->dict,mp);
+ }
+ free((void*)lp);
+ sp->svar = lq;
+ }
+ sp->shpwd=save;
+}
+
+/*
+ * return pointer to alias tree
+ * create new one if in a subshell and one doesn't exist and create is non-zero
+ */
+Dt_t *sh_subaliastree(int create)
+{
+ register struct subshell *sp = subshell_data;
+ if(!sp || sp->shp->curenv==0)
+ return(sh.alias_tree);
+ if(!sp->salias && create)
+ {
+ sp->salias = dtopen(&_Nvdisc,Dtoset);
+ dtview(sp->salias,sp->shp->alias_tree);
+ sp->shp->alias_tree = sp->salias;
+ }
+ return(sp->salias);
+}
+
+/*
+ * return pointer to function tree
+ * create new one if in a subshell and one doesn't exist and create is non-zero
+ */
+Dt_t *sh_subfuntree(int create)
+{
+ register struct subshell *sp = subshell_data;
+ if(!sp || sp->shp->curenv==0)
+ return(sh.fun_tree);
+ if(!sp->sfun && create)
+ {
+ sp->sfun = dtopen(&_Nvdisc,Dtoset);
+ dtview(sp->sfun,sp->shp->fun_tree);
+ sp->shp->fun_tree = sp->sfun;
+ }
+ return(sp->shp->fun_tree);
+}
+
+static void table_unset(register Dt_t *root,int fun)
+{
+ register Namval_t *np,*nq;
+ int flag;
+ for(np=(Namval_t*)dtfirst(root);np;np=nq)
+ {
+ nq = (Namval_t*)dtnext(root,np);
+ flag=0;
+ if(fun && np->nvalue.rp && np->nvalue.rp->fname && *np->nvalue.rp->fname=='/')
+ {
+ np->nvalue.rp->fdict = 0;
+ flag = NV_NOFREE;
+ }
+ else
+ _nv_unset(np,NV_RDONLY);
+ nv_delete(np,root,flag|NV_FUNCTION);
+ }
+}
+
+int sh_subsavefd(register int fd)
+{
+ register struct subshell *sp = subshell_data;
+ register int old=0;
+ if(sp)
+ {
+ old = !(sp->fdsaved&(1<<(fd-1)));
+ sp->fdsaved |= (1<<(fd-1));
+ }
+ return(old);
+}
+
+void sh_subjobcheck(pid_t pid)
+{
+ register struct subshell *sp = subshell_data;
+ while(sp)
+ {
+ if(sp->cpid==pid)
+ {
+ sh_close(sp->coutpipe);
+ sh_close(sp->cpipe);
+ sp->coutpipe = sp->cpipe = -1;
+ return;
+ }
+ sp = sp->prev;
+ }
+}
+
+/*
+ * Run command tree <t> in a virtual sub-shell
+ * If comsub is not null, then output will be placed in temp file (or buffer)
+ * If comsub is not null, the return value will be a stream consisting of
+ * output of command <t>. Otherwise, NULL will be returned.
+ */
+
+Sfio_t *sh_subshell(Shell_t *shp,Shnode_t *t, int flags, int comsub)
+{
+ struct subshell sub_data;
+ register struct subshell *sp = &sub_data;
+ int jmpval,nsig=0,duped=0;
+ int savecurenv = shp->curenv;
+ int savejobpgid = job.curpgid;
+ int *saveexitval = job.exitval;
+ int16_t subshell;
+ char *savsig;
+ Sfio_t *iop=0;
+ struct checkpt buff;
+ struct sh_scoped savst;
+ struct dolnod *argsav=0;
+ int argcnt;
+ memset((char*)sp, 0, sizeof(*sp));
+ sfsync(shp->outpool);
+ sh_sigcheck(shp);
+ shp->savesig = -1;
+ if(argsav = sh_arguse(shp))
+ argcnt = argsav->dolrefcnt;
+ if(shp->curenv==0)
+ {
+ subshell_data=0;
+ subenv = 0;
+ }
+ shp->curenv = ++subenv;
+ savst = shp->st;
+ sh_pushcontext(shp,&buff,SH_JMPSUB);
+ subshell = shp->subshell+1;
+ SH_SUBSHELLNOD->nvalue.s = subshell;
+ shp->subshell = subshell;
+ sp->prev = subshell_data;
+ sp->shp = shp;
+ sp->sig = 0;
+ subshell_data = sp;
+ sp->errcontext = &buff.err;
+ sp->var = shp->var_tree;
+ sp->options = shp->options;
+ sp->jobs = job_subsave();
+ sp->subdup = shp->subdup;
+#if SHOPT_COSHELL
+ sp->coshell = shp->coshell;
+ shp->coshell = 0;
+#endif /* SHOPT_COSHELL */
+ /* make sure initialization has occurred */
+ if(!shp->pathlist)
+ {
+ shp->pathinit = 1;
+ path_get(shp,".");
+ shp->pathinit = 0;
+ }
+ sp->pathlist = path_dup((Pathcomp_t*)shp->pathlist);
+ if(!shp->pwd)
+ path_pwd(shp,0);
+ sp->bckpid = shp->bckpid;
+ if(comsub)
+ sh_stats(STAT_COMSUB);
+ else
+ job.curpgid = 0;
+ sp->subshare = shp->subshare;
+ sp->comsub = shp->comsub;
+ shp->subshare = comsub==2 || (comsub==1 && sh_isoption(SH_SUBSHARE));
+ if(comsub)
+ shp->comsub = comsub;
+ if(!comsub || !shp->subshare)
+ {
+ sp->shpwd = shp->pwd;
+ sp->pwd = (shp->pwd?strdup(shp->pwd):0);
+ sp->mask = shp->mask;
+ sh_stats(STAT_SUBSHELL);
+ /* save trap table */
+ shp->st.otrapcom = 0;
+ if((nsig=shp->st.trapmax*sizeof(char*))>0 || shp->st.trapcom[0])
+ {
+ nsig += sizeof(char*);
+ memcpy(savsig=malloc(nsig),(char*)&shp->st.trapcom[0],nsig);
+ /* this nonsense needed for $(trap) */
+ shp->st.otrapcom = (char**)savsig;
+ }
+ sp->cpid = shp->cpid;
+ sp->coutpipe = shp->coutpipe;
+ sp->cpipe = shp->cpipe[1];
+ shp->cpid = 0;
+ sh_sigreset(0);
+ }
+ jmpval = sigsetjmp(buff.buff,0);
+ if(jmpval==0)
+ {
+ if(comsub)
+ {
+ /* disable job control */
+ shp->spid = 0;
+ sp->jobcontrol = job.jobcontrol;
+ sp->monitor = (sh_isstate(SH_MONITOR)!=0);
+ job.jobcontrol=0;
+ sh_offstate(SH_MONITOR);
+ sp->pipe = sp;
+ /* save sfstdout and status */
+ sp->saveout = sfswap(sfstdout,NIL(Sfio_t*));
+ sp->fdstatus = shp->fdstatus[1];
+ sp->tmpfd = -1;
+ sp->pipefd = -1;
+ /* use sftmp() file for standard output */
+ if(!(iop = sftmp(PIPE_BUF)))
+ {
+ sfswap(sp->saveout,sfstdout);
+ errormsg(SH_DICT,ERROR_system(1),e_tmpcreate);
+ }
+ sfswap(iop,sfstdout);
+ sfset(sfstdout,SF_READ,0);
+ shp->fdstatus[1] = IOWRITE;
+ if(!(sp->nofork = sh_state(SH_NOFORK)))
+ sh_onstate(SH_NOFORK);
+ flags |= sh_state(SH_NOFORK);
+ }
+ else if(sp->prev)
+ {
+ sp->pipe = sp->prev->pipe;
+ flags &= ~sh_state(SH_NOFORK);
+ }
+ if(shp->savesig < 0)
+ {
+ shp->savesig = 0;
+ sh_exec(t,flags);
+ }
+ }
+ if(comsub!=2 && jmpval!=SH_JMPSUB && shp->st.trapcom[0] && shp->subshell)
+ {
+ /* trap on EXIT not handled by child */
+ char *trap=shp->st.trapcom[0];
+ shp->st.trapcom[0] = 0; /* prevent recursion */
+ shp->oldexit = shp->exitval;
+ sh_trap(trap,0);
+ free(trap);
+ }
+ sh_popcontext(shp,&buff);
+ if(shp->subshell==0) /* must be child process */
+ {
+ subshell_data = sp->prev;
+ if(jmpval==SH_JMPSCRIPT)
+ siglongjmp(*shp->jmplist,jmpval);
+ shp->exitval &= SH_EXITMASK;
+ sh_done(shp,0);
+ }
+ if(!shp->savesig)
+ shp->savesig = -1;
+ if(comsub)
+ {
+ /* re-enable job control */
+ if(!sp->nofork)
+ sh_offstate(SH_NOFORK);
+ job.jobcontrol = sp->jobcontrol;
+ if(sp->monitor)
+ sh_onstate(SH_MONITOR);
+ if(sp->pipefd>=0)
+ {
+ /* sftmp() file has been returned into pipe */
+ iop = sh_iostream(shp,sp->pipefd);
+ sfclose(sfstdout);
+ }
+ else
+ {
+ /* move tmp file to iop and restore sfstdout */
+ iop = sfswap(sfstdout,NIL(Sfio_t*));
+ if(!iop)
+ {
+ /* maybe locked try again */
+ sfclrlock(sfstdout);
+ iop = sfswap(sfstdout,NIL(Sfio_t*));
+ }
+ if(iop && sffileno(iop)==1)
+ {
+ int fd=sfsetfd(iop,3);
+ if(fd<0)
+ {
+ shp->toomany = 1;
+ ((struct checkpt*)shp->jmplist)->mode = SH_JMPERREXIT;
+ errormsg(SH_DICT,ERROR_system(1),e_toomany);
+ }
+ if(fd >= shp->gd->lim.open_max)
+ sh_iovalidfd(shp,fd);
+ shp->sftable[fd] = iop;
+ fcntl(fd,F_SETFD,FD_CLOEXEC);
+ shp->fdstatus[fd] = (shp->fdstatus[1]|IOCLEX);
+ shp->fdstatus[1] = IOCLOSE;
+ }
+ sfset(iop,SF_READ,1);
+ }
+ sfswap(sp->saveout,sfstdout);
+ /* check if standard output was preserved */
+ if(sp->tmpfd>=0)
+ {
+ close(1);
+ if (fcntl(sp->tmpfd,F_DUPFD,1) != 1)
+ duped++;
+ sh_close(sp->tmpfd);
+ }
+ shp->fdstatus[1] = sp->fdstatus;
+ }
+ path_delete((Pathcomp_t*)shp->pathlist);
+ shp->pathlist = (void*)sp->pathlist;
+ job_subrestore(sp->jobs);
+ shp->jobenv = savecurenv;
+ job.curpgid = savejobpgid;
+ job.exitval = saveexitval;
+ shp->bckpid = sp->bckpid;
+ if(sp->shpwd) /* restore environment if saved */
+ {
+ int n;
+ shp->options = sp->options;
+ nv_restore(sp);
+ if(sp->salias)
+ {
+ shp->alias_tree = dtview(sp->salias,0);
+ table_unset(sp->salias,0);
+ dtclose(sp->salias);
+ }
+ if(sp->sfun)
+ {
+ shp->fun_tree = dtview(sp->sfun,0);
+ table_unset(sp->sfun,1);
+ dtclose(sp->sfun);
+ }
+ n = shp->st.trapmax-savst.trapmax;
+ sh_sigreset(1);
+ if(n>0)
+ memset(&shp->st.trapcom[savst.trapmax],0,n*sizeof(char*));
+ shp->st = savst;
+ shp->curenv = savecurenv;
+ if(nsig)
+ {
+ memcpy((char*)&shp->st.trapcom[0],savsig,nsig);
+ free((void*)savsig);
+ }
+ shp->options = sp->options;
+ if(!shp->pwd || strcmp(sp->pwd,shp->pwd))
+ {
+ /* restore PWDNOD */
+ Namval_t *pwdnod = sh_scoped(shp,PWDNOD);
+ if(shp->pwd)
+ {
+ chdir(shp->pwd=sp->pwd);
+ path_newdir(shp,shp->pathlist);
+ }
+ if(nv_isattr(pwdnod,NV_NOFREE))
+ pwdnod->nvalue.cp = (const char*)sp->pwd;
+ }
+ else if(sp->shpwd != shp->pwd)
+ {
+ shp->pwd = sp->pwd;
+ if(PWDNOD->nvalue.cp==sp->shpwd)
+ PWDNOD->nvalue.cp = sp->pwd;
+ }
+ else
+ free((void*)sp->pwd);
+ if(sp->mask!=shp->mask)
+ umask(shp->mask=sp->mask);
+ if(shp->coutpipe!=sp->coutpipe)
+ {
+ sh_close(shp->coutpipe);
+ sh_close(shp->cpipe[1]);
+ }
+ shp->cpid = sp->cpid;
+ shp->cpipe[1] = sp->cpipe;
+ shp->coutpipe = sp->coutpipe;
+ }
+ shp->subshare = sp->subshare;
+ shp->comsub = sp->comsub;
+ shp->subdup = sp->subdup;
+#if SHOPT_COSHELL
+ shp->coshell = sp->coshell;
+#endif /* SHOPT_COSHELL */
+ if(shp->subshell)
+ SH_SUBSHELLNOD->nvalue.s = --shp->subshell;
+ subshell = shp->subshell;
+ subshell_data = sp->prev;
+ if(!argsav || argsav->dolrefcnt==argcnt)
+ sh_argfree(shp,argsav,0);
+ if(shp->topfd != buff.topfd)
+ sh_iorestore(shp,buff.topfd|IOSUBSHELL,jmpval);
+ if(sp->sig)
+ {
+ if(sp->prev)
+ sp->prev->sig = sp->sig;
+ else
+ {
+ sh_fault(sp->sig);
+ sh_chktrap(shp);
+ }
+ }
+ sh_sigcheck(shp);
+ shp->trapnote = 0;
+ nsig = shp->savesig;
+ shp->savesig = 0;
+ if(nsig>0)
+ sh_fault(nsig);
+ if(sp->subpid)
+ job_wait(sp->subpid);
+ if(comsub && iop && sp->pipefd<0)
+ sfseek(iop,(off_t)0,SEEK_SET);
+ if(shp->trapnote)
+ sh_chktrap(shp);
+ if(shp->exitval > SH_EXITSIG)
+ {
+ int sig = shp->exitval&SH_EXITMASK;
+ if(sig==SIGINT || sig== SIGQUIT)
+ sh_fault(sig);
+ }
+ if(duped)
+ {
+ ((struct checkpt*)shp->jmplist)->mode = SH_JMPERREXIT;
+ shp->toomany = 1;
+ errormsg(SH_DICT,ERROR_system(1),e_redirect);
+ }
+ if(shp->ignsig)
+ sh_fault(shp->ignsig);
+ if(jmpval==SH_JMPSUB && shp->lastsig)
+ sh_fault(shp->lastsig);
+ if(jmpval && shp->toomany)
+ siglongjmp(*shp->jmplist,jmpval);
+ return(iop);
+}