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