xref: /illumos-gate/usr/src/contrib/ast/src/cmd/ksh93/sh/path.c (revision 28ab0ca48b3e331cbbb231b1c8325f9f24f9af95)
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  * David Korn
23  * AT&T Labs
24  *
25  */
26 /*
27  * Copyright (c) 2007, 2011, Oracle and/or its affiliates. All rights reserved.
28  */
29 
30 #include	"defs.h"
31 #include	<fcin.h>
32 #include	<ls.h>
33 #include	<nval.h>
34 #include	"variables.h"
35 #include	"path.h"
36 #include	"io.h"
37 #include	"jobs.h"
38 #include	"history.h"
39 #include	"test.h"
40 #include	"FEATURE/dynamic"
41 #include	"FEATURE/externs"
42 #if SHOPT_PFSH
43 #   ifdef _hdr_exec_attr
44 #	include	<exec_attr.h>
45 #   endif
46 #   if     _lib_vfork
47 #	include     <ast_vfork.h>
48 #   else
49 #	define vfork()      fork()
50 #   endif
51 #endif
52 
53 #define RW_ALL	(S_IRUSR|S_IRGRP|S_IROTH|S_IWUSR|S_IWGRP|S_IWOTH)
54 #define LIBCMD	"cmd"
55 
56 
57 static int		canexecute(Shell_t*,char*,int);
58 static void		funload(Shell_t*,int,const char*);
59 static void		exscript(Shell_t*,char*, char*[], char**);
60 static int		path_chkpaths(Shell_t*,Pathcomp_t*,Pathcomp_t*,Pathcomp_t*,int);
61 static void		path_checkdup(Shell_t *shp,register Pathcomp_t*);
62 
63 static const char	*std_path;
64 
65 static int onstdpath(const char *name)
66 {
67 	register const char *cp = std_path, *sp;
68 	if(cp)
69 		while(*cp)
70 		{
71 			for(sp=name; *sp && (*cp == *sp); sp++,cp++);
72 			if(*sp==0 && (*cp==0 || *cp==':'))
73 				return(1);
74 			while(*cp && *cp++!=':');
75 		}
76 	return(0);
77 }
78 
79 #if SHOPT_PFSH
80 int path_xattr(Shell_t *shp, const char *path, char *rpath)
81 {
82 	char  resolvedpath[PATH_MAX + 1];
83 	if (shp->gd->user && *shp->gd->user)
84 	{
85 		execattr_t *pf;
86 		if(!rpath)
87 			rpath = resolvedpath;
88 		if (!realpath(path, resolvedpath))
89 			return -1;
90 		if(pf=getexecuser(shp->gd->user, KV_COMMAND, resolvedpath, GET_ONE))
91 		{
92 			if (!pf->attr || pf->attr->length == 0)
93 			{
94 				free_execattr(pf);
95 				return(0);
96 			}
97 			free_execattr(pf);
98 			return(1);
99 		}
100 	}
101 	errno = ENOENT;
102 	return(-1);
103 }
104 #endif /* SHOPT_PFSH */
105 
106 static pid_t path_pfexecve(Shell_t *shp,const char *path, char *argv[],char *const envp[],int spawn)
107 {
108 #if SHOPT_PFSH
109 	char  resolvedpath[PATH_MAX + 1];
110 	pid_t	pid;
111 	if(spawn)
112 	{
113 		while((pid = vfork()) < 0)
114 			_sh_fork(shp,pid, 0, (int*)0);
115 		if(pid)
116 			return(pid);
117 	}
118 	if(!sh_isoption(SH_PFSH))
119 		return(execve(path, argv, envp));
120 	/* Solaris implements realpath(3C) using the resolvepath(2) */
121 	/* system call so we can save us to call access(2) first */
122 
123 	/* we can exec the command directly instead of via pfexec(1) if */
124 	/* there is a matching entry without attributes in exec_attr(4) */
125 	if(!path_xattr(shp,path,resolvedpath))
126 		return(execve(path, argv, envp));
127 	--argv;
128 	argv[0] = argv[1];
129 	argv[1] = resolvedpath;
130 	return(execve("/usr/bin/pfexec", argv, envp));
131 #else
132 	return(execve(path, argv, envp));
133 #endif
134 }
135 
136 
137 static pid_t _spawnveg(Shell_t *shp,const char *path, char* const argv[], char* const envp[], pid_t pgid)
138 {
139 	pid_t pid;
140 	while(1)
141 	{
142 		sh_stats(STAT_SPAWN);
143 		pid = spawnveg(path,argv,envp,pgid);
144 		if(pid>=0 || errno!=EAGAIN)
145 			break;
146 	}
147 	return(pid);
148 }
149 
150 /*
151  * used with command -x to run the command in multiple passes
152  * spawn is non-zero when invoked via spawn
153  * the exitval is set to the maximum for each execution
154  */
155 static pid_t path_xargs(Shell_t *shp,const char *path, char *argv[],char *const envp[], int spawn)
156 {
157 	register char *cp, **av, **xv;
158 	char **avlast= &argv[shp->xargmax], **saveargs=0;
159 	char *const *ev;
160 	long size, left;
161 	int nlast=1,n,exitval=0;
162 	pid_t pid;
163 	if(shp->xargmin < 0)
164 		return((pid_t)-1);
165 	size = shp->gd->lim.arg_max-1024;
166 	for(ev=envp; cp= *ev; ev++)
167 		size -= strlen(cp)-1;
168 	for(av=argv; (cp= *av) && av< &argv[shp->xargmin]; av++)
169 		size -= strlen(cp)-1;
170 	for(av=avlast; cp= *av; av++,nlast++)
171 		size -= strlen(cp)-1;
172 	av =  &argv[shp->xargmin];
173 	if(!spawn)
174 		job_clear();
175 	shp->exitval = 0;
176 	while(av<avlast)
177 	{
178 		for(xv=av,left=size; left>0 && av<avlast;)
179 			left -= strlen(*av++)+1;
180 		/* leave at least two for last */
181 		if(left<0 && (avlast-av)<2)
182 			av--;
183 		if(xv==&argv[shp->xargmin])
184 		{
185 			n = nlast*sizeof(char*);
186 			saveargs = (char**)malloc(n);
187 			memcpy((void*)saveargs, (void*)av, n);
188 			memcpy((void*)av,(void*)avlast,n);
189 		}
190 		else
191 		{
192 			for(n=shp->xargmin; xv < av; xv++)
193 				argv[n++] = *xv;
194 			for(xv=avlast; cp=  *xv; xv++)
195 				argv[n++] = cp;
196 			argv[n] = 0;
197 		}
198 		if(saveargs || av<avlast || (exitval && !spawn))
199 		{
200 			if((pid=_spawnveg(shp,path,argv,envp,0)) < 0)
201 				return(-1);
202 			job_post(shp,pid,0);
203 			job_wait(pid);
204 			if(shp->exitval>exitval)
205 				exitval = shp->exitval;
206 			if(saveargs)
207 			{
208 				memcpy((void*)av,saveargs,n);
209 				free((void*)saveargs);
210 				saveargs = 0;
211 			}
212 		}
213 		else if(spawn && !sh_isoption(SH_PFSH))
214 		{
215 			shp->xargexit = exitval;
216 			if(saveargs)
217 				free((void*)saveargs);
218 			return(_spawnveg(shp,path,argv,envp,spawn>>1));
219 		}
220 		else
221 		{
222 			if(saveargs)
223 				free((void*)saveargs);
224 			return(path_pfexecve(shp,path,argv,envp,spawn));
225 		}
226 	}
227 	if(!spawn)
228 		exit(exitval);
229 	return((pid_t)-1);
230 }
231 
232 /*
233  * make sure PWD is set up correctly
234  * Return the present working directory
235  * Invokes getcwd() if flag==0 and if necessary
236  * Sets the PWD variable to this value
237  */
238 char *path_pwd(Shell_t *shp,int flag)
239 {
240 	register char *cp;
241 	register char *dfault = (char*)e_dot;
242 	register int count = 0;
243 	if(shp->pwd)
244 		return((char*)shp->pwd);
245 	while(1)
246 	{
247 		/* try from lowest to highest */
248 		switch(count++)
249 		{
250 			case 0:
251 				cp = nv_getval(PWDNOD);
252 				break;
253 			case 1:
254 				cp = nv_getval(HOME);
255 				break;
256 			case 2:
257 				cp = "/";
258 				break;
259 			case 3:
260 				cp = (char*)e_crondir;
261 				if(flag) /* skip next case when non-zero flag */
262 					++count;
263 				break;
264 			case 4:
265 			{
266 				if(cp=getcwd(NIL(char*),0))
267 				{
268 					nv_offattr(PWDNOD,NV_NOFREE);
269 					_nv_unset(PWDNOD,0);
270 					PWDNOD->nvalue.cp = cp;
271 					goto skip;
272 				}
273 				break;
274 			}
275 			case 5:
276 				return(dfault);
277 		}
278 		if(cp && *cp=='/' && test_inode(cp,e_dot))
279 			break;
280 	}
281 	if(count>1)
282 	{
283 		nv_offattr(PWDNOD,NV_NOFREE);
284 		nv_putval(PWDNOD,cp,NV_RDONLY);
285 	}
286 skip:
287 	nv_onattr(PWDNOD,NV_NOFREE|NV_EXPORT);
288 	shp->pwd = (char*)(PWDNOD->nvalue.cp);
289 	return(cp);
290 }
291 
292 /*
293  * delete current Pathcomp_t structure
294  */
295 void  path_delete(Pathcomp_t *first)
296 {
297 	register Pathcomp_t *pp=first, *old=0, *ppnext;
298 	while(pp)
299 	{
300 		ppnext = pp->next;
301 		if(--pp->refcount<=0)
302 		{
303 			if(pp->lib)
304 				free((void*)pp->lib);
305 			if(pp->bbuf)
306 				free((void*)pp->bbuf);
307 			free((void*)pp);
308 			if(old)
309 				old->next = ppnext;
310 		}
311 		else
312 			old = pp;
313 		pp = ppnext;
314 	}
315 }
316 
317 /*
318  * returns library variable from .paths
319  * The value might be returned on the stack overwriting path
320  */
321 static char *path_lib(Shell_t *shp,Pathcomp_t *pp, char *path)
322 {
323 	register char *last = strrchr(path,'/');
324 	register int r;
325 	struct stat statb;
326 	if(last)
327 		*last = 0;
328 	else
329 		path = ".";
330 	r = stat(path,&statb);
331 	if(last)
332 		*last = '/';
333 	if(r>=0)
334 	{
335 		Pathcomp_t pcomp;
336 		char save[8];
337 		for( ;pp; pp=pp->next)
338 		{
339 			path_checkdup(shp,pp);
340 			if(pp->ino==statb.st_ino && pp->dev==statb.st_dev && pp->mtime==statb.st_mtime)
341 				return(pp->lib);
342 		}
343 		pcomp.len = 0;
344 		if(last)
345 			pcomp.len = last-path;
346 		memcpy((void*)save, (void*)stakptr(PATH_OFFSET+pcomp.len),sizeof(save));
347 		if(path_chkpaths(shp,(Pathcomp_t*)0,(Pathcomp_t*)0,&pcomp,PATH_OFFSET))
348 			return(stakfreeze(1));
349 		memcpy((void*)stakptr(PATH_OFFSET+pcomp.len),(void*)save,sizeof(save));
350 	}
351 	return(0);
352 }
353 
354 #if 0
355 void path_dump(register Pathcomp_t *pp)
356 {
357 	sfprintf(sfstderr,"dump\n");
358 	while(pp)
359 	{
360 		sfprintf(sfstderr,"pp=%x dev=%d ino=%d len=%d flags=%o name=%.*s\n",
361 			pp,pp->dev,pp->ino,pp->len,pp->flags,pp->len,pp->name);
362 		pp = pp->next;
363 	}
364 }
365 #endif
366 
367 /*
368  * check for duplicate directories on PATH
369  */
370 static void path_checkdup(Shell_t *shp,register Pathcomp_t *pp)
371 {
372 	register char		*name = pp->name;
373 	register Pathcomp_t	*oldpp,*first;
374 	register int		flag=0;
375 	struct stat 		statb;
376 	if(stat(name,&statb)<0 || !S_ISDIR(statb.st_mode))
377 	{
378 		pp->flags |= PATH_SKIP;
379 		pp->dev = *name=='/';
380 		return;
381 	}
382 	pp->mtime = statb.st_mtime;
383 	pp->ino = statb.st_ino;
384 	pp->dev = statb.st_dev;
385 	if(*name=='/' && onstdpath(name))
386 		flag = PATH_STD_DIR;
387 	first = (pp->flags&PATH_CDPATH)?(Pathcomp_t*)shp->cdpathlist:path_get(shp,"");
388 	for(oldpp=first; oldpp && oldpp!=pp; oldpp=oldpp->next)
389 	{
390 		if(pp->ino==oldpp->ino && pp->dev==oldpp->dev && pp->mtime==oldpp->mtime)
391 		{
392 			flag |= PATH_SKIP;
393 			break;
394 		}
395 	}
396 	pp->flags |= flag;
397 	if(((pp->flags&(PATH_PATH|PATH_SKIP))==PATH_PATH))
398 	{
399 		int offset = staktell();
400 		stakputs(name);
401 		path_chkpaths(shp,first,0,pp,offset);
402 		stakseek(offset);
403 	}
404 }
405 
406 /*
407  * write the next path to search on the current stack
408  * if last is given, all paths that come before <last> are skipped
409  * the next pathcomp is returned.
410  */
411 Pathcomp_t *path_nextcomp(Shell_t *shp,register Pathcomp_t *pp, const char *name, Pathcomp_t *last)
412 {
413 	Pathcomp_t	*ppnext;
414 	stakseek(PATH_OFFSET);
415 	if(*name=='/')
416 		pp = 0;
417 	else
418 	{
419 		for(;pp && pp!=last;pp=ppnext)
420 		{
421 			ppnext = pp->next;
422 			if(!pp->dev && !pp->ino)
423 				path_checkdup(shp,pp);
424 			if(pp->flags&PATH_SKIP)
425 				return(ppnext);
426 			if(!last || *pp->name!='/')
427 				break;
428 		}
429 		if(!pp)		/* this should not happen */
430 			pp = last;
431 	}
432 	if(pp && (pp->name[0]!='.' || pp->name[1]))
433 	{
434 		if(*pp->name!='/')
435 		{
436 			stakputs(path_pwd(shp,1));
437 			if(*stakptr(staktell()-1)!='/')
438 				stakputc('/');
439 		}
440 		stakwrite(pp->name,pp->len);
441 		if(pp->name[pp->len-1]!='/')
442 			stakputc('/');
443 	}
444 	stakputs(name);
445 	stakputc(0);
446 	while(pp && pp!=last && (pp=pp->next))
447 	{
448 		if(!(pp->flags&PATH_SKIP))
449 			return(pp);
450 	}
451 	return((Pathcomp_t*)0);
452 }
453 
454 static Pathcomp_t* defpath_init(Shell_t *shp)
455 {
456 	Pathcomp_t *pp = (void*)path_addpath(shp,(Pathcomp_t*)0,(std_path),PATH_PATH);
457 	return(pp);
458 }
459 
460 static void path_init(Shell_t *shp)
461 {
462 	const char *val;
463 	Pathcomp_t *pp;
464 	if(!std_path && !(std_path=astconf("PATH",NIL(char*),NIL(char*))))
465 		std_path = e_defpath;
466 	if(val=sh_scoped(shp,(PATHNOD))->nvalue.cp)
467 	{
468 		shp->pathlist = pp = (void*)path_addpath(shp,(Pathcomp_t*)shp->pathlist,val,PATH_PATH);
469 	}
470 	else
471 	{
472 		if(!(pp=(Pathcomp_t*)shp->defpathlist))
473 			pp = defpath_init(shp);
474 		shp->pathlist = (void*)path_dup(pp);
475 	}
476 	if(val=sh_scoped(shp,(FPATHNOD))->nvalue.cp)
477 	{
478 		pp = (void*)path_addpath(shp,(Pathcomp_t*)shp->pathlist,val,PATH_FPATH);
479 	}
480 }
481 
482 /*
483  * returns that pathlist to search
484  */
485 Pathcomp_t *path_get(register Shell_t *shp,register const char *name)
486 {
487 	register Pathcomp_t *pp=0;
488 	if(*name && strchr(name,'/'))
489 		return(0);
490 	if(!sh_isstate(SH_DEFPATH))
491 	{
492 		if(!shp->pathlist)
493 			path_init(shp);
494 		pp = (Pathcomp_t*)shp->pathlist;
495 	}
496 	if(!pp && (!(sh_scoped(shp,PATHNOD)->nvalue.cp)) || sh_isstate(SH_DEFPATH))
497 	{
498 		if(!(pp=(Pathcomp_t*)shp->defpathlist))
499 			pp = defpath_init(shp);
500 	}
501 	return(pp);
502 }
503 
504 /*
505  * open file corresponding to name using path give by <pp>
506  */
507 static int	path_opentype(Shell_t *shp,const char *name, register Pathcomp_t *pp, int fun)
508 {
509 	register int fd= -1;
510 	struct stat statb;
511 	Pathcomp_t *oldpp;
512 	if(!pp && !shp->pathlist)
513 		path_init(shp);
514 	if(!fun && strchr(name,'/'))
515 	{
516 		if(sh_isoption(SH_RESTRICTED))
517 			errormsg(SH_DICT,ERROR_exit(1),e_restricted,name);
518 	}
519 	do
520 	{
521 		pp = path_nextcomp(shp,oldpp=pp,name,0);
522 		while(oldpp && (oldpp->flags&PATH_SKIP))
523 			oldpp = oldpp->next;
524 		if(fun && (!oldpp || !(oldpp->flags&PATH_FPATH)))
525 			continue;
526 		if((fd = sh_open(path_relative(shp,stakptr(PATH_OFFSET)),O_RDONLY,0)) >= 0)
527 		{
528 			if(fstat(fd,&statb)<0 || S_ISDIR(statb.st_mode))
529 			{
530 				errno = EISDIR;
531 				sh_close(fd);
532 				fd = -1;
533 			}
534 		}
535 	}
536 	while( fd<0 && pp);
537 	if(fd>=0 && (fd = sh_iomovefd(fd)) > 0)
538 	{
539 		fcntl(fd,F_SETFD,FD_CLOEXEC);
540 		VALIDATE_FD(shp, fd);
541 		shp->fdstatus[fd] |= IOCLEX;
542 	}
543 	return(fd);
544 }
545 
546 /*
547  * open file corresponding to name using path give by <pp>
548  */
549 int	path_open(Shell_t *shp,const char *name, register Pathcomp_t *pp)
550 {
551 	return(path_opentype(shp,name,pp,0));
552 }
553 
554 /*
555  * given a pathname return the base name
556  */
557 
558 char	*path_basename(register const char *name)
559 {
560 	register const char *start = name;
561 	while (*name)
562 		if ((*name++ == '/') && *name)	/* don't trim trailing / */
563 			start = name;
564 	return ((char*)start);
565 }
566 
567 char *path_fullname(Shell_t *shp,const char *name)
568 {
569 	int len=strlen(name)+1,dirlen=0;
570 	char *path,*pwd;
571 	if(*name!='/')
572 	{
573 		pwd = path_pwd(shp,1);
574 		dirlen = strlen(pwd)+1;
575 	}
576 	path = (char*)malloc(len+dirlen);
577 	if(dirlen)
578 	{
579 		memcpy((void*)path,(void*)pwd,dirlen);
580 		path[dirlen-1] = '/';
581 	}
582 	memcpy((void*)&path[dirlen],(void*)name,len);
583 	pathcanon(path,0);
584 	return(path);
585 }
586 
587 /*
588  * load functions from file <fno>
589  */
590 static void funload(Shell_t *shp,int fno, const char *name)
591 {
592 	char		*pname,*oldname=shp->st.filename, buff[IOBSIZE+1];
593 	Namval_t	*np;
594 	struct Ufunction *rp,*rpfirst;
595 	int		 savestates = sh_getstate(), oldload=shp->funload;
596 	pname = path_fullname(shp,stakptr(PATH_OFFSET));
597 	if(shp->fpathdict && (rp = dtmatch(shp->fpathdict,(void*)pname)))
598 	{
599 		Dt_t	*funtree = sh_subfuntree(1);
600 		while(1)
601 		{
602 			rpfirst = dtprev(shp->fpathdict,rp);
603 			if(!rpfirst || strcmp(pname,rpfirst->fname))
604 				break;
605 			rp = rpfirst;
606 		}
607 		do
608 		{
609 			if((np = dtsearch(funtree,rp->np)) && is_afunction(np))
610 			{
611 				if(np->nvalue.rp)
612 					np->nvalue.rp->fdict = 0;
613 				nv_delete(np,funtree,NV_NOFREE);
614 			}
615 			dtinsert(funtree,rp->np);
616 			rp->fdict = funtree;
617 		}
618 		while((rp=dtnext(shp->fpathdict,rp)) && strcmp(pname,rp->fname)==0);
619 		sh_close(fno);
620 		return;
621 	}
622 	sh_onstate(SH_NOLOG);
623 	sh_onstate(SH_NOALIAS);
624 	shp->readscript = (char*)name;
625 	shp->st.filename = pname;
626 	shp->funload = 1;
627 	error_info.line = 0;
628 	sh_eval(sfnew(NIL(Sfio_t*),buff,IOBSIZE,fno,SF_READ),SH_FUNEVAL);
629 	sh_close(fno);
630 	shp->readscript = 0;
631 #if SHOPT_NAMESPACE
632 	if(shp->namespace)
633 		np = sh_fsearch(shp,name,0);
634 	else
635 #endif /* SHOPT_NAMESPACE */
636 		np = nv_search(name,shp->fun_tree,0);
637 	if(!np || !np->nvalue.ip)
638 		pname = stakcopy(shp->st.filename);
639 	else
640 		pname = 0;
641 	free((void*)shp->st.filename);
642 	shp->funload = oldload;
643 	shp->st.filename = oldname;
644 	sh_setstate(savestates);
645 	if(pname)
646 		errormsg(SH_DICT,ERROR_exit(ERROR_NOEXEC),e_funload,name,pname);
647 }
648 
649 /*
650  * do a path search and track alias if requested
651  * if flag is 0, or if name not found, then try autoloading function
652  * if flag==2 or 3, returns 1 if name found on FPATH
653  * if flag==3 no tracked alias will be set
654  * returns 1, if function was autoloaded.
655  * If oldpp is not NULL, it will contain a pointer to the path component
656  *    where it was found.
657  */
658 
659 int	path_search(Shell_t *shp,register const char *name,Pathcomp_t **oldpp, int flag)
660 {
661 	register Namval_t *np;
662 	register int fno;
663 	Pathcomp_t *pp=0;
664 	if(name && strchr(name,'/'))
665 	{
666 		stakseek(PATH_OFFSET);
667 		stakputs(name);
668 		if(canexecute(shp,stakptr(PATH_OFFSET),0)<0)
669 		{
670 			*stakptr(PATH_OFFSET) = 0;
671 			return(0);
672 		}
673 		if(*name=='/')
674 			return(1);
675 		stakseek(PATH_OFFSET);
676 		stakputs(path_pwd(shp,1));
677 		stakputc('/');
678 		stakputs(name);
679 		stakputc(0);
680 		return(0);
681 	}
682 	if(sh_isstate(SH_DEFPATH))
683 	{
684 		if(!shp->defpathlist)
685 			defpath_init(shp);
686 	}
687 	else if(!shp->pathlist)
688 		path_init(shp);
689 	if(flag)
690 	{
691 		if(!(flag&1) && (np=nv_search(name,shp->track_tree,0)) && !nv_isattr(np,NV_NOALIAS) && (pp=(Pathcomp_t*)np->nvalue.cp))
692 		{
693 			stakseek(PATH_OFFSET);
694 			path_nextcomp(shp,pp,name,pp);
695 			if(oldpp)
696 				*oldpp = pp;
697 			stakputc(0);
698 			return(0);
699 		}
700 		pp = path_absolute(shp,name,oldpp?*oldpp:NIL(Pathcomp_t*));
701 		if(oldpp)
702 			*oldpp = pp;
703 		if(!pp && (np=nv_search(name,shp->fun_tree,0))&&np->nvalue.ip)
704 			return(1);
705 		if(!pp)
706 			*stakptr(PATH_OFFSET) = 0;
707 	}
708 	if(flag==0 || !pp || (pp->flags&PATH_FPATH))
709 	{
710 		if(!pp)
711 			pp=sh_isstate(SH_DEFPATH)?shp->defpathlist:shp->pathlist;
712 		if(pp && strmatch(name,e_alphanum)  && (fno=path_opentype(shp,name,pp,1))>=0)
713 		{
714 			if(flag==2)
715 			{
716 				sh_close(fno);
717 				return(1);
718 			}
719 			funload(shp,fno,name);
720 			return(1);
721 		}
722 		*stakptr(PATH_OFFSET) = 0;
723 		return(0);
724 	}
725 	else if(pp && !sh_isstate(SH_DEFPATH) && *name!='/' && flag<3)
726 	{
727 		if(np=nv_search(name,shp->track_tree,NV_ADD))
728 			path_alias(np,pp);
729 	}
730 	return(0);
731 }
732 
733 /*
734  * do a path search and find the full pathname of file name
735  */
736 Pathcomp_t *path_absolute(Shell_t *shp,register const char *name, Pathcomp_t *pp)
737 {
738 	register int	f,isfun;
739 	int		noexec=0;
740 	Pathcomp_t	*oldpp;
741 	Namval_t	*np;
742 	char		*cp;
743 	char		*bp;
744 	shp->path_err = ENOENT;
745 	if(!pp && !(pp=path_get(shp,"")))
746 		return(0);
747 	shp->path_err = 0;
748 	while(1)
749 	{
750 		sh_sigcheck(shp);
751 		shp->bltin_dir = 0;
752 		while(oldpp=pp)
753 		{
754 			pp = path_nextcomp(shp,pp,name,0);
755 			if(!(oldpp->flags&PATH_SKIP))
756 				break;
757 		}
758 		if(!oldpp)
759 		{
760 			shp->path_err = ENOENT;
761 			return(0);
762 		}
763 		isfun = (oldpp->flags&PATH_FPATH);
764 		if(!isfun && !sh_isoption(SH_RESTRICTED))
765 		{
766 #if SHOPT_DYNAMIC
767 			Shbltin_f addr;
768 			int n;
769 #endif
770 			if(*stakptr(PATH_OFFSET)=='/' && nv_search(stakptr(PATH_OFFSET),shp->bltin_tree,0))
771 				return(oldpp);
772 #if SHOPT_DYNAMIC
773 			n = staktell();
774 			stakputs("b_");
775 			stakputs(name);
776 			stakputc(0);
777 			if((addr = sh_getlib(shp, stakptr(n), oldpp)) &&
778 			   (np = sh_addbuiltin(stakptr(PATH_OFFSET),addr,NiL)) &&
779 			   nv_isattr(np,NV_BLTINOPT))
780 			{
781 				shp->bltin_dir = 0;
782 				return(oldpp);
783 			}
784 			stakseek(n);
785 			while(bp = oldpp->blib)
786 			{
787 				char *fp;
788 				void *dll;
789 				int m;
790 				if(fp = strchr(bp, ':'))
791 				{
792 					*fp++ = 0;
793 					oldpp->blib = fp;
794 					fp = 0;
795 				}
796 				else
797 				{
798 					fp = oldpp->bbuf;
799 					oldpp->blib = oldpp->bbuf = 0;
800 				}
801 				n = staktell();
802 				stakputs("b_");
803 				stakputs(name);
804 				stakputc(0);
805 				m = staktell();
806 				shp->bltin_dir = oldpp->name;
807 				if(*bp!='/')
808 				{
809 					stakputs(oldpp->name);
810 					stakputc('/');
811 				}
812 				stakputs(bp);
813 				stakputc(0);
814 				if(cp = strrchr(stakptr(m),'/'))
815 					cp++;
816 				else
817 					cp = stakptr(m);
818 				if(!strcmp(cp,LIBCMD) &&
819 				   (addr=(Shbltin_f)dlllook((void*)0,stakptr(n))) &&
820 				   (np = sh_addbuiltin(stakptr(PATH_OFFSET),addr,NiL)) &&
821 				   nv_isattr(np,NV_BLTINOPT))
822 				{
823 				found:
824 					if(fp)
825 						free(fp);
826 					shp->bltin_dir = 0;
827 					return(oldpp);
828 				}
829 #ifdef SH_PLUGIN_VERSION
830 				if (dll = dllplugin(SH_ID, stakptr(m), NiL, SH_PLUGIN_VERSION, NiL, RTLD_LAZY, NiL, 0))
831 					sh_addlib(shp,dll,stakptr(m),oldpp);
832 #else
833 #if (_AST_VERSION>=20040404)
834 				if (dll = dllplug(SH_ID, stakptr(m), NiL, RTLD_LAZY, NiL, 0))
835 #else
836 				if (dll = dllfind(stakptr(m), NiL, RTLD_LAZY, NiL, 0))
837 #endif
838 				{
839 					/*
840 					 * this detects the 2007-05-11 builtin context change and also
841 					 * the 2008-03-30 opt_info.num change that hit libcmd::b_head
842 					 */
843 
844 					if (libcmd && !dlllook(dll, "b_pids"))
845 					{
846 						dlclose(dll);
847 						dll = 0;
848 					}
849 					else
850 						sh_addlib(shp,dll,stakptr(m),oldpp);
851 				}
852 #endif
853 				if(dll &&
854 				   (addr=(Shbltin_f)dlllook(dll,stakptr(n))) &&
855 				   (!(np = sh_addbuiltin(stakptr(PATH_OFFSET),NiL,NiL)) || np->nvalue.bfp!=(Nambfp_f)addr) &&
856 				   (np = sh_addbuiltin(stakptr(PATH_OFFSET),addr,NiL)))
857 				{
858 					np->nvenv = dll;
859 					goto found;
860 				}
861 				if(*stakptr(PATH_OFFSET)=='/' && nv_search(stakptr(PATH_OFFSET),shp->bltin_tree,0))
862 					goto found;
863 				if(fp)
864 					free(fp);
865 				stakseek(n);
866 			}
867 #endif /* SHOPT_DYNAMIC */
868 		}
869 		shp->bltin_dir = 0;
870 		sh_stats(STAT_PATHS);
871 		f = canexecute(shp,stakptr(PATH_OFFSET),isfun);
872 		if(isfun && f>=0 && (cp = strrchr(name,'.')))
873 		{
874 			*cp = 0;
875 			if(nv_open(name,sh_subfuntree(1),NV_NOARRAY|NV_IDENT|NV_NOSCOPE))
876 				f = -1;
877 			*cp = '.';
878 		}
879 		if(isfun && f>=0)
880 		{
881 			nv_onattr(nv_open(name,sh_subfuntree(1),NV_NOARRAY|NV_IDENT|NV_NOSCOPE),NV_LTOU|NV_FUNCTION);
882 			funload(shp,f,name);
883 			close(f);
884 			f = -1;
885 			return(0);
886 		}
887 		else if(f>=0 && (oldpp->flags & PATH_STD_DIR))
888 		{
889 			int n = staktell();
890 			stakputs("/bin/");
891 			stakputs(name);
892 			stakputc(0);
893 			np = nv_search(stakptr(n),shp->bltin_tree,0);
894 			stakseek(n);
895 			if(np)
896 			{
897 				n = np->nvflag;
898 				np = sh_addbuiltin(stakptr(PATH_OFFSET),(Shbltin_f)np->nvalue.bfp,nv_context(np));
899 				np->nvflag = n;
900 			}
901 		}
902 		if(!pp || f>=0)
903 			break;
904 		if(errno!=ENOENT)
905 			noexec = errno;
906 	}
907 	if(f<0)
908 	{
909 		shp->path_err = (noexec?noexec:ENOENT);
910 		return(0);
911 	}
912 	stakputc(0);
913 	return(oldpp);
914 }
915 
916 /*
917  * returns 0 if path can execute
918  * sets exec_err if file is found but can't be executable
919  */
920 #undef S_IXALL
921 #ifdef S_IXUSR
922 #   define S_IXALL	(S_IXUSR|S_IXGRP|S_IXOTH)
923 #else
924 #   ifdef S_IEXEC
925 #	define S_IXALL	(S_IEXEC|(S_IEXEC>>3)|(S_IEXEC>>6))
926 #   else
927 #	define S_IXALL	0111
928 #   endif /*S_EXEC */
929 #endif /* S_IXUSR */
930 
931 static int canexecute(Shell_t *shp,register char *path, int isfun)
932 {
933 	struct stat statb;
934 	register int fd=0;
935 	path = path_relative(shp,path);
936 	if(isfun)
937 	{
938 		if((fd=open(path,O_RDONLY,0))<0 || fstat(fd,&statb)<0)
939 			goto err;
940 	}
941 	else if(stat(path,&statb) < 0)
942 	{
943 #if _WINIX
944 		/* check for .exe or .bat suffix */
945 		char *cp;
946 		if(errno==ENOENT && (!(cp=strrchr(path,'.')) || strlen(cp)>4 || strchr(cp,'/')))
947 		{
948 			int offset = staktell()-1;
949 			stakseek(offset);
950 			stakputs(".bat");
951 			path = stakptr(PATH_OFFSET);
952 			if(stat(path,&statb) < 0)
953 			{
954 				if(errno!=ENOENT)
955 					goto err;
956 				memcpy(stakptr(offset),".sh",4);
957 				if(stat(path,&statb) < 0)
958 					goto err;
959 			}
960 		}
961 		else
962 #endif /* _WINIX */
963 		goto err;
964 	}
965 	errno = EPERM;
966 	if(S_ISDIR(statb.st_mode))
967 		errno = EISDIR;
968 	else if((statb.st_mode&S_IXALL)==S_IXALL || sh_access(path,X_OK)>=0)
969 		return(fd);
970 	if(isfun && fd>=0)
971 		sh_close(fd);
972 err:
973 	return(-1);
974 }
975 
976 /*
977  * Return path relative to present working directory
978  */
979 
980 char *path_relative(Shell_t *shp,register const char* file)
981 {
982 	register const char *pwd;
983 	register const char *fp = file;
984 	/* can't relpath when shp->pwd not set */
985 	if(!(pwd=shp->pwd))
986 		return((char*)fp);
987 	while(*pwd==*fp)
988 	{
989 		if(*pwd++==0)
990 			return((char*)e_dot);
991 		fp++;
992 	}
993 	if(*pwd==0 && *fp == '/')
994 	{
995 		while(*++fp=='/');
996 		if(*fp)
997 			return((char*)fp);
998 		return((char*)e_dot);
999 	}
1000 	return((char*)file);
1001 }
1002 
1003 void	path_exec(Shell_t *shp,register const char *arg0,register char *argv[],struct argnod *local)
1004 {
1005 	char **envp;
1006 	const char *opath;
1007 	Pathcomp_t *libpath, *pp=0;
1008 	int slash=0;
1009 	nv_setlist(local,NV_EXPORT|NV_IDENT|NV_ASSIGN,0);
1010 	envp = sh_envgen();
1011 	if(strchr(arg0,'/'))
1012 	{
1013 		slash=1;
1014 		/* name containing / not allowed for restricted shell */
1015 		if(sh_isoption(SH_RESTRICTED))
1016 			errormsg(SH_DICT,ERROR_exit(1),e_restricted,arg0);
1017 	}
1018 	else
1019 		pp=path_get(shp,arg0);
1020 	shp->path_err= ENOENT;
1021 	sfsync(NIL(Sfio_t*));
1022 	timerdel(NIL(void*));
1023 	/* find first path that has a library component */
1024 	while(pp && (pp->flags&PATH_SKIP))
1025 		pp = pp->next;
1026 	if(pp || slash) do
1027 	{
1028 		sh_sigcheck(shp);
1029 		if(libpath=pp)
1030 		{
1031 			pp = path_nextcomp(shp,pp,arg0,0);
1032 			opath = stakfreeze(1)+PATH_OFFSET;
1033 		}
1034 		else
1035 			opath = arg0;
1036 		path_spawn(shp,opath,argv,envp,libpath,0);
1037 		while(pp && (pp->flags&PATH_FPATH))
1038 			pp = path_nextcomp(shp,pp,arg0,0);
1039 	}
1040 	while(pp);
1041 	/* force an exit */
1042 	((struct checkpt*)shp->jmplist)->mode = SH_JMPEXIT;
1043 	if((errno=shp->path_err)==ENOENT)
1044 		errormsg(SH_DICT,ERROR_exit(ERROR_NOENT),e_found,arg0);
1045 	else
1046 		errormsg(SH_DICT,ERROR_system(ERROR_NOEXEC),e_exec,arg0);
1047 }
1048 
1049 pid_t path_spawn(Shell_t *shp,const char *opath,register char **argv, char **envp, Pathcomp_t *libpath, int spawn)
1050 {
1051 	register char *path;
1052 	char **xp=0, *xval, *libenv = (libpath?libpath->lib:0);
1053 	Namval_t*	np;
1054 	char		*s, *v;
1055 	int		r, n, pidsize;
1056 	pid_t		pid= -1;
1057 	/* leave room for inserting _= pathname in environment */
1058 	envp--;
1059 #if _lib_readlink
1060 	/* save original pathname */
1061 	stakseek(PATH_OFFSET);
1062 	pidsize = sfprintf(stkstd,"*%d*",spawn?getpid():getppid());
1063 	stakputs(opath);
1064 	opath = stakfreeze(1)+PATH_OFFSET+pidsize;
1065 	np=nv_search(argv[0],shp->track_tree,0);
1066 	while(libpath && !libpath->lib)
1067 		libpath=libpath->next;
1068 	if(libpath && (!np || nv_size(np)>0))
1069 	{
1070 		/* check for symlink and use symlink name */
1071 		char buff[PATH_MAX+1];
1072 		char save[PATH_MAX+1];
1073 		stakseek(PATH_OFFSET);
1074 		stakputs(opath);
1075 		path = stakptr(PATH_OFFSET);
1076 		while((n=readlink(path,buff,PATH_MAX))>0)
1077 		{
1078 			buff[n] = 0;
1079 			n = PATH_OFFSET;
1080 			r = 0;
1081 			if((v=strrchr(path,'/')) && *buff!='/')
1082 			{
1083 				if(buff[0]=='.' && buff[1]=='.' && (r = strlen(path) + 1) <= PATH_MAX)
1084 					memcpy(save, path, r);
1085 				else
1086 					r = 0;
1087 				n += (v+1-path);
1088 			}
1089 			stakseek(n);
1090 			stakputs(buff);
1091 			stakputc(0);
1092 			path = stakptr(PATH_OFFSET);
1093 			if(v && buff[0]=='.' && buff[1]=='.')
1094 			{
1095 				pathcanon(path, 0);
1096 				if(r && access(path,X_OK))
1097 				{
1098 					memcpy(path, save, r);
1099 					break;
1100 				}
1101 			}
1102 			if(libenv = path_lib(shp,libpath,path))
1103 				break;
1104 		}
1105 		stakseek(0);
1106 	}
1107 #endif
1108 	if(libenv && (v = strchr(libenv,'=')))
1109 	{
1110 		n = v - libenv;
1111 		*v = 0;
1112 		np = nv_open(libenv,shp->var_tree,0);
1113 		*v = '=';
1114 		s = nv_getval(np);
1115 		stakputs(libenv);
1116 		if(s)
1117 		{
1118 			stakputc(':');
1119 			stakputs(s);
1120 		}
1121 		v = stakfreeze(1);
1122 		r = 1;
1123 		xp = envp + 1;
1124 		while (s = *xp++)
1125 		{
1126 			if (strneq(s, v, n) && s[n] == '=')
1127 			{
1128 				xval = *--xp;
1129 				*xp = v;
1130 				r = 0;
1131 				break;
1132 			}
1133 		}
1134 		if (r)
1135 		{
1136 			*envp-- = v;
1137 			xp = 0;
1138 		}
1139 	}
1140 	if(!opath)
1141 		opath = stakptr(PATH_OFFSET);
1142 	envp[0] =  (char*)opath-(PATH_OFFSET+pidsize);
1143 	envp[0][0] =  '_';
1144 	envp[0][1] =  '=';
1145 	sfsync(sfstderr);
1146 	sh_sigcheck(shp);
1147 	path = path_relative(shp,opath);
1148 #ifdef SHELLMAGIC
1149 	if(*path!='/' && path!=opath)
1150 	{
1151 		/*
1152 		 * The following code because execv(foo,) and execv(./foo,)
1153 		 * may not yield the same results
1154 		 */
1155 		char *sp = (char*)malloc(strlen(path)+3);
1156 		sp[0] = '.';
1157 		sp[1] = '/';
1158 		strcpy(sp+2,path);
1159 		path = sp;
1160 	}
1161 #endif /* SHELLMAGIC */
1162 	if(spawn && !sh_isoption(SH_PFSH))
1163 		pid = _spawnveg(shp,opath, &argv[0],envp, spawn>>1);
1164 	else
1165 		pid = path_pfexecve(shp,opath, &argv[0] ,envp,spawn);
1166 	if(xp)
1167 		*xp = xval;
1168 #ifdef SHELLMAGIC
1169 	if(*path=='.' && path!=opath)
1170 	{
1171 		free(path);
1172 		path = path_relative(shp,opath);
1173 	}
1174 #endif /* SHELLMAGIC */
1175 	if(pid>0)
1176 		return(pid);
1177 retry:
1178 	switch(shp->path_err = errno)
1179 	{
1180 #ifdef apollo
1181 	    /*
1182   	     * On apollo's execve will fail with eacces when
1183 	     * file has execute but not read permissions. So,
1184 	     * for now we will pretend that EACCES and ENOEXEC
1185  	     * mean the same thing.
1186  	     */
1187 	    case EACCES:
1188 #endif /* apollo */
1189 	    case ENOEXEC:
1190 #if SHOPT_SUID_EXEC
1191 	    case EPERM:
1192 		/* some systems return EPERM if setuid bit is on */
1193 #endif
1194 		errno = ENOEXEC;
1195 		if(spawn)
1196 		{
1197 #ifdef _lib_fork
1198 			if(shp->subshell)
1199 				return(-1);
1200 			do
1201 			{
1202 				if((pid=fork())>0)
1203 					return(pid);
1204 			}
1205 			while(_sh_fork(shp,pid,0,(int*)0) < 0);
1206 			((struct checkpt*)shp->jmplist)->mode = SH_JMPEXIT;
1207 #else
1208 			return(-1);
1209 #endif
1210 		}
1211 		exscript(shp,path,argv,envp);
1212 #ifndef apollo
1213 		/* FALLTHROUGH */
1214 	    case EACCES:
1215 	    {
1216 		struct stat statb;
1217 		if(stat(path,&statb)>=0)
1218 		{
1219 			if(S_ISDIR(statb.st_mode))
1220 				errno = EISDIR;
1221 #ifdef S_ISSOCK
1222 			if(S_ISSOCK(statb.st_mode))
1223 				exscript(shp,path,argv,envp);
1224 #endif
1225 		}
1226 	    }
1227 #endif /* !apollo */
1228 #ifdef ENAMETOOLONG
1229 		/* FALLTHROUGH */
1230 	    case ENAMETOOLONG:
1231 #endif /* ENAMETOOLONG */
1232 #if !SHOPT_SUID_EXEC
1233 	    case EPERM:
1234 #endif
1235 		shp->path_err = errno;
1236 		return(-1);
1237 	    case ENOTDIR:
1238 	    case ENOENT:
1239 	    case EINTR:
1240 #ifdef EMLINK
1241 	    case EMLINK:
1242 #endif /* EMLINK */
1243 		return(-1);
1244 	    case E2BIG:
1245 		if(shp->xargmin)
1246 		{
1247 			pid = path_xargs(shp,opath, &argv[0] ,envp,spawn);
1248 			if(pid<0)
1249 				goto retry;
1250 			return(pid);
1251 		}
1252 		/* FALLTHROUGH */
1253 	    default:
1254 		errormsg(SH_DICT,ERROR_system(ERROR_NOEXEC),e_exec,path);
1255 	}
1256 	return 0;
1257 }
1258 
1259 /*
1260  * File is executable but not machine code.
1261  * Assume file is a Shell script and execute it.
1262  */
1263 
1264 static void exscript(Shell_t *shp,register char *path,register char *argv[],char **envp)
1265 {
1266 	register Sfio_t *sp;
1267 	path = path_relative(shp,path);
1268 	shp->comdiv=0;
1269 	shp->bckpid = 0;
1270 	shp->coshell = 0;
1271 	shp->st.ioset=0;
1272 	/* clean up any cooperating processes */
1273 	if(shp->cpipe[0]>0)
1274 		sh_pclose(shp->cpipe);
1275 	if(shp->cpid && shp->outpipe)
1276 		sh_close(*shp->outpipe);
1277 	shp->cpid = 0;
1278 	if(sp=fcfile())
1279 		while(sfstack(sp,SF_POPSTACK));
1280 	job_clear();
1281 	VALIDATE_FD(shp, shp->infd);
1282 	if(shp->infd>0 && (shp->fdstatus[shp->infd]&IOCLEX))
1283 		sh_close(shp->infd);
1284 	sh_setstate(sh_state(SH_FORKED));
1285 	sfsync(sfstderr);
1286 #if SHOPT_SUID_EXEC && !SHOPT_PFSH
1287 	/* check if file cannot open for read or script is setuid/setgid  */
1288 	{
1289 		static char name[] = "/tmp/euidXXXXXXXXXX";
1290 		register int n;
1291 		register uid_t euserid;
1292 		char *savet=0;
1293 		struct stat statb;
1294 		if((n=sh_open(path,O_RDONLY,0)) >= 0)
1295 		{
1296 			/* move <n> if n=0,1,2 */
1297 			n = sh_iomovefd(n);
1298 			if(fstat(n,&statb)>=0 && !(statb.st_mode&(S_ISUID|S_ISGID)))
1299 				goto openok;
1300 			sh_close(n);
1301 		}
1302 		if((euserid=geteuid()) != shp->gd->userid)
1303 		{
1304 			strncpy(name+9,fmtbase((long)getpid(),10,0),sizeof(name)-10);
1305 			/* create a suid open file with owner equal effective uid */
1306 			if((n=open(name,O_CREAT|O_TRUNC|O_WRONLY,S_ISUID|S_IXUSR)) < 0)
1307 				goto fail;
1308 			unlink(name);
1309 			/* make sure that file has right owner */
1310 			if(fstat(n,&statb)<0 || statb.st_uid != euserid)
1311 				goto fail;
1312 			if(n!=10)
1313 			{
1314 				sh_close(10);
1315 				fcntl(n, F_DUPFD, 10);
1316 				sh_close(n);
1317 				n=10;
1318 			}
1319 		}
1320 		savet = *--argv;
1321 		*argv = path;
1322 		path_pfexecve(shp,e_suidexec,argv,envp,0);
1323 	fail:
1324 		/*
1325 		 *  The following code is just for compatibility
1326 		 */
1327 		if((n=open(path,O_RDONLY,0)) < 0)
1328 			errormsg(SH_DICT,ERROR_system(ERROR_NOEXEC),e_exec,path);
1329 		if(savet)
1330 			*argv++ = savet;
1331 	openok:
1332 		shp->infd = n;
1333 	}
1334 #else
1335 	if((shp->infd = sh_open(path,O_RDONLY,0)) < 0)
1336 		errormsg(SH_DICT,ERROR_system(ERROR_NOEXEC),e_exec,path);
1337 #endif
1338 	shp->infd = sh_iomovefd(shp->infd);
1339 #if SHOPT_ACCT
1340 	sh_accbegin(path) ;  /* reset accounting */
1341 #endif	/* SHOPT_ACCT */
1342 	shp->arglist = sh_argcreate(argv);
1343 	shp->lastarg = strdup(path);
1344 	/* save name of calling command */
1345 	shp->readscript = error_info.id;
1346 	/* close history file if name has changed */
1347 	if(shp->gd->hist_ptr && (path=nv_getval(HISTFILE)) && strcmp(path,shp->gd->hist_ptr->histname))
1348 	{
1349 		hist_close(shp->gd->hist_ptr);
1350 		(HISTCUR)->nvalue.lp = 0;
1351 	}
1352 	sh_offstate(SH_FORKED);
1353 	if(shp->sigflag[SIGCHLD]==SH_SIGOFF)
1354 		shp->sigflag[SIGCHLD] = SH_SIGFAULT;
1355 	siglongjmp(*shp->jmplist,SH_JMPSCRIPT);
1356 }
1357 
1358 #if SHOPT_ACCT
1359 #   include <sys/acct.h>
1360 #   include "FEATURE/time"
1361 
1362     static struct acct sabuf;
1363     static struct tms buffer;
1364     static clock_t	before;
1365     static char *SHACCT; /* set to value of SHACCT environment variable */
1366     static shaccton;	/* non-zero causes accounting record to be written */
1367     static int compress(time_t);
1368     /*
1369      *	initialize accounting, i.e., see if SHACCT variable set
1370      */
1371     void sh_accinit(void)
1372     {
1373 	SHACCT = getenv("SHACCT");
1374     }
1375     /*
1376     * suspend accounting until turned on by sh_accbegin()
1377     */
1378     void sh_accsusp(void)
1379     {
1380 	shaccton=0;
1381 #ifdef AEXPAND
1382 	sabuf.ac_flag |= AEXPND;
1383 #endif /* AEXPAND */
1384     }
1385 
1386     /*
1387      * begin an accounting record by recording start time
1388      */
1389     void sh_accbegin(const char *cmdname)
1390     {
1391 	if(SHACCT)
1392 	{
1393 		sabuf.ac_btime = time(NIL(time_t *));
1394 		before = times(&buffer);
1395 		sabuf.ac_uid = getuid();
1396 		sabuf.ac_gid = getgid();
1397 		strncpy(sabuf.ac_comm, (char*)path_basename(cmdname),
1398 			sizeof(sabuf.ac_comm));
1399 		shaccton = 1;
1400 	}
1401     }
1402     /*
1403      * terminate an accounting record and append to accounting file
1404      */
1405     void	sh_accend(void)
1406     {
1407 	int	fd;
1408 	clock_t	after;
1409 
1410 	if(shaccton)
1411 	{
1412 		after = times(&buffer);
1413 		sabuf.ac_utime = compress(buffer.tms_utime + buffer.tms_cutime);
1414 		sabuf.ac_stime = compress(buffer.tms_stime + buffer.tms_cstime);
1415 		sabuf.ac_etime = compress( (time_t)(after-before));
1416 		fd = open( SHACCT , O_WRONLY | O_APPEND | O_CREAT,RW_ALL);
1417 		write(fd, (const char*)&sabuf, sizeof( sabuf ));
1418 		close( fd);
1419 	}
1420     }
1421 
1422     /*
1423      * Produce a pseudo-floating point representation
1424      * with 3 bits base-8 exponent, 13 bits fraction.
1425      */
1426     static int compress(register time_t t)
1427     {
1428 	register int exp = 0, rund = 0;
1429 
1430 	while (t >= 8192)
1431 	{
1432 		exp++;
1433 		rund = t&04;
1434 		t >>= 3;
1435 	}
1436 	if (rund)
1437 	{
1438 		t++;
1439 		if (t >= 8192)
1440 		{
1441 			t >>= 3;
1442 			exp++;
1443 		}
1444 	}
1445 	return((exp<<13) + t);
1446     }
1447 #endif	/* SHOPT_ACCT */
1448 
1449 
1450 
1451 /*
1452  * add a pathcomponent to the path search list and eliminate duplicates
1453  * and non-existing absolute paths.
1454  */
1455 static Pathcomp_t *path_addcomp(Shell_t *shp,Pathcomp_t *first, Pathcomp_t *old,const char *name, int flag)
1456 {
1457 	register Pathcomp_t *pp, *oldpp;
1458 	int len, offset=staktell();
1459 	if(!(flag&PATH_BFPATH))
1460 	{
1461 		register const char *cp = name;
1462 		while(*cp && *cp!=':')
1463 			stakputc(*cp++);
1464 		len = staktell()-offset;
1465 		stakputc(0);
1466 		stakseek(offset);
1467 		name = (const char*)stakptr(offset);
1468 	}
1469 	else
1470 		len = strlen(name);
1471 	for(pp=first; pp; pp=pp->next)
1472 	{
1473 		if(len == pp->len && memcmp(name,pp->name,len)==0)
1474 		{
1475 			pp->flags |= flag;
1476 			return(first);
1477 		}
1478 	}
1479 	for(pp=first, oldpp=0; pp; oldpp=pp, pp=pp->next);
1480 	pp = newof((Pathcomp_t*)0,Pathcomp_t,1,len+1);
1481 	pp->shp = shp;
1482 	pp->refcount = 1;
1483 	memcpy((char*)(pp+1),name,len+1);
1484 	pp->name = (char*)(pp+1);
1485 	pp->len = len;
1486 	if(oldpp)
1487 		oldpp->next = pp;
1488 	else
1489 		first = pp;
1490 	pp->flags = flag;
1491 	if(strcmp(name,SH_CMDLIB_DIR)==0)
1492 	{
1493 		pp->dev = 1;
1494 		pp->flags |= PATH_BUILTIN_LIB;
1495 		pp->blib = pp->bbuf = malloc(sizeof(LIBCMD));
1496 		strcpy(pp->blib,LIBCMD);
1497 		return(first);
1498 	}
1499 	if((old||shp->pathinit) &&  ((flag&(PATH_PATH|PATH_SKIP))==PATH_PATH))
1500 		path_chkpaths(shp,first,old,pp,offset);
1501 	return(first);
1502 }
1503 
1504 /*
1505  * This function checks for the .paths file in directory in <pp>
1506  * it assumes that the directory is on the stack at <offset>
1507  */
1508 static int path_chkpaths(Shell_t *shp,Pathcomp_t *first, Pathcomp_t* old,Pathcomp_t *pp, int offset)
1509 {
1510 	struct stat statb;
1511 	int k,m,n,fd;
1512 	char *sp,*cp,*ep;
1513 	stakseek(offset+pp->len);
1514 	if(pp->len==1 && *stakptr(offset)=='/')
1515 		stakseek(offset);
1516 	stakputs("/.paths");
1517 	if((fd=open(stakptr(offset),O_RDONLY))>=0)
1518 	{
1519 		fstat(fd,&statb);
1520 		n = statb.st_size;
1521 		stakseek(offset+pp->len+n+2);
1522 		sp = stakptr(offset+pp->len);
1523 		*sp++ = '/';
1524 		n=read(fd,cp=sp,n);
1525 		sp[n] = 0;
1526 		close(fd);
1527 		for(ep=0; n--; cp++)
1528 		{
1529 			if(*cp=='=')
1530 			{
1531 				ep = cp+1;
1532 				continue;
1533 			}
1534 			else if(*cp!='\r' &&  *cp!='\n')
1535 				continue;
1536 			if(*sp=='#' || sp==cp)
1537 			{
1538 				sp = cp+1;
1539 				continue;
1540 			}
1541 			*cp = 0;
1542 			m = ep ? (ep-sp) : 0;
1543 			if(m==0 || m==6 && memcmp((void*)sp,(void*)"FPATH=",m)==0)
1544 			{
1545 				if(first)
1546 				{
1547 					char *ptr = stakptr(offset+pp->len+1);
1548 					if(ep)
1549 						strcpy(ptr,ep);
1550 					path_addcomp(shp,first,old,stakptr(offset),PATH_FPATH|PATH_BFPATH);
1551 				}
1552 			}
1553 			else if(m==11 && memcmp((void*)sp,(void*)"PLUGIN_LIB=",m)==0)
1554 			{
1555 				if(pp->bbuf)
1556 					free(pp->bbuf);
1557 				pp->blib = pp->bbuf = strdup(ep);
1558 			}
1559 			else if(m)
1560 			{
1561 				pp->lib = (char*)malloc(cp-sp+pp->len+2);
1562 				memcpy((void*)pp->lib,(void*)sp,m);
1563 				memcpy((void*)&pp->lib[m],stakptr(offset),pp->len);
1564 				pp->lib[k=m+pp->len] = '/';
1565 				strcpy((void*)&pp->lib[k+1],ep);
1566 				pathcanon(&pp->lib[m],0);
1567 				if(!first)
1568 				{
1569 					stakseek(0);
1570 					stakputs(pp->lib);
1571 					free((void*)pp->lib);
1572 					return(1);
1573 				}
1574 			}
1575 			sp = cp+1;
1576 			ep = 0;
1577 		}
1578 	}
1579 	return(0);
1580 }
1581 
1582 
1583 Pathcomp_t *path_addpath(Shell_t *shp,Pathcomp_t *first, register const char *path,int type)
1584 {
1585 	register const char *cp;
1586 	Pathcomp_t *old=0;
1587 	int offset = staktell();
1588 	char *savptr;
1589 
1590 	if(!path && type!=PATH_PATH)
1591 		return(first);
1592 	if(type!=PATH_FPATH)
1593 	{
1594 		old = first;
1595 		first = 0;
1596 	}
1597 	if(offset)
1598 		savptr = stakfreeze(0);
1599 	if(path) while(*(cp=path))
1600 	{
1601 		if(*cp==':')
1602 		{
1603 			if(type!=PATH_FPATH)
1604 				first = path_addcomp(shp,first,old,".",type);
1605 			while(*++path == ':');
1606 		}
1607 		else
1608 		{
1609 			int c;
1610 			while(*path && *path!=':')
1611 				path++;
1612 			c = *path++;
1613 			first = path_addcomp(shp,first,old,cp,type);
1614 			if(c==0)
1615 				break;
1616 			if(*path==0)
1617 				path--;
1618 		}
1619 	}
1620 	if(old)
1621 	{
1622 		if(!first && !path)
1623 		{
1624 			Pathcomp_t *pp = (Pathcomp_t*)shp->defpathlist;
1625 			if(!pp)
1626 				pp = defpath_init(shp);
1627 			first = path_dup(pp);
1628 		}
1629 		if(cp=(sh_scoped(shp,FPATHNOD))->nvalue.cp)
1630 			first = (void*)path_addpath(shp,(Pathcomp_t*)first,cp,PATH_FPATH);
1631 		path_delete(old);
1632 	}
1633 	if(offset)
1634 		stakset(savptr,offset);
1635 	else
1636 		stakseek(0);
1637 	return(first);
1638 }
1639 
1640 /*
1641  * duplicate the path give by <first> by incremented reference counts
1642  */
1643 Pathcomp_t *path_dup(Pathcomp_t *first)
1644 {
1645 	register Pathcomp_t *pp=first;
1646 	while(pp)
1647 	{
1648 		pp->refcount++;
1649 		pp = pp->next;
1650 	}
1651 	return(first);
1652 }
1653 
1654 /*
1655  * called whenever the directory is changed
1656  */
1657 void path_newdir(Shell_t *shp,Pathcomp_t *first)
1658 {
1659 	register Pathcomp_t *pp=first, *next, *pq;
1660 	struct stat statb;
1661 	for(pp=first; pp; pp=pp->next)
1662 	{
1663 		pp->flags &= ~PATH_SKIP;
1664 		if(*pp->name=='/')
1665 			continue;
1666 		/* delete .paths component */
1667 		if((next=pp->next) && (next->flags&PATH_BFPATH))
1668 		{
1669 			pp->next = next->next;
1670 			if(--next->refcount<=0)
1671 				free((void*)next);
1672 		}
1673 		if(stat(pp->name,&statb)<0 || !S_ISDIR(statb.st_mode))
1674 		{
1675 			pp->dev = 0;
1676 			pp->ino = 0;
1677 			continue;
1678 		}
1679 		pp->dev = statb.st_dev;
1680 		pp->ino = statb.st_ino;
1681 		pp->mtime = statb.st_mtime;
1682 		for(pq=first;pq!=pp;pq=pq->next)
1683 		{
1684 			if(pp->ino==pq->ino && pp->dev==pq->dev)
1685 				pp->flags |= PATH_SKIP;
1686 		}
1687 		for(pq=pp;pq=pq->next;)
1688 		{
1689 			if(pp->ino==pq->ino && pp->dev==pq->dev)
1690 				pq->flags |= PATH_SKIP;
1691 		}
1692 		if((pp->flags&(PATH_PATH|PATH_SKIP))==PATH_PATH)
1693 		{
1694 			/* try to insert .paths component */
1695 			int offset = staktell();
1696 			stakputs(pp->name);
1697 			stakseek(offset);
1698 			next = pp->next;
1699 			pp->next = 0;
1700 			path_chkpaths(shp,first,(Pathcomp_t*)0,pp,offset);
1701 			if(pp->next)
1702 				pp = pp->next;
1703 			pp->next = next;
1704 		}
1705 	}
1706 #if 0
1707 	path_dump(first);
1708 #endif
1709 }
1710 
1711 Pathcomp_t *path_unsetfpath(Shell_t *shp)
1712 {
1713 	Pathcomp_t	*first = (Pathcomp_t*)shp->pathlist;
1714 	register Pathcomp_t *pp=first, *old=0;
1715 	if(shp->fpathdict)
1716 	{
1717 		struct Ufunction  *rp, *rpnext;
1718 		for(rp=(struct Ufunction*)dtfirst(shp->fpathdict);rp;rp=rpnext)
1719 		{
1720 			rpnext = (struct Ufunction*)dtnext(shp->fpathdict,rp);
1721 			if(rp->fdict)
1722 				nv_delete(rp->np,rp->fdict,NV_NOFREE);
1723 			rp->fdict = 0;
1724 		}
1725 	}
1726 	while(pp)
1727 	{
1728 		if((pp->flags&PATH_FPATH) && !(pp->flags&PATH_BFPATH))
1729 		{
1730 			if(pp->flags&PATH_PATH)
1731 				pp->flags &= ~PATH_FPATH;
1732 			else
1733 			{
1734 				Pathcomp_t *ppsave=pp;
1735 				if(old)
1736 					old->next = pp->next;
1737 				else
1738 					first = pp->next;
1739 				pp = pp->next;
1740 				if(--ppsave->refcount<=0)
1741 				{
1742 					if(ppsave->lib)
1743 						free((void*)ppsave->lib);
1744 					free((void*)ppsave);
1745 				}
1746 				continue;
1747 			}
1748 
1749 		}
1750 		old = pp;
1751 		pp = pp->next;
1752 	}
1753 	return(first);
1754 }
1755 
1756 Pathcomp_t *path_dirfind(Pathcomp_t *first,const char *name,int c)
1757 {
1758 	register Pathcomp_t *pp=first;
1759 	while(pp)
1760 	{
1761 		if(memcmp(name,pp->name,pp->len)==0 && name[pp->len]==c)
1762 			return(pp);
1763 		pp = pp->next;
1764 	}
1765 	return(0);
1766 }
1767 
1768 /*
1769  * get discipline for tracked alias
1770  */
1771 static char *talias_get(Namval_t *np, Namfun_t *nvp)
1772 {
1773 	Pathcomp_t *pp = (Pathcomp_t*)np->nvalue.cp;
1774 	char *ptr;
1775 	if(!pp)
1776 		return(NULL);
1777 	pp->shp->last_table = 0;
1778 	path_nextcomp(pp->shp,pp,nv_name(np),pp);
1779 	ptr = stakfreeze(0);
1780 	return(ptr+PATH_OFFSET);
1781 }
1782 
1783 static void talias_put(register Namval_t* np,const char *val,int flags,Namfun_t *fp)
1784 {
1785 	if(!val && np->nvalue.cp)
1786 	{
1787 		Pathcomp_t *pp = (Pathcomp_t*)np->nvalue.cp;
1788 		if(--pp->refcount<=0)
1789 			free((void*)pp);
1790 	}
1791 	nv_putv(np,val,flags,fp);
1792 }
1793 
1794 static const Namdisc_t talias_disc   = { 0, talias_put, talias_get   };
1795 static Namfun_t  talias_init = { &talias_disc, 1 };
1796 
1797 /*
1798  *  set tracked alias node <np> to value <pp>
1799  */
1800 void path_alias(register Namval_t *np,register Pathcomp_t *pp)
1801 {
1802 	if(pp)
1803 	{
1804 		struct stat statb;
1805 		char *sp;
1806 		nv_offattr(np,NV_NOPRINT);
1807 		nv_stack(np,&talias_init);
1808 		np->nvalue.cp = (char*)pp;
1809 		pp->refcount++;
1810 		nv_setattr(np,NV_TAGGED|NV_NOFREE);
1811 		path_nextcomp(pp->shp,pp,nv_name(np),pp);
1812 		sp = stakptr(PATH_OFFSET);
1813 		if(sp && lstat(sp,&statb)>=0 && S_ISLNK(statb.st_mode))
1814 			nv_setsize(np,statb.st_size+1);
1815 		else
1816 			nv_setsize(np,0);
1817 	}
1818 	else
1819 		_nv_unset(np,0);
1820 }
1821 
1822