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