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