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