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 */
55static void	exfile(Shell_t*, Sfio_t*,int);
56static 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
67static struct stat lastmail;
68static time_t	mailtime;
69static char	beenhere = 0;
70
71#ifdef _lib_sigvec
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
94int 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
121int 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
364static 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	}
592done:
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 */
612static 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 */
705static 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