1 /***********************************************************************
2 *                                                                      *
3 *               This software is part of the ast package               *
4 *          Copyright (c) 1982-2014 AT&T Intellectual Property          *
5 *                      and is licensed under the                       *
6 *                 Eclipse Public License, Version 1.0                  *
7 *                    by AT&T Intellectual Property                     *
8 *                                                                      *
9 *                A copy of the License is available at                 *
10 *          http://www.eclipse.org/org/documents/epl-v10.html           *
11 *         (with md5 checksum b35adb5213ca9657e911e9befb180842)         *
12 *                                                                      *
13 *              Information and Software Systems Research               *
14 *                            AT&T Research                             *
15 *                           Florham Park NJ                            *
16 *                                                                      *
17 *                    David Korn <dgkorn@gmail.com>                     *
18 *                                                                      *
19 ***********************************************************************/
20 #pragma prototyped
21 /*
22  * UNIX shell
23  *
24  * S. R. Bourne
25  * Rewritten By David Korn
26  * AT&T Labs
27  *
28  */
29 
30 #include	<ast.h>
31 #include	<sfio.h>
32 #include	<stak.h>
33 #include	<ls.h>
34 #include	<fcin.h>
35 #include	"defs.h"
36 #include	"variables.h"
37 #include	"path.h"
38 #include	"io.h"
39 #include	"jobs.h"
40 #include	"shlex.h"
41 #include	"shnodes.h"
42 #include	"history.h"
43 #include	"timeout.h"
44 #include	"FEATURE/time"
45 #include	"FEATURE/pstat"
46 #include	"FEATURE/execargs"
47 #include	"FEATURE/externs"
48 #ifdef	_hdr_nc
49 #   include	<nc.h>
50 #endif	/* _hdr_nc */
51 
52 #define CMD_LENGTH	64
53 
54 /* These routines are referenced by this module */
55 static void	exfile(Shell_t*, Sfio_t*,int);
56 static void	chkmail(Shell_t *shp, char*);
57 #if defined(_lib_fork) && !defined(_NEXT_SOURCE)
58     static void	fixargs(char**,int);
59 #else
60 #   define fixargs(a,b)
61 #endif
62 
63 #ifndef environ
64     extern char	**environ;
65 #endif
66 
67 static struct stat lastmail;
68 static time_t	mailtime;
69 static char	beenhere = 0;
70 
71 #ifdef _lib_sigvec
clearsigmask(register int sig)72     void clearsigmask(register int sig)
73     {
74 	struct sigvec vec;
75 	if(sigvec(sig,NIL(struct sigvec*),&vec)>=0 && vec.sv_mask)
76 	{
77 		vec.sv_mask = 0;
78 		sigvec(sig,&vec,NIL(struct sigvec*));
79 	}
80     }
81 #endif /* _lib_sigvec */
82 
83 #ifdef PATH_BFPATH
84 #define PATHCOMP	NIL(Pathcomp_t*)
85 #else
86 #define PATHCOMP	""
87 #endif
88 
89 /*
90  * search for file and exfile() it if it exists
91  * 1 returned if file found, 0 otherwise
92  */
93 
sh_source(Shell_t * shp,Sfio_t * iop,const char * file)94 bool sh_source(Shell_t *shp, Sfio_t *iop, const char *file)
95 {
96 	char*	oid;
97 	char*	nid;
98 	int	fd;
99 
100 	if (!file || !*file || (fd = path_open(shp,file, PATHCOMP)) < 0)
101 	{
102 		REGRESS(source, "sh_source", ("%s:ENOENT", file));
103 		return false;
104 	}
105 	oid = error_info.id;
106 	nid = error_info.id = strdup(file);
107 	shp->st.filename = path_fullname(shp,stkptr(shp->stk,PATH_OFFSET));
108 	REGRESS(source, "sh_source", ("%s", file));
109 	exfile(shp, iop, fd);
110 	error_info.id = oid;
111 	free(nid);
112 	return true;
113 }
114 
115 #ifdef S_ISSOCK
116 #define REMOTE(m)	(S_ISSOCK(m)||!(m))
117 #else
118 #define REMOTE(m)	!(m)
119 #endif
120 
sh_main(int ac,char * av[],Shinit_f userinit)121 int sh_main(int ac, char *av[], Shinit_f userinit)
122 {
123 	register char	*name;
124 	register int	fdin;
125 	register Sfio_t  *iop;
126 	register Shell_t *shp;
127 	struct stat	statb;
128 	int 		i;	/* set for restricted shell */
129 	bool		rshflag;	/* set for restricted shell */
130 	char *command;
131 #ifdef AST_SERIAL_RESTART
132 	/* restart all ast_*() intercepted syscalls on EINTR */
133 	astserial(AST_SERIAL_RESTART, AST_SERIAL_always);
134 #endif
135 	free(malloc(64*1024));
136 #ifdef _lib_sigvec
137 	/* This is to clear mask that may be left on by rlogin */
138 	clearsigmask(SIGALRM);
139 	clearsigmask(SIGHUP);
140 	clearsigmask(SIGCHLD);
141 #endif /* _lib_sigvec */
142 #ifdef	_hdr_nc
143 	_NutConf(_NC_SET_SUFFIXED_SEARCHING, 1);
144 #endif	/* _hdr_nc */
145 	fixargs(av,0);
146 	shp = sh_init(ac,av,userinit);
147 	time(&mailtime);
148 	if(rshflag=sh_isoption(shp,SH_RESTRICTED))
149 		sh_offoption(shp,SH_RESTRICTED);
150 	if(sigsetjmp(*((sigjmp_buf*)shp->jmpbuffer),0))
151 	{
152 		/* begin script execution here */
153 		sh_reinit(shp,(char**)0);
154 		shp->gd->pid = getpid();
155 		shp->gd->ppid = getppid();
156 	}
157 	shp->fn_depth = shp->dot_depth = 0;
158 	command = error_info.id;
159 	/* set pidname '$$' */
160 	srand(shp->gd->pid&0x7fff);
161 	if(nv_isnull(PS4NOD))
162 		nv_putval(PS4NOD,e_traceprompt,NV_RDONLY);
163 	path_pwd(shp,1);
164 	iop = (Sfio_t*)0;
165 #if SHOPT_BRACEPAT
166 	sh_onoption(shp,SH_BRACEEXPAND);
167 #endif
168 	if((beenhere++)==0)
169 	{
170 		sh_onstate(shp,SH_PROFILE);
171 		((Lex_t*)shp->lex_context)->nonstandard = 0;
172 		if(shp->gd->ppid==1)
173 			shp->login_sh++;
174 		if(shp->login_sh >= 2)
175 			sh_onoption(shp,SH_LOGIN_SHELL);
176 		/* decide whether shell is interactive */
177 		if(!sh_isoption(shp,SH_INTERACTIVE) && !sh_isoption(shp,SH_TFLAG) && !sh_isoption(shp,SH_CFLAG) &&
178 		   sh_isoption(shp,SH_SFLAG) && tty_check(0) && tty_check(ERRIO))
179 			sh_onoption(shp,SH_INTERACTIVE);
180 		if(sh_isoption(shp,SH_INTERACTIVE))
181 		{
182 			sh_onoption(shp,SH_BGNICE);
183 			sh_onoption(shp,SH_RC);
184 		}
185 		if(!sh_isoption(shp,SH_RC) && (sh_isoption(shp,SH_BASH) && !sh_isoption(shp,SH_POSIX)
186 #if SHOPT_REMOTE
187 		   || !fstat(0, &statb) && REMOTE(statb.st_mode)
188 #endif
189 		  ))
190 			sh_onoption(shp,SH_RC);
191 		for(i=0; i<elementsof(shp->offoptions.v); i++)
192 			shp->options.v[i] &= ~shp->offoptions.v[i];
193 		if(sh_isoption(shp,SH_INTERACTIVE))
194 		{
195 #ifdef SIGXCPU
196 			signal(SIGXCPU,SIG_DFL);
197 #endif /* SIGXCPU */
198 #ifdef SIGXFSZ
199 			signal(SIGXFSZ,SIG_DFL);
200 #endif /* SIGXFSZ */
201 			sh_onoption(shp,SH_MONITOR);
202 		}
203 		job_init(shp,sh_isoption(shp,SH_LOGIN_SHELL));
204 		if(sh_isoption(shp,SH_LOGIN_SHELL))
205 		{
206 			/*	system profile	*/
207 			sh_source(shp, iop, e_sysprofile);
208 			if(!sh_isoption(shp,SH_NOUSRPROFILE) && !sh_isoption(shp,SH_PRIVILEGED))
209 			{
210 				char **files = shp->gd->login_files;
211 				while ((name = *files++) && !sh_source(shp, iop, sh_mactry(shp,name)));
212 			}
213 		}
214 		/* make sure PWD is set up correctly */
215 		path_pwd(shp,1);
216 		if(!sh_isoption(shp,SH_NOEXEC))
217 		{
218 			if(!sh_isoption(shp,SH_NOUSRPROFILE) && !sh_isoption(shp,SH_PRIVILEGED) && sh_isoption(shp,SH_RC))
219 			{
220 #if SHOPT_BASH
221 				if(sh_isoption(shp,SH_BASH) && !sh_isoption(shp,SH_POSIX))
222 				{
223 #if SHOPT_SYSRC
224 					sh_source(shp, iop, e_bash_sysrc);
225 #endif
226 					sh_source(shp, iop, shp->gd->rcfile ? shp->gd->rcfile : sh_mactry(shp,(char*)e_bash_rc));
227 				}
228 				else
229 #endif
230 				{
231 					if(name = sh_mactry(shp,nv_getval(ENVNOD)))
232 						name = *name ? strdup(name) : (char*)0;
233 #if SHOPT_SYSRC
234 					if(!name || !strmatch(name, "?(.)/./*"))
235 						sh_source(shp, iop, e_sysrc);
236 #endif
237 					if(name)
238 					{
239 						sh_source(shp, iop, name);
240 						free(name);
241 					}
242 				}
243 			}
244 			else if(sh_isoption(shp,SH_INTERACTIVE) && sh_isoption(shp,SH_PRIVILEGED))
245 				sh_source(shp, iop, e_suidprofile);
246 		}
247 		/* add enum type _Bool */
248 		i=0;
249 		if(sh_isoption(shp,SH_XTRACE))
250 		{
251 			i = 1;
252 			sh_offoption(shp,SH_XTRACE);
253 		}
254 		if(sh_isoption(shp,SH_NOEXEC))
255 		{
256 			i |= 2;
257 			sh_offoption(shp,SH_NOEXEC);
258 		}
259 		if(sh_isoption(shp,SH_VERBOSE))
260 		{
261 			i |= 4;
262 			sh_offoption(shp,SH_VERBOSE);
263 		}
264 		sh_trap(shp,"enum _Bool=(false true) ;",0);
265 		if(i&1)
266 			sh_onoption(shp,SH_XTRACE);
267 		if(i&2)
268 			sh_onoption(shp,SH_NOEXEC);
269 		if(i&4)
270 			sh_onoption(shp,SH_VERBOSE);
271 		shp->st.cmdname = error_info.id = command;
272 		sh_offstate(shp,SH_PROFILE);
273 		if(rshflag)
274 			sh_onoption(shp,SH_RESTRICTED);
275 		/* open input file if specified */
276 		if(shp->comdiv)
277 		{
278 		shell_c:
279 			iop = sfnew(NIL(Sfio_t*),shp->comdiv,strlen(shp->comdiv),0,SF_STRING|SF_READ);
280 		}
281 		else
282 		{
283 			name = error_info.id;
284 			error_info.id = shp->shname;
285 			if(sh_isoption(shp,SH_SFLAG))
286 				fdin = 0;
287 			else
288 			{
289 				char *sp;
290 				/* open stream should have been passed into shell */
291 				if(strmatch(name,e_devfdNN))
292 				{
293 #if !_WINIX
294 					char *cp;
295 					int type;
296 #endif
297 					fdin = (int)strtol(name+8, (char**)0, 10);
298 					if(fstat(fdin,&statb)<0)
299 						errormsg(SH_DICT,ERROR_system(1),e_open,name);
300 #if !_WINIX
301 					/*
302 					 * try to undo effect of solaris 2.5+
303 					 * change for argv for setuid scripts
304 					 */
305 					if(((type = sh_type(cp = av[0])) & SH_TYPE_SH) && (!(name = nv_getval(L_ARGNOD)) || !((type = sh_type(cp = name)) & SH_TYPE_SH)))
306 					{
307 						av[0] = (type & SH_TYPE_LOGIN) ? cp : path_basename(cp);
308 						/*  exec to change $0 for ps */
309 						execv(pathshell(),av);
310 						/* exec fails */
311 						shp->st.dolv[0] = av[0];
312 						fixargs(shp->st.dolv,1);
313 					}
314 #endif
315 					name = av[0];
316 					sh_offoption(shp,SH_VERBOSE);
317 					sh_offoption(shp,SH_XTRACE);
318 				}
319 				else
320 				{
321 					int isdir = 0;
322 					if((fdin=sh_open(name,O_RDONLY,0))>=0 &&(fstat(fdin,&statb)<0 || S_ISDIR(statb.st_mode)))
323 					{
324 						sh_close(fdin);
325 						isdir = 1;
326 						fdin = -1;
327 					}
328 					else
329 						shp->st.filename = path_fullname(shp,name);
330 					sp = 0;
331 					if(fdin < 0 && !strchr(name,'/'))
332 					{
333 #ifdef PATH_BFPATH
334 						if(path_absolute(shp,name,NIL(Pathcomp_t*)))
335 							sp = stkptr(shp->stk,PATH_OFFSET);
336 #else
337 							sp = path_absolute(shp,name,NIL(char*));
338 #endif
339 						if(sp)
340 						{
341 							if((fdin=sh_open(sp,O_RDONLY,0))>=0)
342 								shp->st.filename = path_fullname(shp,sp);
343 						}
344 					}
345 					if(fdin<0)
346 					{
347 						if(isdir)
348 							errno = EISDIR;
349 						 error_info.id = av[0];
350 						if(sp || errno!=ENOENT)
351 							errormsg(SH_DICT,ERROR_system(ERROR_NOEXEC),e_open,name);
352 						/* try sh -c 'name "$@"' */
353 						sh_onoption(shp,SH_CFLAG);
354 						shp->comdiv = (char*)malloc(strlen(name)+7);
355 						name = strcopy(shp->comdiv,name);
356 						if(shp->st.dolc)
357 							strcopy(name," \"$@\"");
358 						goto shell_c;
359 					}
360 					if(fdin==0)
361 						fdin = sh_iomovefd(shp,fdin);
362 				}
363 				shp->readscript = shp->shname;
364 			}
365 			error_info.id = name;
366 			shp->comdiv--;
367 #if SHOPT_ACCT
368 			sh_accinit();
369 			if(fdin != 0)
370 				sh_accbegin(error_info.id);
371 #endif	/* SHOPT_ACCT */
372 		}
373 	}
374 	else
375 	{
376 		fdin = shp->infd;
377 		fixargs(shp->st.dolv,1);
378 	}
379 	if(sh_isoption(shp,SH_INTERACTIVE))
380 		sh_onstate(shp,SH_INTERACTIVE);
381 	nv_putval(IFSNOD,(char*)e_sptbnl,NV_RDONLY);
382 	exfile(shp,iop,fdin);
383 	sh_done(shp,0);
384 	/* NOTREACHED */
385 	return(0);
386 }
387 
388 /*
389  * iop is not null when the input is a string
390  * fdin is the input file descriptor
391  */
392 
exfile(register Shell_t * shp,register Sfio_t * iop,register int fno)393 static void	exfile(register Shell_t *shp, register Sfio_t *iop,register int fno)
394 {
395 	time_t curtime;
396 	Shnode_t *t;
397 	int maxtry=IOMAXTRY, tdone=0, execflags;
398 	int states,jmpval;
399 	struct checkpt buff;
400 	sh_pushcontext(shp,&buff,SH_JMPERREXIT);
401 	/* open input stream */
402 	nv_putval(SH_PATHNAMENOD, shp->st.filename ,NV_NOFREE);
403 	if(!iop)
404 	{
405 		if(fno > 0)
406 		{
407 			int r;
408 			if(fno < 10 && ((r=sh_fcntl(fno,F_DUPFD,10))>=10))
409 			{
410 				shp->fdstatus[r] = shp->fdstatus[fno];
411 				sh_close(fno);
412 				fno = r;
413 			}
414 			fcntl(fno,F_SETFD,FD_CLOEXEC);
415 			shp->fdstatus[fno] |= IOCLEX;
416 			iop = sh_iostream((void*)shp,fno,fno);
417 		}
418 		else
419 			iop = sfstdin;
420 	}
421 	else
422 		fno = -1;
423 	shp->infd = fno;
424 	if(sh_isstate(shp,SH_INTERACTIVE))
425 	{
426 		if(nv_isnull(PS1NOD))
427 			nv_putval(PS1NOD,(shp->gd->euserid?e_stdprompt:e_supprompt),NV_RDONLY);
428 		sh_sigdone(shp);
429 		if(sh_histinit((void*)shp))
430 			sh_onoption(shp,SH_HISTORY);
431 	}
432 	else
433 	{
434 		if(!sh_isstate(shp,SH_PROFILE))
435 		{
436 			buff.mode = SH_JMPEXIT;
437 			sh_onoption(shp,SH_TRACKALL);
438 		}
439 		sh_offstate(shp,SH_INTERACTIVE);
440 		if(sh_isoption(shp,SH_MONITOR))
441 			sh_onstate(shp,SH_MONITOR);
442 		sh_offstate(shp,SH_HISTORY);
443 		sh_offoption(shp,SH_HISTORY);
444 	}
445 	states = sh_getstate(shp);
446 	jmpval = sigsetjmp(buff.buff,0);
447 	if(jmpval)
448 	{
449 		Sfio_t *top;
450 		sh_iorestore((void*)shp,0,jmpval);
451 		hist_flush(shp->gd->hist_ptr);
452 		sfsync(shp->outpool);
453 		shp->st.execbrk = shp->st.breakcnt = 0;
454 		/* check for return from profile or env file */
455 		if(sh_isstate(shp,SH_PROFILE) && (jmpval==SH_JMPFUN || jmpval==SH_JMPEXIT))
456 		{
457 			sh_setstate(shp,states);
458 			goto done;
459 		}
460 		if(!sh_isoption(shp,SH_INTERACTIVE) || sh_isstate(shp,SH_FORKED) || (jmpval > SH_JMPERREXIT && job_close(shp) >=0))
461 		{
462 			sh_offstate(shp,SH_INTERACTIVE);
463 			sh_offstate(shp,SH_MONITOR);
464 			goto done;
465 		}
466 		exitset(shp);
467 		/* skip over remaining input */
468 		if(top = fcfile())
469 		{
470 			while(fcget()>0);
471 			fcclose();
472 			while(top=sfstack(iop,SF_POPSTACK))
473 				sfclose(top);
474 		}
475 		/* make sure that we own the terminal */
476 #ifdef SIGTSTP
477 		tcsetpgrp(job.fd,shp->gd->pid);
478 #endif /* SIGTSTP */
479 	}
480 	/* error return here */
481 	sfclrerr(iop);
482 	sh_setstate(shp,states);
483 	shp->st.optindex = 1;
484 	opt_info.offset = 0;
485 	shp->st.loopcnt = 0;
486 	shp->trapnote = 0;
487 	shp->intrap = 0;
488 	error_info.line = 1;
489 	shp->inlineno = 1;
490 	shp->binscript = 0;
491 	if(sfeof(iop))
492 		goto eof_or_error;
493 	/* command loop */
494 	while(1)
495 	{
496 		shp->nextprompt = 1;
497 		sh_freeup(shp);
498 		stkset(shp->stk,NIL(char*),0);
499 		sh_offstate(shp,SH_STOPOK);
500 		sh_offstate(shp,SH_ERREXIT);
501 		sh_offstate(shp,SH_VERBOSE);
502 		sh_offstate(shp,SH_TIMING);
503 		sh_offstate(shp,SH_GRACE);
504 		sh_offstate(shp,SH_TTYWAIT);
505 		if(sh_isoption(shp,SH_VERBOSE))
506 			sh_onstate(shp,SH_VERBOSE);
507 		sh_onstate(shp,SH_ERREXIT);
508 		/* -eim  flags don't apply to profiles */
509 		if(sh_isstate(shp,SH_PROFILE))
510 		{
511 			sh_offstate(shp,SH_INTERACTIVE);
512 			sh_offstate(shp,SH_ERREXIT);
513 			sh_offstate(shp,SH_MONITOR);
514 		}
515 		if(sh_isstate(shp,SH_INTERACTIVE) && !tdone)
516 		{
517 			register char *mail;
518 #ifdef JOBS
519 			sh_offstate(shp,SH_MONITOR);
520 			if(sh_isoption(shp,SH_MONITOR))
521 				sh_onstate(shp,SH_MONITOR);
522 			if(job.pwlist)
523 			{
524 				job_walk(shp,sfstderr,job_list,JOB_NFLAG,(char**)0);
525 				job_wait((pid_t)0);
526 			}
527 #endif	/* JOBS */
528 			if((mail=nv_getval(MAILPNOD)) || (mail=nv_getval(MAILNOD)))
529 			{
530 				time(&curtime);
531 				if ((curtime - mailtime) >= sh_mailchk)
532 				{
533 					chkmail(shp,mail);
534 					mailtime = curtime;
535 				}
536 			}
537 			if(shp->gd->hist_ptr)
538 				hist_eof(shp->gd->hist_ptr);
539 			/* sets timeout for command entry */
540 			shp->timeout = shp->st.tmout;
541 #if SHOPT_TIMEOUT
542 			if(shp->timeout <= 0 || shp->timeout > SHOPT_TIMEOUT)
543 				shp->timeout = SHOPT_TIMEOUT;
544 #endif /* SHOPT_TIMEOUT */
545 			shp->inlineno = 1;
546 			error_info.line = 1;
547 			shp->trapnote = 0;
548 			if(buff.mode == SH_JMPEXIT)
549 			{
550 				buff.mode = SH_JMPERREXIT;
551 #ifdef DEBUG
552 				errormsg(SH_DICT,ERROR_warn(0),"%d: mode changed to JMP_EXIT",getpid());
553 #endif
554 			}
555 		}
556 		errno = 0;
557 		if(tdone || !sfreserve(iop,0,0))
558 		{
559 		eof_or_error:
560 			if(sh_isstate(shp,SH_INTERACTIVE) && !sferror(iop))
561 			{
562 				if(--maxtry>0 && sh_isoption(shp,SH_IGNOREEOF) &&
563 					 !sferror(sfstderr) && (shp->fdstatus[fno]&IOTTY))
564 				{
565 					sfclrerr(iop);
566 					errormsg(SH_DICT,0,e_logout);
567 					continue;
568 				}
569 				else if(job_close(shp)<0)
570 					continue;
571 			}
572 			if(errno==0 && sferror(iop) && --maxtry>0)
573 			{
574 				sfclrlock(iop);
575 				sfclrerr(iop);
576 				continue;
577 			}
578 			goto done;
579 		}
580 		shp->exitval = shp->savexit;
581 		maxtry = IOMAXTRY;
582 		if(sh_isstate(shp,SH_INTERACTIVE) && shp->gd->hist_ptr)
583 		{
584 			job_wait((pid_t)0);
585 			hist_eof(shp->gd->hist_ptr);
586 			sfsync(sfstderr);
587 		}
588 		if(sh_isoption(shp,SH_HISTORY))
589 			sh_onstate(shp,SH_HISTORY);
590 		job.waitall = job.curpgid = 0;
591 		error_info.flags |= ERROR_INTERACTIVE;
592 		t = (Shnode_t*)sh_parse(shp,iop,0);
593 		if(!sh_isstate(shp,SH_INTERACTIVE) && !sh_isoption(shp,SH_CFLAG))
594 			error_info.flags &= ~ERROR_INTERACTIVE;
595 		shp->readscript = 0;
596 		if(sh_isstate(shp,SH_INTERACTIVE) && shp->gd->hist_ptr)
597 			hist_flush(shp->gd->hist_ptr);
598 		sh_offstate(shp,SH_HISTORY);
599 		if(t)
600 		{
601 			execflags = sh_state(SH_ERREXIT)|sh_state(SH_INTERACTIVE);
602 			/* The last command may not have to fork */
603 			if(!sh_isstate(shp,SH_PROFILE) && sh_isoption(shp,SH_CFLAG) &&
604 				(fno<0 || !(shp->fdstatus[fno]&(IOTTY|IONOSEEK)))
605 				&& !sfreserve(iop,0,0))
606 			{
607 					execflags |= sh_state(SH_NOFORK);
608 			}
609 			shp->st.execbrk = 0;
610 			sh_exec(shp,t,execflags);
611 			if(shp->forked)
612 			{
613 				sh_offstate(shp,SH_INTERACTIVE);
614 				goto done;
615 			}
616 			/* This is for sh -t */
617 			if(sh_isoption(shp,SH_TFLAG) && !sh_isstate(shp,SH_PROFILE))
618 				tdone++;
619 		}
620 	}
621 done:
622 	sh_popcontext(shp,&buff);
623 	if(sh_isstate(shp,SH_INTERACTIVE))
624 	{
625 		sfputc(sfstderr,'\n');
626 		job_close(shp);
627 	}
628 	if(jmpval == SH_JMPSCRIPT)
629 		siglongjmp(*shp->jmplist,jmpval);
630 	else if(jmpval == SH_JMPEXIT)
631 		sh_done(shp,0);
632 	if(fno>0)
633 		sh_close(fno);
634 	if(shp->st.filename)
635 		free((void*)shp->st.filename);
636 	shp->st.filename = 0;
637 }
638 
639 
640 /* prints out messages if files in list have been modified since last call */
chkmail(Shell_t * shp,char * files)641 static void chkmail(Shell_t *shp, char *files)
642 {
643 	register char *cp,*sp,*qp;
644 	register char save;
645 	struct argnod *arglist=0;
646 	int	offset = stktell(shp->stk);
647 	char 	*savstak=stkptr(shp->stk,0);
648 	struct stat	statb;
649 	if(*(cp=files) == 0)
650 		return;
651 	sp = cp;
652 	do
653 	{
654 		/* skip to : or end of string saving first '?' */
655 		for(qp=0;*sp && *sp != ':';sp++)
656 			if((*sp == '?' || *sp=='%') && qp == 0)
657 				qp = sp;
658 		save = *sp;
659 		*sp = 0;
660 		/* change '?' to end-of-string */
661 		if(qp)
662 			*qp = 0;
663 		do
664 		{
665 			/* see if time has been modified since last checked
666 			 * and the access time <= the modification time
667 			 */
668 			if(stat(cp,&statb) >= 0 && statb.st_mtime >= mailtime
669 				&& statb.st_atime <= statb.st_mtime)
670 			{
671 				/* check for directory */
672 				if(!arglist && S_ISDIR(statb.st_mode))
673 				{
674 					/* generate list of directory entries */
675 					path_complete(shp,cp,"/*",&arglist);
676 				}
677 				else
678 				{
679 					/*
680 					 * If the file has shrunk,
681 					 * or if the size is zero
682 					 * then don't print anything
683 					 */
684 					if(statb.st_size &&
685 						(  statb.st_ino != lastmail.st_ino
686 						|| statb.st_dev != lastmail.st_dev
687 						|| statb.st_size > lastmail.st_size))
688 					{
689 						/* save and restore $_ */
690 						char *save = shp->lastarg;
691 						shp->lastarg = cp;
692 						errormsg(SH_DICT,0,sh_mactry(shp,qp?qp+1:(char*)e_mailmsg));
693 						shp->lastarg = save;
694 					}
695 					lastmail = statb;
696 					break;
697 				}
698 			}
699 			if(arglist)
700 			{
701 				cp = arglist->argval;
702 				arglist = arglist->argchn.ap;
703 			}
704 			else
705 				cp = 0;
706 		}
707 		while(cp);
708 		if(qp)
709 			*qp = '?';
710 		*sp++ = save;
711 		cp = sp;
712 	}
713 	while(save);
714 	stkset(shp->stk,savstak,offset);
715 }
716 
717 #undef EXECARGS
718 #undef PSTAT
719 #if defined(_hdr_execargs) && defined(pdp11)
720 #   include	<execargs.h>
721 #   define EXECARGS	1
722 #endif
723 
724 #if defined(_lib_pstat) && defined(_sys_pstat)
725 #   include	<sys/pstat.h>
726 #   define PSTAT	1
727 #endif
728 
729 #if defined(_lib_fork) && !defined(_NEXT_SOURCE)
730 /*
731  * fix up command line for ps command
732  * mode is 0 for initialization
733  */
fixargs(char ** argv,int mode)734 static void fixargs(char **argv, int mode)
735 {
736 #if EXECARGS
737 	*execargs=(char *)argv;
738 #else
739 	static char *buff;
740 	static size_t command_len;
741 	register char *cp;
742 	int offset=0;
743 	size_t size;
744 #   ifdef PSTAT
745 	union pstun un;
746 	if(mode==0)
747 	{
748 		struct pst_static st;
749 		un.pst_static = &st;
750 		if(pstat(PSTAT_STATIC, un, sizeof(struct pst_static), 1, 0)<0)
751 			return;
752 		command_len = st.command_length;
753 		return;
754 	}
755 	stakseek(command_len+2);
756 	buff = stakseek(0);
757 #   else
758 	if(mode==0)
759 	{
760 		buff = argv[0];
761 		while(cp = *argv++)
762 			command_len += strlen(cp)+1;
763 		if(environ && *environ==buff+command_len)
764 		{
765 			for(argv=environ; cp = *argv; cp++)
766 			{
767 				if(command_len > CMD_LENGTH)
768 				{
769 					command_len = CMD_LENGTH;
770 					break;
771 				}
772 				*argv++ = strdup(cp);
773 				command_len += strlen(cp)+1;
774 			}
775 		}
776 		command_len -= 1;
777 		return;
778 	}
779 #   endif /* PSTAT */
780 	if(command_len==0)
781 		return;
782 	while((cp = *argv++) && offset < command_len)
783 	{
784 		if(offset + (size=strlen(cp)) >= command_len)
785 			size = command_len - offset;
786 		memcpy(buff+offset,cp,size);
787 		offset += size;
788 		buff[offset++] = ' ';
789 	}
790 	memset(&buff[offset - 1], 0, command_len - offset + 1);
791 #   ifdef PSTAT
792 	un.pst_command = stakptr(0);
793 	pstat(PSTAT_SETCMD,un,0,0,0);
794 #   endif /* PSTAT */
795 #endif /* EXECARGS */
796 }
797 #endif /* _lib_fork */
798