Add GNU LGPL headers to all .c .C and .h files
[oweals/cde.git] / cde / programs / dtksh / ksh93 / src / cmd / ksh93 / sh / subshell.c
1 /*
2  * CDE - Common Desktop Environment
3  *
4  * Copyright (c) 1993-2012, The Open Group. All rights reserved.
5  *
6  * These libraries and programs are free software; you can
7  * redistribute them and/or modify them under the terms of the GNU
8  * Lesser General Public License as published by the Free Software
9  * Foundation; either version 2 of the License, or (at your option)
10  * any later version.
11  *
12  * These libraries and programs are distributed in the hope that
13  * they will be useful, but WITHOUT ANY WARRANTY; without even the
14  * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15  * PURPOSE. See the GNU Lesser General Public License for more
16  * details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with these librararies and programs; if not, write
20  * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
21  * Floor, Boston, MA 02110-1301 USA
22  */
23 /* $XConsortium: subshell.c /main/3 1995/11/01 16:51:36 rswiston $ */
24 /***************************************************************
25 *                                                              *
26 *                      AT&T - PROPRIETARY                      *
27 *                                                              *
28 *        THIS IS UNPUBLISHED PROPRIETARY SOURCE CODE OF        *
29 *                    AT&T BELL LABORATORIES                    *
30 *         AND IS NOT TO BE DISCLOSED OR USED EXCEPT IN         *
31 *            ACCORDANCE WITH APPLICABLE AGREEMENTS             *
32 *                                                              *
33 *                Copyright (c) 1995 AT&T Corp.                 *
34 *              Unpublished & Not for Publication               *
35 *                     All Rights Reserved                      *
36 *                                                              *
37 *       The copyright notice above does not evidence any       *
38 *      actual or intended publication of such source code      *
39 *                                                              *
40 *               This software was created by the               *
41 *           Advanced Software Technology Department            *
42 *                    AT&T Bell Laboratories                    *
43 *                                                              *
44 *               For further information contact                *
45 *                    {research,attmail}!dgk                    *
46 *                                                              *
47 ***************************************************************/
48
49 /* : : generated by proto : : */
50
51 #if !defined(__PROTO__)
52 #if defined(__STDC__) || defined(__cplusplus) || defined(_proto) || defined(c_plusplus)
53 #if defined(__cplusplus)
54 #define __MANGLE__      "C"
55 #else
56 #define __MANGLE__
57 #endif
58 #define __STDARG__
59 #define __PROTO__(x)    x
60 #define __OTORP__(x)
61 #define __PARAM__(n,o)  n
62 #if !defined(__STDC__) && !defined(__cplusplus)
63 #if !defined(c_plusplus)
64 #define const
65 #endif
66 #define signed
67 #define void            int
68 #define volatile
69 #define __V_            char
70 #else
71 #define __V_            void
72 #endif
73 #else
74 #define __PROTO__(x)    ()
75 #define __OTORP__(x)    x
76 #define __PARAM__(n,o)  o
77 #define __MANGLE__
78 #define __V_            char
79 #define const
80 #define signed
81 #define void            int
82 #define volatile
83 #endif
84 #if defined(__cplusplus) || defined(c_plusplus)
85 #define __VARARG__      ...
86 #else
87 #define __VARARG__
88 #endif
89 #if defined(__STDARG__)
90 #define __VA_START__(p,a)       va_start(p,a)
91 #else
92 #define __VA_START__(p,a)       va_start(p)
93 #endif
94 #endif
95 #include        "defs.h"
96 #include        <ls.h>
97 #include        "io.h"
98 #include        "fault.h"
99 #include        "shnodes.h"
100 #include        "shlex.h"
101 #include        "jobs.h"
102 #include        "variables.h"
103 #include        "path.h"
104
105
106 /*
107  * The following structure is used for command substitution and (...)
108  */
109 static struct subshell
110 {
111         struct subshell *prev;  /* previous subshell data */
112         struct subshell *pipe;  /* subshell where output goes to pipe on fork */
113         Hashtab_t       *var;   /* variable table at time of subshell */
114         Hashtab_t       *svar;  /* save shell variable table */
115         struct errorcontext *errcontext;
116         Shopt_t         options;/* save shell options */
117         pid_t           subpid; /* child process id */
118         Sfio_t* saveout;/*saved standard output */
119         char            *pwd;   /* present working directory */
120         int             mask;   /* present umask */
121         short           tmpfd;  /* saved tmp file descriptor */
122         char            jobcontrol;
123         char            monitor;
124         unsigned char   fdstatus;
125 } *subshell_data;
126
127 static int subenv;
128
129 /*
130  * This routine will turn the sftmp() file into a real /tmp file
131  */
132 void    sh_subtmpfile __PARAM__((void), ()){
133         if(sfset(sfstdout,0,0)&SF_STRING)
134         {
135                 register int fd;
136                 register struct checkpt *pp = (struct checkpt*)sh.jmplist;
137                 register struct subshell *sp = subshell_data->pipe;
138                 /* save file descriptor 1 if open */
139                 if((sp->tmpfd = fd = fcntl(1,F_DUPFD,10)) >= 0)
140                 {
141                         fcntl(fd,F_SETFD,FD_CLOEXEC);
142                         sh.fdstatus[fd] = sh.fdstatus[1]|IOCLEX;
143                         close(1);
144                 }
145                 /* popping a discipline forces a /tmp file create */
146                 sfdisc(sfstdout,SF_POPDISC);
147                 sh.fdstatus[fd=sffileno(sfstdout)] = IOREAD|IOWRITE;
148                 sfsync(sfstdout);
149                 if(fd==1)
150                         fcntl(1,F_SETFD,0);
151                 else
152                 {
153                         sfsetfd(sfstdout,1);
154                         sh.fdstatus[1] = sh.fdstatus[fd];
155                         sh.fdstatus[fd] = IOCLOSE;
156                 }
157                 sh_iostream(1);
158                 sfset(sfstdout,SF_SHARE|SF_PUBLIC,1);
159                 sfpool(sfstdout,sh.outpool,SF_WRITE);
160                 if(pp && pp->olist  && pp->olist->strm == sfstdout)
161                         pp->olist->strm = 0;
162         }
163 }
164
165 /*
166  * This routine creates a temp file if necessary and creates a subshell.
167  * The parent routine longjmps back to sh_subshell()
168  * The child continues possibly with its standard output replaced by temp file
169  */
170 void sh_subfork __PARAM__((void), ()){
171         register struct subshell *sp = subshell_data;
172         pid_t pid;
173         /* see whether inside $(...) */
174         if(sp->pipe)
175                 sh_subtmpfile();
176         if(pid = sh_fork(0,NIL(int*)))
177         {
178                 /* this is the parent part of the fork */
179                 if(sp->subpid==0)
180                         sp->subpid = pid;
181                 siglongjmp(*sh.jmplist,SH_JMPSUB);
182         }
183         else
184         {
185                 /* this is the child part of the fork */
186                 /* setting subpid to 1 causes subshell to exit when reached */
187                 sh_onstate(SH_FORKED|SH_NOLOG);
188                 sh_offstate(SH_MONITOR);
189                 subshell_data = 0;
190                 sh.subshell = 0;
191                 sp->subpid=0;
192         }
193 }
194
195 /*
196  * This routine will make a copy of the given node in the
197  * layer created by the most recent subshell_fork if the
198  * node hasn't already been copied
199  */
200 Namval_t *sh_assignok __PARAM__((register Namval_t *np,int add), (np, add)) __OTORP__(register Namval_t *np;int add;){
201         register Namval_t *mp;
202         if(nv_isnull(np) && !add)       /* don't bother with this */
203                 return(np);
204         /* don't bother to save if in newer scope */
205         if(nv_search((char*)np,subshell_data->var,HASH_BUCKET)!=np)
206                 return(np);
207         mp = nv_search((char*)np,subshell_data->svar,NV_ADD|HASH_BUCKET);
208         if(mp->nvflag || mp->nvalue.cp)         /* see if already saved */
209                 return(np);
210         if(nv_isnull(np))
211         {
212                 /* mark so that it can be restored */
213                 nv_onattr(mp,NV_NOFREE);
214                 return(np);
215         }
216         nv_setsize(mp,nv_size(np));
217         mp->nvenv = np->nvenv;
218         mp->nvfun = np->nvfun;
219         mp->nvalue.cp = np->nvalue.cp;
220         mp->nvflag = np->nvflag;
221         nv_onattr(np,NV_NOFREE);
222         return(np);
223 }
224
225 /*
226  * restore the variables
227  */
228 static void nv_restore __PARAM__((struct subshell *sp), (sp)) __OTORP__(struct subshell *sp;){
229         register Namval_t *mp, *np;
230         register Hashpos_t *pos = hashscan(sp->svar,0);
231         while(np=(Namval_t*)hashnext(pos))
232         {
233                 if(mp = nv_search((char*)np,sp->var,HASH_BUCKET))
234                 {
235                         nv_unset(mp);
236                         nv_setsize(mp,nv_size(np));
237                         mp->nvenv = np->nvenv;
238                         mp->nvfun = np->nvfun;
239                         mp->nvalue.cp = np->nvalue.cp;
240                         mp->nvflag = np->nvflag;
241                 }
242                 np->nvalue.cp = 0;
243                 np->nvflag = NV_DEFAULT;
244         }
245         hashdone(pos);
246         hashfree(sp->svar);
247 }
248
249
250 /*
251  * Run command tree <t> in a virtual sub-shell
252  * If comsub is not null, then output will be placed in temp file (or buffer)
253  * If comsub is not null, the return value will be a stream consisting of
254  * output of command <t>.  Otherwise, NULL will be returned.
255  */
256
257 Sfio_t *sh_subshell __PARAM__((union anynode *t, int flags, int comsub), (t, flags, comsub)) __OTORP__(union anynode *t; int flags; int comsub;){
258         struct subshell sub_data;
259         register struct subshell *sp = &sub_data;
260         int jmpval,nsig;
261         int savecurenv = sh.curenv;
262         char *savstak;
263         Sfio_t *iop=0;
264         struct checkpt buff;
265         struct sh_scoped savst;
266         struct dolnod   *argsav=0;
267         sfsync(sh.outpool);
268         argsav = sh_arguse();
269         sh.curenv = ++subenv;
270         savst = sh.st;
271         sh_pushcontext(&buff,SH_JMPSUB);
272         sh.subshell++;
273         sp->prev = subshell_data;
274         sp->subpid = 0;
275         sp->errcontext = &buff.err;
276         sp->var = sh.var_tree;
277         sp->options = sh.options;
278         sp->pipe = 0;
279         if(!sh.pwd)
280                 path_pwd(0);
281         sp->pwd = (sh.pwd?strdup(sh.pwd):0);
282         umask(sp->mask=umask(0));
283         subshell_data = sp;
284         /* save trap table */
285         sh.st.otrapcom = 0;
286         if((nsig=sh.st.trapmax*sizeof(char*))>0 || sh.st.trapcom[0])
287         {
288                 nsig += sizeof(char*);
289                 memcpy(savstak=stakalloc(nsig),(char*)&sh.st.trapcom[0],nsig);
290                 /* this nonsense needed for $(trap) */
291                 sh.st.otrapcom = (char**)savstak;
292         }
293         sh_sigreset(0);
294         sp->svar = hashalloc(sh.var_tree,HASH_set,HASH_ALLOCATE,0);
295         jmpval = sigsetjmp(buff.buff,0);
296         if(jmpval==0)
297         {
298                 if(comsub)
299                 {
300                         /* disable job control */
301                         sp->jobcontrol = job.jobcontrol;
302                         sp->monitor = (sh_isstate(SH_MONITOR)!=0);
303                         job.jobcontrol=0;
304                         sh_offstate(SH_MONITOR);
305                         sp->pipe = sp;
306                         /* save sfstdout and status */
307                         sp->saveout = sfswap(sfstdout,NIL(Sfio_t*));
308                         sp->fdstatus = sh.fdstatus[1];
309                         sp->tmpfd = -1;
310                         /* use sftmp() file for standard output */
311                         iop = sftmp(IOBSIZE+1);
312                         sfswap(iop,sfstdout);
313                         sh.fdstatus[1] = IOWRITE;
314                 }
315                 else if(sp->prev)
316                 {
317                         sp->pipe = sp->prev->pipe;
318                         flags &= ~SH_NOFORK;
319                 }
320                 sh_exec(t,flags);
321         }
322         if(jmpval!=SH_JMPSUB && sh.st.trapcom[0] && sh.subshell)
323         {
324                 /* trap on EXIT not handled by child */
325                 char *trap=sh.st.trapcom[0];
326                 sh.st.trapcom[0] = 0;   /* prevent recursion */
327                 sh.oldexit = sh.exitval;
328                 sh_trap(trap,0);
329                 free(trap);
330         }
331         sh_popcontext(&buff);
332         if(sh.subshell==0)      /* must be child process */
333         {
334                 if(jmpval==SH_JMPSCRIPT)
335                         siglongjmp(*sh.jmplist,jmpval);
336                 sh_done(0);
337         }
338         if(comsub)
339         {
340                 /* re-enable job control */
341                 job.jobcontrol = sp->jobcontrol;
342                 if(sp->monitor)
343                         sh_onstate(SH_MONITOR);
344                 /* move tmp file to iop and restore sfstdout */
345                 iop = sfswap(sfstdout,NIL(Sfio_t*));
346                 if(sffileno(iop)==1)
347                 {
348                         int fd=sfsetfd(iop,3);
349                         if(fd<0)
350                                 error(ERROR_system(1),e_toomany);
351                         sh.sftable[fd] = iop;
352                         fcntl(fd,F_SETFD,FD_CLOEXEC);
353                         sh.fdstatus[fd] = (sh.fdstatus[1]|IOCLEX);
354                         sh.fdstatus[1] = IOCLOSE;
355                 }
356                 sfswap(sp->saveout,sfstdout);
357                 /*  check if standard output was preserved */
358                 if(sp->tmpfd>=0)
359                 {
360                         close(1);
361                         fcntl(sp->tmpfd,F_DUPFD,1);
362                         sh_close(sp->tmpfd);
363                 }
364                 sh.fdstatus[1] = sp->fdstatus;
365         }
366         if(sp->subpid)
367                 job_wait(sp->subpid);
368         if(comsub && iop)
369                 sfseek(iop,(off_t)0,SEEK_SET);
370         if(sh.subshell)
371                 sh.subshell--;
372         sh.options = sp->options;
373         nv_restore(sp);
374         sh_argfree(argsav,0);
375         sh_sigreset(1);
376         sh.st = savst;
377         sh.jobenv = sh.curenv = savecurenv;
378         if(nsig)
379                 memcpy((char*)&sh.st.trapcom[0],savstak,nsig);
380         sh.trapnote = 0;
381         if(nsig)
382                 stakset(savstak,0);
383         sh.options = sp->options;
384         subshell_data = sp->prev;
385         if(!sh.pwd || strcmp(sp->pwd,sh.pwd))
386         {
387                 /* restore PWDNOD */
388                 Namval_t *pwdnod = nv_scoped(PWDNOD);
389                 if(sh.pwd)
390                         chdir(sh.pwd=sp->pwd);
391                 if(nv_isattr(pwdnod,NV_NOFREE))
392                         pwdnod->nvalue.cp = (const char*)sp->pwd;
393         }
394         else
395                 free((__V_*)sp->pwd);
396         umask(sp->mask);
397         if(sh.topfd != buff.topfd)
398                 sh_iorestore(buff.topfd);
399         if(sh.exitval > SH_EXITSIG)
400                 sh_fault(sh.exitval&SH_EXITMASK);
401         return(iop);
402 }