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