Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
att
GitHub Repository: att/ast
Path: blob/master/src/cmd/ksh93/sh/path.c
1810 views
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 <[email protected]> *
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(Shell_t*,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(Shell_t*,Pathcomp_t*,Pathcomp_t*,Pathcomp_t*,int);
58
static void path_checkdup(Shell_t *shp,register Pathcomp_t*);
59
60
static const char *std_path;
61
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
76
#if SHOPT_PFSH
77
int 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
103
static 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
134
static 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
*/
152
static 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
*/
235
char *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
}
283
skip:
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
*/
292
void 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
*/
318
static 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
352
void 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
*/
367
static 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
*/
408
Pathcomp_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
451
static 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
457
static 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
*/
482
Pathcomp_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
*/
504
static 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
*/
545
int 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
554
char *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
563
char *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
*/
586
static 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
655
int 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
*/
732
Pathcomp_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
927
static 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);
968
err:
969
return(-1);
970
}
971
972
/*
973
* Return path relative to present working directory
974
*/
975
976
char *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
999
void 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
1045
pid_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);
1173
retry:
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
1258
static 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
*/
1448
static 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
*/
1501
static 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
1576
Pathcomp_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
*/
1636
Pathcomp_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
*/
1650
void 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
1704
Pathcomp_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
1749
Pathcomp_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
*/
1764
static 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
1776
static 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
1787
static const Namdisc_t talias_disc = { 0, talias_put, talias_get };
1788
static Namfun_t talias_init = { &talias_disc, 1 };
1789
1790
/*
1791
* set tracked alias node <np> to value <pp>
1792
*/
1793
void 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
1816