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