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