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