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
54static int		canexecute(char*,int);
55static void		funload(Shell_t*,int,const char*);
56static void		exscript(Shell_t*,char*, char*[], char**);
57static int		path_chkpaths(Pathcomp_t*,Pathcomp_t*,Pathcomp_t*,int);
58static void		path_checkdup(register Pathcomp_t*);
59
60static const char	*std_path;
61
62static 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
76static 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
126static 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 */
151static 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 */
228char *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	}
277skip:
278	nv_onattr(PWDNOD,NV_NOFREE|NV_EXPORT);
279	shp->pwd = (char*)(PWDNOD->nvalue.cp);
280	return(cp);
281}
282
283static 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 */
305void  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 */
339static 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
373void 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 */
388static 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 */
429Pathcomp_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
473static 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
481static 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 */
510Pathcomp_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 */
533static 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 */
588int	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
597char	*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
606char *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 */
629static 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
677int	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 */
753Pathcomp_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
892static 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);
933err:
934	return(-1);
935}
936
937/*
938 * Return path relative to present working directory
939 */
940
941char *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
964void	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
1011pid_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);
1140retry:
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		/* FALLTHROUGH */
1177	    case EACCES:
1178	    {
1179		struct stat statb;
1180		if(stat(path,&statb)>=0)
1181		{
1182			if(S_ISDIR(statb.st_mode))
1183				errno = EISDIR;
1184#ifdef S_ISSOCK
1185			if(S_ISSOCK(statb.st_mode))
1186				exscript(shp,path,argv,envp);
1187#endif
1188		}
1189	    }
1190#endif /* !apollo */
1191#ifdef ENAMETOOLONG
1192		/* FALLTHROUGH */
1193	    case ENAMETOOLONG:
1194#endif /* ENAMETOOLONG */
1195#if !SHOPT_SUID_EXEC
1196	    case EPERM:
1197#endif
1198		shp->path_err = errno;
1199		return(-1);
1200	    case ENOTDIR:
1201	    case ENOENT:
1202	    case EINTR:
1203#ifdef EMLINK
1204	    case EMLINK:
1205#endif /* EMLINK */
1206		return(-1);
1207	    case E2BIG:
1208		if(sh.xargmin)
1209		{
1210			pid = path_xargs(opath, &argv[0] ,envp,spawn);
1211			if(pid<0)
1212				goto retry;
1213			return(pid);
1214		}
1215		/* FALLTHROUGH */
1216	    default:
1217		errormsg(SH_DICT,ERROR_system(ERROR_NOEXEC),e_exec,path);
1218	}
1219	return 0;
1220}
1221
1222/*
1223 * File is executable but not machine code.
1224 * Assume file is a Shell script and execute it.
1225 */
1226
1227static void exscript(Shell_t *shp,register char *path,register char *argv[],char **envp)
1228{
1229	register Sfio_t *sp;
1230	path = path_relative(path);
1231	shp->comdiv=0;
1232	shp->bckpid = 0;
1233	shp->st.ioset=0;
1234	/* clean up any cooperating processes */
1235	if(shp->cpipe[0]>0)
1236		sh_pclose(shp->cpipe);
1237	if(shp->cpid && shp->outpipe)
1238		sh_close(*shp->outpipe);
1239	shp->cpid = 0;
1240	if(sp=fcfile())
1241		while(sfstack(sp,SF_POPSTACK));
1242	job_clear();
1243	if(shp->infd>0 && (shp->fdstatus[shp->infd]&IOCLEX))
1244		sh_close(shp->infd);
1245	sh_setstate(sh_state(SH_FORKED));
1246	sfsync(sfstderr);
1247#if SHOPT_SUID_EXEC && !SHOPT_PFSH
1248	/* check if file cannot open for read or script is setuid/setgid  */
1249	{
1250		static char name[] = "/tmp/euidXXXXXXXXXX";
1251		register int n;
1252		register uid_t euserid;
1253		char *savet=0;
1254		struct stat statb;
1255		if((n=sh_open(path,O_RDONLY,0)) >= 0)
1256		{
1257			/* move <n> if n=0,1,2 */
1258			n = sh_iomovefd(n);
1259			if(fstat(n,&statb)>=0 && !(statb.st_mode&(S_ISUID|S_ISGID)))
1260				goto openok;
1261			sh_close(n);
1262		}
1263		if((euserid=geteuid()) != shp->userid)
1264		{
1265			strncpy(name+9,fmtbase((long)getpid(),10,0),sizeof(name)-10);
1266			/* create a suid open file with owner equal effective uid */
1267			if((n=open(name,O_CREAT|O_TRUNC|O_WRONLY,S_ISUID|S_IXUSR)) < 0)
1268				goto fail;
1269			unlink(name);
1270			/* make sure that file has right owner */
1271			if(fstat(n,&statb)<0 || statb.st_uid != euserid)
1272				goto fail;
1273			if(n!=10)
1274			{
1275				sh_close(10);
1276				fcntl(n, F_DUPFD, 10);
1277				sh_close(n);
1278				n=10;
1279			}
1280		}
1281		savet = *--argv;
1282		*argv = path;
1283		path_pfexecve(e_suidexec,argv,envp,0);
1284	fail:
1285		/*
1286		 *  The following code is just for compatibility
1287		 */
1288		if((n=open(path,O_RDONLY,0)) < 0)
1289			errormsg(SH_DICT,ERROR_system(ERROR_NOEXEC),e_exec,path);
1290		if(savet)
1291			*argv++ = savet;
1292	openok:
1293		shp->infd = n;
1294	}
1295#else
1296	if((shp->infd = sh_open(path,O_RDONLY,0)) < 0)
1297		errormsg(SH_DICT,ERROR_system(ERROR_NOEXEC),e_exec,path);
1298#endif
1299	shp->infd = sh_iomovefd(shp->infd);
1300#if SHOPT_ACCT
1301	sh_accbegin(path) ;  /* reset accounting */
1302#endif	/* SHOPT_ACCT */
1303	shp->arglist = sh_argcreate(argv);
1304	shp->lastarg = strdup(path);
1305	/* save name of calling command */
1306	shp->readscript = error_info.id;
1307	/* close history file if name has changed */
1308	if(shp->hist_ptr && (path=nv_getval(HISTFILE)) && strcmp(path,shp->hist_ptr->histname))
1309	{
1310		hist_close(shp->hist_ptr);
1311		(HISTCUR)->nvalue.lp = 0;
1312	}
1313	sh_offstate(SH_FORKED);
1314	if(shp->sigflag[SIGCHLD]==SH_SIGOFF)
1315		shp->sigflag[SIGCHLD] = SH_SIGFAULT;
1316	siglongjmp(*shp->jmplist,SH_JMPSCRIPT);
1317}
1318
1319#if SHOPT_ACCT
1320#   include <sys/acct.h>
1321#   include "FEATURE/time"
1322
1323    static struct acct sabuf;
1324    static struct tms buffer;
1325    static clock_t	before;
1326    static char *SHACCT; /* set to value of SHACCT environment variable */
1327    static shaccton;	/* non-zero causes accounting record to be written */
1328    static int compress(time_t);
1329    /*
1330     *	initialize accounting, i.e., see if SHACCT variable set
1331     */
1332    void sh_accinit(void)
1333    {
1334	SHACCT = getenv("SHACCT");
1335    }
1336    /*
1337    * suspend accounting until turned on by sh_accbegin()
1338    */
1339    void sh_accsusp(void)
1340    {
1341	shaccton=0;
1342#ifdef AEXPAND
1343	sabuf.ac_flag |= AEXPND;
1344#endif /* AEXPAND */
1345    }
1346
1347    /*
1348     * begin an accounting record by recording start time
1349     */
1350    void sh_accbegin(const char *cmdname)
1351    {
1352	if(SHACCT)
1353	{
1354		sabuf.ac_btime = time(NIL(time_t *));
1355		before = times(&buffer);
1356		sabuf.ac_uid = getuid();
1357		sabuf.ac_gid = getgid();
1358		strncpy(sabuf.ac_comm, (char*)path_basename(cmdname),
1359			sizeof(sabuf.ac_comm));
1360		shaccton = 1;
1361	}
1362    }
1363    /*
1364     * terminate an accounting record and append to accounting file
1365     */
1366    void	sh_accend(void)
1367    {
1368	int	fd;
1369	clock_t	after;
1370
1371	if(shaccton)
1372	{
1373		after = times(&buffer);
1374		sabuf.ac_utime = compress(buffer.tms_utime + buffer.tms_cutime);
1375		sabuf.ac_stime = compress(buffer.tms_stime + buffer.tms_cstime);
1376		sabuf.ac_etime = compress( (time_t)(after-before));
1377		fd = open( SHACCT , O_WRONLY | O_APPEND | O_CREAT,RW_ALL);
1378		write(fd, (const char*)&sabuf, sizeof( sabuf ));
1379		close( fd);
1380	}
1381    }
1382
1383    /*
1384     * Produce a pseudo-floating point representation
1385     * with 3 bits base-8 exponent, 13 bits fraction.
1386     */
1387    static int compress(register time_t t)
1388    {
1389	register int exp = 0, rund = 0;
1390
1391	while (t >= 8192)
1392	{
1393		exp++;
1394		rund = t&04;
1395		t >>= 3;
1396	}
1397	if (rund)
1398	{
1399		t++;
1400		if (t >= 8192)
1401		{
1402			t >>= 3;
1403			exp++;
1404		}
1405	}
1406	return((exp<<13) + t);
1407    }
1408#endif	/* SHOPT_ACCT */
1409
1410
1411
1412/*
1413 * add a pathcomponent to the path search list and eliminate duplicates
1414 * and non-existing absolute paths.
1415 */
1416static Pathcomp_t *path_addcomp(Pathcomp_t *first, Pathcomp_t *old,const char *name, int flag)
1417{
1418	register Pathcomp_t *pp, *oldpp;
1419	int len, offset=staktell();
1420	if(!(flag&PATH_BFPATH))
1421	{
1422		register const char *cp = name;
1423		while(*cp && *cp!=':')
1424			stakputc(*cp++);
1425		len = staktell()-offset;
1426		stakputc(0);
1427		stakseek(offset);
1428		name = (const char*)stakptr(offset);
1429	}
1430	else
1431		len = strlen(name);
1432	for(pp=first; pp; pp=pp->next)
1433	{
1434		if(memcmp(name,pp->name,len)==0 && (pp->name[len]==':' || pp->name[len]==0))
1435		{
1436			pp->flags |= flag;
1437			return(first);
1438		}
1439	}
1440	for(pp=first, oldpp=0; pp; oldpp=pp, pp=pp->next);
1441	pp = newof((Pathcomp_t*)0,Pathcomp_t,1,len+1);
1442	pp->refcount = 1;
1443	memcpy((char*)(pp+1),name,len+1);
1444	pp->name = (char*)(pp+1);
1445	pp->len = len;
1446	if(oldpp)
1447		oldpp->next = pp;
1448	else
1449		first = pp;
1450	pp->flags = flag;
1451	if(strcmp(name,SH_CMDLIB_DIR)==0)
1452	{
1453		pp->dev = 1;
1454		pp->flags |= PATH_BUILTIN_LIB;
1455		pp->blib = malloc(4);
1456		strcpy(pp->blib,LIBCMD);
1457		return(first);
1458	}
1459	if(old && ((flag&(PATH_PATH|PATH_SKIP))==PATH_PATH))
1460		path_chkpaths(first,old,pp,offset);
1461	return(first);
1462}
1463
1464/*
1465 * This function checks for the .paths file in directory in <pp>
1466 * it assumes that the directory is on the stack at <offset>
1467 */
1468static int path_chkpaths(Pathcomp_t *first, Pathcomp_t* old,Pathcomp_t *pp, int offset)
1469{
1470	struct stat statb;
1471	int k,m,n,fd;
1472	char *sp,*cp,*ep;
1473	stakseek(offset+pp->len);
1474	if(pp->len==1 && *stakptr(offset)=='/')
1475		stakseek(offset);
1476	stakputs("/.paths");
1477	if((fd=open(stakptr(offset),O_RDONLY))>=0)
1478	{
1479		fstat(fd,&statb);
1480		n = statb.st_size;
1481		stakseek(offset+pp->len+n+2);
1482		sp = stakptr(offset+pp->len);
1483		*sp++ = '/';
1484		n=read(fd,cp=sp,n);
1485		sp[n] = 0;
1486		close(fd);
1487		for(ep=0; n--; cp++)
1488		{
1489			if(*cp=='=')
1490			{
1491				ep = cp+1;
1492				continue;
1493			}
1494			else if(*cp!='\r' &&  *cp!='\n')
1495				continue;
1496			if(*sp=='#' || sp==cp)
1497			{
1498				sp = cp+1;
1499				continue;
1500			}
1501			*cp = 0;
1502			m = ep ? (ep-sp) : 0;
1503			if(m==0 || m==6 && memcmp((void*)sp,(void*)"FPATH=",6)==0)
1504			{
1505				if(first)
1506				{
1507					char *ptr = stakptr(offset+pp->len+1);
1508					if(ep)
1509						strcpy(ptr,ep);
1510					path_addcomp(first,old,stakptr(offset),PATH_FPATH|PATH_BFPATH);
1511				}
1512			}
1513			else if(m==12 && memcmp((void*)sp,(void*)"BUILTIN_LIB=",12)==0)
1514			{
1515				if(!(pp->flags & PATH_BUILTIN_LIB) || strchr(ep,'-'))
1516				{
1517					if ((pp->flags & (PATH_BUILTIN_LIB|PATH_STD_DIR)) == PATH_BUILTIN_LIB)
1518					{
1519						free(pp->blib);
1520						pp->blib = 0;
1521					}
1522					pp->flags |= PATH_BUILTIN_LIB;
1523					if (*ep == '.' && !*(ep + 1))
1524						pp->flags |= PATH_STD_DIR;
1525					else
1526					{
1527						k = strlen(ep)+1;
1528						if (*ep != '/')
1529							k +=  pp->len+1;
1530						pp->blib = sp = malloc(k);
1531						if (*ep != '/')
1532						{
1533							strcpy(pp->blib,pp->name);
1534							sp += pp->len;
1535							*sp++ = '/';
1536						}
1537						strcpy(sp,ep);
1538					}
1539				}
1540			}
1541			else if(m)
1542			{
1543				pp->lib = (char*)malloc(cp-sp+pp->len+2);
1544				memcpy((void*)pp->lib,(void*)sp,m);
1545				memcpy((void*)&pp->lib[m],stakptr(offset),pp->len);
1546				pp->lib[k=m+pp->len] = '/';
1547				strcpy((void*)&pp->lib[k+1],ep);
1548				pathcanon(&pp->lib[m],0);
1549				if(!first)
1550				{
1551					stakseek(0);
1552					stakputs(pp->lib);
1553					free((void*)pp->lib);
1554					return(1);
1555				}
1556			}
1557			sp = cp+1;
1558			ep = 0;
1559		}
1560	}
1561	return(0);
1562}
1563
1564
1565Pathcomp_t *path_addpath(Pathcomp_t *first, register const char *path,int type)
1566{
1567	register const char *cp;
1568	Pathcomp_t *old=0;
1569	int offset = staktell();
1570	char *savptr;
1571
1572	if(!path && type!=PATH_PATH)
1573		return(first);
1574	if(type!=PATH_FPATH)
1575	{
1576		old = first;
1577		first = 0;
1578	}
1579	if(offset)
1580		savptr = stakfreeze(0);
1581	if(path) while(*(cp=path))
1582	{
1583		if(*cp==':')
1584		{
1585			if(type!=PATH_FPATH)
1586				first = path_addcomp(first,old,".",type);
1587			while(*++path == ':');
1588		}
1589		else
1590		{
1591			int c;
1592			while(*path && *path!=':')
1593				path++;
1594			c = *path++;
1595			first = path_addcomp(first,old,cp,type);
1596			if(c==0)
1597				break;
1598			if(*path==0)
1599				path--;
1600		}
1601	}
1602	if(old)
1603	{
1604		if(!first && !path)
1605		{
1606			Pathcomp_t *pp = (Pathcomp_t*)old->shp->defpathlist;
1607			if(!pp)
1608				pp = defpath_init(old->shp);
1609			first = path_dup(pp);
1610		}
1611		if(cp=(FPATHNOD)->nvalue.cp)
1612			first = (void*)path_addpath((Pathcomp_t*)first,cp,PATH_FPATH);
1613		path_delete(old);
1614	}
1615	if(offset)
1616		stakset(savptr,offset);
1617	else
1618		stakseek(0);
1619	return(first);
1620}
1621
1622/*
1623 * duplicate the path give by <first> by incremented reference counts
1624 */
1625Pathcomp_t *path_dup(Pathcomp_t *first)
1626{
1627	register Pathcomp_t *pp=first;
1628	while(pp)
1629	{
1630		pp->refcount++;
1631		pp = pp->next;
1632	}
1633	return(first);
1634}
1635
1636/*
1637 * called whenever the directory is changed
1638 */
1639void path_newdir(Pathcomp_t *first)
1640{
1641	register Pathcomp_t *pp=first, *next, *pq;
1642	struct stat statb;
1643	for(pp=first; pp; pp=pp->next)
1644	{
1645		pp->flags &= ~PATH_SKIP;
1646		if(*pp->name=='/')
1647			continue;
1648		/* delete .paths component */
1649		if((next=pp->next) && (next->flags&PATH_BFPATH))
1650		{
1651			pp->next = next->next;
1652			if(--next->refcount<=0)
1653				free((void*)next);
1654		}
1655		if(stat(pp->name,&statb)<0 || !S_ISDIR(statb.st_mode))
1656		{
1657			pp->dev = 0;
1658			pp->ino = 0;
1659			continue;
1660		}
1661		pp->dev = statb.st_dev;
1662		pp->ino = statb.st_ino;
1663		pp->mtime = statb.st_mtime;
1664		for(pq=first;pq!=pp;pq=pq->next)
1665		{
1666			if(pp->ino==pq->ino && pp->dev==pq->dev)
1667				pp->flags |= PATH_SKIP;
1668		}
1669		for(pq=pp;pq=pq->next;)
1670		{
1671			if(pp->ino==pq->ino && pp->dev==pq->dev)
1672				pq->flags |= PATH_SKIP;
1673		}
1674		if((pp->flags&(PATH_PATH|PATH_SKIP))==PATH_PATH)
1675		{
1676			/* try to insert .paths component */
1677			int offset = staktell();
1678			stakputs(pp->name);
1679			stakseek(offset);
1680			next = pp->next;
1681			pp->next = 0;
1682			path_chkpaths(first,(Pathcomp_t*)0,pp,offset);
1683			if(pp->next)
1684				pp = pp->next;
1685			pp->next = next;
1686		}
1687	}
1688#if 0
1689	path_dump(first);
1690#endif
1691}
1692
1693Pathcomp_t *path_unsetfpath(Pathcomp_t *first)
1694{
1695	register Pathcomp_t *pp=first, *old=0;
1696	Shell_t	*shp = &sh;
1697	if(shp->fpathdict)
1698	{
1699		struct Ufunction  *rp, *rpnext;
1700		for(rp=(struct Ufunction*)dtfirst(shp->fpathdict);rp;rp=rpnext)
1701		{
1702			rpnext = (struct Ufunction*)dtnext(shp->fpathdict,rp);
1703			if(rp->fdict)
1704				nv_delete(rp->np,rp->fdict,NV_NOFREE);
1705			rp->fdict = 0;
1706		}
1707	}
1708	while(pp)
1709	{
1710		if((pp->flags&PATH_FPATH) && !(pp->flags&PATH_BFPATH))
1711		{
1712			if(pp->flags&PATH_PATH)
1713				pp->flags &= ~PATH_FPATH;
1714			else
1715			{
1716				Pathcomp_t *ppsave=pp;
1717				if(old)
1718					old->next = pp->next;
1719				else
1720					first = pp->next;
1721				pp = pp->next;
1722				if(--ppsave->refcount<=0)
1723				{
1724					if(ppsave->lib)
1725						free((void*)ppsave->lib);
1726					free((void*)ppsave);
1727				}
1728				continue;
1729			}
1730
1731		}
1732		old = pp;
1733		pp = pp->next;
1734	}
1735	return(first);
1736}
1737
1738Pathcomp_t *path_dirfind(Pathcomp_t *first,const char *name,int c)
1739{
1740	register Pathcomp_t *pp=first;
1741	while(pp)
1742	{
1743		if(memcmp(name,pp->name,pp->len)==0 && name[pp->len]==c)
1744			return(pp);
1745		pp = pp->next;
1746	}
1747	return(0);
1748}
1749
1750/*
1751 * get discipline for tracked alias
1752 */
1753static char *talias_get(Namval_t *np, Namfun_t *nvp)
1754{
1755	Pathcomp_t *pp = (Pathcomp_t*)np->nvalue.cp;
1756	char *ptr;
1757	if(!pp)
1758		return(NULL);
1759	path_nextcomp(pp,nv_name(np),pp);
1760	ptr = stakfreeze(0);
1761	return(ptr+PATH_OFFSET);
1762}
1763
1764static void talias_put(register Namval_t* np,const char *val,int flags,Namfun_t *fp)
1765{
1766	if(!val && np->nvalue.cp)
1767	{
1768		Pathcomp_t *pp = (Pathcomp_t*)np->nvalue.cp;
1769		if(--pp->refcount<=0)
1770			free((void*)pp);
1771	}
1772	nv_putv(np,val,flags,fp);
1773}
1774
1775static const Namdisc_t talias_disc   = { 0, talias_put, talias_get   };
1776static Namfun_t  talias_init = { &talias_disc, 1 };
1777
1778/*
1779 *  set tracked alias node <np> to value <pp>
1780 */
1781void path_alias(register Namval_t *np,register Pathcomp_t *pp)
1782{
1783	if(pp)
1784	{
1785		struct stat statb;
1786		char *sp;
1787		nv_offattr(np,NV_NOPRINT);
1788		nv_stack(np,&talias_init);
1789		np->nvalue.cp = (char*)pp;
1790		pp->refcount++;
1791		nv_setattr(np,NV_TAGGED|NV_NOFREE);
1792		path_nextcomp(pp,nv_name(np),pp);
1793		sp = stakptr(PATH_OFFSET);
1794		if(sp && lstat(sp,&statb)>=0 && S_ISLNK(statb.st_mode))
1795			nv_setsize(np,statb.st_size+1);
1796		else
1797			nv_setsize(np,0);
1798	}
1799	else
1800		nv_unset(np);
1801}
1802
1803