Add GNU LGPL headers to all .c .C and .h files
[oweals/cde.git] / cde / programs / dtksh / ksh93 / src / cmd / ksh93 / sh / path.c
1 /*
2  * CDE - Common Desktop Environment
3  *
4  * Copyright (c) 1993-2012, The Open Group. All rights reserved.
5  *
6  * These libraries and programs are free software; you can
7  * redistribute them and/or modify them under the terms of the GNU
8  * Lesser General Public License as published by the Free Software
9  * Foundation; either version 2 of the License, or (at your option)
10  * any later version.
11  *
12  * These libraries and programs are distributed in the hope that
13  * they will be useful, but WITHOUT ANY WARRANTY; without even the
14  * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15  * PURPOSE. See the GNU Lesser General Public License for more
16  * details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with these librararies and programs; if not, write
20  * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
21  * Floor, Boston, MA 02110-1301 USA
22  */
23 /* $XConsortium: path.c /main/3 1995/11/01 16:50:31 rswiston $ */
24 /***************************************************************
25 *                                                              *
26 *                      AT&T - PROPRIETARY                      *
27 *                                                              *
28 *        THIS IS UNPUBLISHED PROPRIETARY SOURCE CODE OF        *
29 *                    AT&T BELL LABORATORIES                    *
30 *         AND IS NOT TO BE DISCLOSED OR USED EXCEPT IN         *
31 *            ACCORDANCE WITH APPLICABLE AGREEMENTS             *
32 *                                                              *
33 *                Copyright (c) 1995 AT&T Corp.                 *
34 *              Unpublished & Not for Publication               *
35 *                     All Rights Reserved                      *
36 *                                                              *
37 *       The copyright notice above does not evidence any       *
38 *      actual or intended publication of such source code      *
39 *                                                              *
40 *               This software was created by the               *
41 *           Advanced Software Technology Department            *
42 *                    AT&T Bell Laboratories                    *
43 *                                                              *
44 *               For further information contact                *
45 *                    {research,attmail}!dgk                    *
46 *                                                              *
47 ***************************************************************/
48
49 /* : : generated by proto : : */
50
51 #if !defined(__PROTO__)
52 #if defined(__STDC__) || defined(__cplusplus) || defined(_proto) || defined(c_plusplus)
53 #if defined(__cplusplus)
54 #define __MANGLE__      "C"
55 #else
56 #define __MANGLE__
57 #endif
58 #define __STDARG__
59 #define __PROTO__(x)    x
60 #define __OTORP__(x)
61 #define __PARAM__(n,o)  n
62 #if !defined(__STDC__) && !defined(__cplusplus)
63 #if !defined(c_plusplus)
64 #define const
65 #endif
66 #define signed
67 #define void            int
68 #define volatile
69 #define __V_            char
70 #else
71 #define __V_            void
72 #endif
73 #else
74 #define __PROTO__(x)    ()
75 #define __OTORP__(x)    x
76 #define __PARAM__(n,o)  o
77 #define __MANGLE__
78 #define __V_            char
79 #define const
80 #define signed
81 #define void            int
82 #define volatile
83 #endif
84 #if defined(__cplusplus) || defined(c_plusplus)
85 #define __VARARG__      ...
86 #else
87 #define __VARARG__
88 #endif
89 #if defined(__STDARG__)
90 #define __VA_START__(p,a)       va_start(p,a)
91 #else
92 #define __VA_START__(p,a)       va_start(p)
93 #endif
94 #endif
95 #include        "defs.h"
96 #include        <fcin.h>
97 #include        <ls.h>
98 #include        "variables.h"
99 #include        "path.h"
100 #include        "io.h"
101 #include        "jobs.h"
102 #include        "history.h"
103 #include        "test.h"
104 #include        "FEATURE/externs"
105
106 #define RW_ALL  (S_IRUSR|S_IRGRP|S_IROTH|S_IWUSR|S_IWGRP|S_IWOTH)
107
108 static const char       usrbin[] = "/usr/bin";
109 static char             *prune __PROTO__((char*, const char*));
110 static char             *execs __PROTO__((const char*, const char*, char**));
111 static int              canexecute __PROTO__((char*,int));
112 static char             *path_to_stak __PROTO__((const char*));
113 static int              path_inpath __PROTO__((const char*,const char*));
114 static void             funload __PROTO__((int,const char*));
115 static void             exscript __PROTO__((char*, char*[]));
116 #ifdef SHOPT_VPIX
117     static int          suffix_offset;
118     extern __MANGLE__ char              *suffix_list[];
119 #endif /* SHOPT_VPIX */
120
121 static Namval_t         *tracknod;
122 static char             **xecenv;
123 static int              pruned;
124 static int              exec_err;
125
126 /*
127  * make sure PWD is set up correctly
128  * Return the present working directory
129  * Invokes getcwd() if flag==0 and if necessary
130  * Sets the PWD variable to this value
131  */
132 char *path_pwd __PARAM__((int flag), (flag)) __OTORP__(int flag;){
133         register char *cp;
134         register char *dfault = (char*)e_dot;
135         register int count = 0;
136         if(sh.pwd)
137                 return((char*)sh.pwd);
138         while(1) 
139         {
140                 /* try from lowest to highest */
141                 switch(count++)
142                 {
143                         case 0:
144                                 cp = nv_getval(PWDNOD);
145                                 break;
146                         case 1:
147                                 cp = nv_getval(HOME);
148                                 break;
149                         case 2:
150                                 cp = "/";
151                                 break;
152                         case 3:
153                                 cp = (char*)e_crondir;
154                                 if(flag) /* skip next case when non-zero flag */
155                                         ++count;
156                                 break;
157                         case 4:
158                         {
159                                 if(cp=getcwd(NIL(char*),0))
160                                 {  
161                                         nv_offattr(PWDNOD,NV_NOFREE);
162                                         nv_unset(PWDNOD);
163                                         PWDNOD->nvalue.cp = cp;
164                                         goto skip;
165                                 }
166                                 break;
167                         }
168                         case 5:
169                                 return(dfault);
170                 }
171                 if(cp && *cp=='/' && test_inode(cp,e_dot))
172                         break;
173         }
174         if(count>1)
175         {
176                 nv_offattr(PWDNOD,NV_NOFREE);
177                 nv_putval(PWDNOD,cp,NV_RDONLY);
178         }
179 skip:
180         nv_onattr(PWDNOD,NV_NOFREE|NV_EXPORT);
181         sh.pwd = (char*)(PWDNOD->nvalue.cp);
182         return(cp);
183 }
184
185 /*
186  * given <s> return a colon separated list of directories to search on the stack
187  * This routine adds names to the tracked alias list, if possible, and returns
188  * a reduced path string for tracked aliases
189  */
190
191 char *path_get __PARAM__((const char *name), (name)) __OTORP__(const char *name;){
192         register char *path=0;
193         register char *sp = sh.lastpath;
194         static int bin_is_usrbin = -1;
195         if(strchr(name,'/'))
196                 return("");
197         if(!sh_isstate(SH_DEFPATH))
198                 path = nv_getval(nv_scoped(PATHNOD));
199         if(!path)
200                 path = (char*)e_defpath;
201         if(bin_is_usrbin < 0)
202                 bin_is_usrbin = test_inode(usrbin+4,usrbin);
203         if(bin_is_usrbin)
204                 path = path_to_stak(path); 
205         else
206                 path = stakcopy(path);
207         if(sh_isstate(SH_DEFPATH))
208                 return(path);
209         if(sp || *name && ((tracknod=nv_search(name,sh.track_tree,0)) &&
210                 nv_isattr(tracknod,NV_NOALIAS)==0 &&
211                 (sp=nv_getval(tracknod))))
212         {
213                 path = prune(path,sp);
214                 pruned++;
215         }
216         return(path);
217 }
218
219 /*
220  * convert each /usr/bin to /bin and eliminate multiple /bin from <path>
221  * The resulting path is placed on the stack
222  */
223 static char *path_to_stak __PARAM__((register const char *path), (path)) __OTORP__(register const char *path;){
224         register const char *cp=path, *base;
225         register int n, nbin=0;
226         stakseek(0);
227         while(*cp)
228         {
229                 while(*cp==':')
230                         cp++;
231                 base = cp;
232                 while((n= *cp) && n!=':')
233                         cp++;
234                 if((n=cp-base)==8 && strncmp(usrbin,base,8)==0)
235                         ;
236                 else if(n!=4 || strncmp(usrbin+4,base,4))
237                         continue;
238                 if((n=base-path) > 0)
239                         stakwrite(path,n);
240                 path = cp;
241                 if(nbin++ == 0)
242                         stakwrite(usrbin+4,4);
243                 else
244                         path++;
245                 
246         }
247         if((n=cp-path) > 0)
248                 stakwrite(path,n);
249         return(stakfreeze(1));
250 }
251
252 int     path_open __PARAM__((const char *name,char *path), (name, path)) __OTORP__(const char *name;char *path;){
253         register int fd;
254         struct stat statb;
255         if(strchr(name,'/'))
256         {
257                 if(sh_isoption(SH_RESTRICTED))
258                         error(ERROR_exit(1),e_restricted,name);
259         }
260         else
261         {
262                 if(!path)
263                         path = (char*)e_defpath;
264                 path = stakcopy(path);
265         }
266         do
267         {
268                 path=path_join(path,name);
269                 if((fd = sh_open(path_relative(stakptr(PATH_OFFSET)),O_RDONLY,0)) >= 0)
270                 {
271                         if(fstat(fd,&statb)<0 || S_ISDIR(statb.st_mode))
272                         {
273                                 errno = EISDIR;
274                                 sh_close(fd);
275                                 fd = -1;
276                         }
277                 }
278         }
279         while( fd<0 && path);
280         if((fd = sh_iomovefd(fd)) > 0)
281         {
282                 fcntl(fd,F_SETFD,FD_CLOEXEC);
283                 sh.fdstatus[fd] |= IOCLEX;
284         }
285         return(fd);
286 }
287
288 /*
289  * This routine returns 1 if first directory in <path> is also in <fpath>
290  * If <path> begins with :, or first directory is ., $PWD must be in <fpath>
291  * Otherwise, 0 is returned
292  */
293
294 static int path_inpath __PARAM__((const char *path, const char *fpath), (path, fpath)) __OTORP__(const char *path; const char *fpath;){
295         register const char *dp = fpath;
296         register const char *sp = path;
297         register int c, match=1;
298         if(!dp || !sp || *sp==0)
299                 return(0);
300         if(*sp==':' || (*sp=='.' && ((c=sp[1])==0 || c==':')))
301                 sp = path = path_pwd(1);
302         while(1)
303         {
304                 if((c= *dp++)==0 || c == ':')
305                 {
306                         if(match==1 && sp>path && (*sp==0 || *sp==':'))
307                                 return(1);
308                         if(c==0)
309                                 return(0);
310                         match = 1;
311                         sp = path;
312                 }
313                 else if(match)
314                 {
315                         if(*sp++ != c)
316                                 match = 0;
317                 }
318         }
319         /* NOTREACHED */
320 }
321
322 /*
323  *  set tracked alias node <np> to value <sp>
324  */
325
326 void path_alias __PARAM__((register Namval_t *np,register char *sp), (np, sp)) __OTORP__(register Namval_t *np;register char *sp;){
327         if(!sp)
328                 nv_unset(np);
329         else
330         {
331                 register const char *vp = np->nvalue.cp;
332                 register int n = 1;
333                 register int nofree = nv_isattr(np,NV_NOFREE);
334                 nv_offattr(np,NV_NOPRINT);
335                 if(!vp || (n=strcmp(sp,vp))!=0)
336                 {
337                         int subshell = sh.subshell;
338                         sh.subshell = 0;
339                         nv_putval(np,sp,0);
340                         sh.subshell = subshell;
341                 }
342                 nv_setattr(np,NV_TAGGED|NV_EXPORT);
343                 if(nofree && n==0)
344                         nv_onattr(np,NV_NOFREE);
345         }
346 }
347
348
349 /*
350  * given a pathname return the base name
351  */
352
353 char    *path_basename __PARAM__((register const char *name), (name)) __OTORP__(register const char *name;){
354         register const char *start = name;
355         while (*name)
356                 if ((*name++ == '/') && *name)  /* don't trim trailing / */
357                         start = name;
358         return ((char*)start);
359 }
360
361 /*
362  * load functions from file <fno>
363  */
364 static void funload __PARAM__((int fno, const char *name), (fno, name)) __OTORP__(int fno; const char *name;){
365         char buff[IOBSIZE+1];
366         int savestates = sh_isstate(~0);
367         sh_onstate(SH_NOLOG|SH_NOALIAS);
368         sh.readscript = (char*)name;
369         sh_eval(sfnew(NIL(Sfio_t*),buff,IOBSIZE,fno,SF_READ),0);
370         sh.readscript = 0;
371         sh_setstate(savestates);
372 }
373
374 /*
375  * do a path search and track alias if requested
376  * if flag is 0, or if name not found, then try autoloading function
377  * if flag==2, returns 1 if name found on FPATH
378  * returns 1, if function was autoloaded.
379  * If endpath!=NULL, Path search ends when path matches endpath.
380  */
381
382 int     path_search __PARAM__((register const char *name,const char *endpath, int flag), (name, endpath, flag)) __OTORP__(register const char *name;const char *endpath; int flag;){
383         register Namval_t *np;
384         register int fno;
385         if(flag)
386         {
387                 /* if not found on pruned path, rehash and try again */
388                 while(!(sh.lastpath=path_absolute(name,endpath)) && pruned)
389                         nv_onattr(tracknod,NV_NOALIAS);
390                 if(!sh.lastpath && (np=nv_search(name,sh.fun_tree,HASH_NOSCOPE))&&np->nvalue.ip)
391                         return(1);
392                 np = 0;
393         }
394         if(flag==0 || !sh.lastpath)
395         {
396                 register char *path=0;
397                 if(strmatch(name,e_alphanum))
398                         path = nv_getval(nv_scoped(FPATHNOD));
399                 if(path && (fno=path_open(name,path))>=0)
400                 {
401                         if(flag==2)
402                         {
403                                 sh_close(fno);
404                                 return(1);
405                         }
406                         funload(fno,name);
407                         if((np=nv_search(name,sh.fun_tree,HASH_NOSCOPE))&&np->nvalue.ip)
408                                 return(1);
409                 }
410                 return(0);
411         }
412         else if(!sh_isstate(SH_DEFPATH))
413         {
414                 if((np=tracknod) || ((endpath||sh_isoption(SH_TRACKALL)) &&
415                         (np=nv_search(name,sh.track_tree,NV_ADD)))
416                   )
417                         path_alias(np,sh.lastpath);
418         }
419         return(0);
420 }
421
422
423 /*
424  * do a path search and find the full pathname of file name
425  * end search of path matches endpath without checking execute permission
426  */
427
428 char    *path_absolute __PARAM__((register const char *name, const char *endpath), (name, endpath)) __OTORP__(register const char *name; const char *endpath;){
429         register int    f;
430         register char *path;
431         register const char *fpath=0;
432         register int isfun;
433 #ifdef SHOPT_VPIX
434         char **suffix = 0;
435         char *top;
436 #endif /* SHOPT_VPIX */
437         pruned = 0;
438         path = path_get(name);
439         if(*path && strmatch(name,e_alphanum))
440                 fpath = nv_getval(nv_scoped(FPATHNOD));
441         do
442         {
443                 sh_sigcheck();
444                 isfun = (fpath && path_inpath(path,fpath));
445 #ifdef SHOPT_VPIX
446                 if(!suffix)
447                 {
448                         if(!isfun && path_inpath(path,nv_getval(nv_scoped(DOSPATHNOD))))
449                                 suffix = suffix_list;
450                         path=path_join(path,name);
451                         if(suffix)
452                                 top = stakptr(suffix_offset);
453                 }
454                 if(suffix)
455                 {
456
457                         strcpy(top,*suffix);
458                         if(**suffix==0)
459                                 suffix = 0;
460                         else
461                                 suffix++;
462                 }
463 #else
464                 path=path_join(path,name);
465 #endif /* SHOPT_VPIX */
466                 if(endpath && strcmp(endpath,stakptr(PATH_OFFSET))==0)
467                         return((char*)endpath);
468                 f = canexecute(stakptr(PATH_OFFSET),isfun);
469                 if(isfun && f>=0)
470                 {
471                         funload(f,stakptr(PATH_OFFSET));
472                         f = -1;
473                         path = 0;
474                 }
475         }
476 #ifdef SHOPT_VPIX
477         while(f<0 && (path||suffix));
478 #else
479         while(f<0 && path);
480 #endif /* SHOPT_VPIX */
481         if(f<0)
482                 return(0);
483         /* check for relative pathname */
484         if(*stakptr(PATH_OFFSET)!='/')
485                 path_join(path_pwd(1),(char*)stakfreeze(1)+PATH_OFFSET);
486         return((char*)stakfreeze(1)+PATH_OFFSET);
487 }
488
489 /*
490  * returns 0 if path can execute
491  * sets exec_err if file is found but can't be executable
492  */
493 #undef S_IXALL
494 #ifdef S_IXUSR
495 #   define S_IXALL      (S_IXUSR|S_IXGRP|S_IXOTH)
496 #else
497 #   ifdef S_IEXEC
498 #       define S_IXALL  (S_IEXEC|(S_IEXEC>>3)|(S_IEXEC>>6))
499 #   else
500 #       define S_IXALL  0111
501 #   endif /*S_EXEC */
502 #endif /* S_IXUSR */
503
504 static int canexecute __PARAM__((register char *path, int isfun), (path, isfun)) __OTORP__(register char *path; int isfun;){
505         struct stat statb;
506         register int fd=0;
507         path = path_relative(path);
508         if(isfun)
509         {
510                 if((fd=open(path,O_RDONLY,0))<0 || fstat(fd,&statb)<0)
511                         goto err;
512         }
513         else if(stat(path,&statb) < 0)
514         {
515 #ifdef _WIN32
516                 /* check for .exe suffix */
517                 if(errno==ENOENT)
518                 {
519                         stakputs(".exe");
520                         path = stakptr(PATH_OFFSET);
521                         if(stat(path,&statb) < 0)
522                                 goto err;
523                 }
524                 else
525 #endif /* _WIN32 */
526                 goto err;
527         }
528         errno = EPERM;
529         if(S_ISDIR(statb.st_mode))
530                 errno = EISDIR;
531         else if((statb.st_mode&S_IXALL)==S_IXALL || sh_access(path,X_OK)>=0)
532                 return(fd);
533         if(isfun && fd>=0)
534                 sh_close(fd);
535 err:
536         if(errno!=ENOENT)
537                 exec_err = errno;
538         return(-1);
539 }
540
541 /*
542  * Return path relative to present working directory
543  */
544
545 char *path_relative __PARAM__((register const char* file), (file)) __OTORP__(register const char* file;){
546         register const char *pwd;
547         register const char *fp = file;
548         /* can't relpath when sh.pwd not set */
549         if(!(pwd=sh.pwd))
550                 return((char*)fp);
551         while(*pwd==*fp)
552         {
553                 if(*pwd++==0)
554                         return((char*)e_dot);
555                 fp++;
556         }
557         if(*pwd==0 && *fp == '/')
558         {
559                 while(*++fp=='/');
560                 if(*fp)
561                         return((char*)fp);
562                 return((char*)e_dot);
563         }
564         return((char*)file);
565 }
566
567 char *path_join __PARAM__((register char *path,const char *name), (path, name)) __OTORP__(register char *path;const char *name;){
568         /* leaves result on top of stack */
569         register char *scanp = path;
570         register int c;
571         stakseek(PATH_OFFSET);
572         if(*scanp=='.')
573         {
574                 if((c= *++scanp)==0 || c==':')
575                         path = scanp;
576                 else if(c=='/')
577                         path = ++scanp;
578                 else
579                         scanp--;
580         }
581         while(*scanp && *scanp!=':')
582                 stakputc(*scanp++);
583         if(scanp!=path)
584         {
585                 stakputc('/');
586                 /* position past ":" unless a trailing colon after pathname */
587                 if(*scanp && *++scanp==0)
588                         scanp--;
589         }
590         else
591                 while(*scanp == ':')
592                         scanp++;
593         path=(*scanp ? scanp : 0);
594         stakputs(name);
595 #ifdef SHOPT_VPIX
596         /* make sure that there is room for suffixes */
597         suffix_offset = staktell();
598         stakputs(*suffix_list);
599         *stakptr(suffix_offset) = 0;
600 #endif /*SHOPT_VPIX */
601         return((char*)path);
602 }
603
604
605 void    path_exec __PARAM__((register const char *arg0,register char *argv[],struct argnod *local), (arg0, argv, local)) __OTORP__(register const char *arg0;register char *argv[];struct argnod *local;){
606         register const char *path = "";
607         nv_setlist(local,NV_EXPORT|NV_IDENT|NV_ASSIGN);
608         xecenv=sh_envgen();
609         if(strchr(arg0,'/'))
610         {
611                 /* name containing / not allowed for restricted shell */
612                 if(sh_isoption(SH_RESTRICTED))
613                         error(ERROR_exit(1),e_restricted,arg0);
614         }
615         else
616                 path=path_get(arg0);
617         /* leave room for inserting _= pathname in environment */
618         xecenv--;
619         exec_err = ENOENT;
620         sfsync(NIL(Sfio_t*));
621         timerdel(NIL(__V_*));
622         while(path=execs(path,arg0,argv));
623         /* force an exit */
624         ((struct checkpt*)sh.jmplist)->mode = SH_JMPEXIT;
625         if((errno = exec_err)==ENOENT)
626                 error(ERROR_exit(ERROR_NOENT),e_found,arg0);
627         else
628                 error(ERROR_system(ERROR_NOEXEC),e_exec,arg0);
629 }
630
631 /*
632  * This routine constructs a short path consisting of all
633  * Relative directories up to the directory of fullname <name>
634  */
635 static char *prune __PARAM__((register char *path,const char *fullname), (path, fullname)) __OTORP__(register char *path;const char *fullname;){
636         register char *p = path;
637         register char *s;
638         int n = 1; 
639         const char *base;
640         char *inpath = path;
641         if(!fullname  || *fullname != '/' || *path==0)
642                 return(path);
643         base = path_basename(fullname);
644         do
645         {
646                 /* a null path means current directory */
647                 if(*path == ':')
648                 {
649                         *p++ = ':';
650                         path++;
651                         continue;
652                 }
653                 s = path;
654                 path=path_join(path,base);
655                 if(*s != '/' || (n=strcmp(stakptr(PATH_OFFSET),fullname))==0)
656                 {
657                         /* position p past end of path */
658                         while(*s && *s!=':')
659                                 *p++ = *s++;
660                         if(n==0 || !path)
661                         {
662                                 *p = 0;
663                                 return(inpath);
664                         }
665                         *p++ = ':';
666                 }
667         }
668         while(path);
669         /* if there is no match just return path */
670         path = nv_getval(nv_scoped(PATHNOD));
671         if(!path)
672                 path = (char*)e_defpath;
673         strcpy(inpath,path);
674         return(inpath);
675 }
676
677
678 static char *execs __PARAM__((const char *ap,const char *arg0,register char **argv), (ap, arg0, argv)) __OTORP__(const char *ap;const char *arg0;register char **argv;){
679         register char *path, *prefix;
680         sh_sigcheck();
681         prefix=path_join((char*)ap,arg0);
682         xecenv[0] =  stakptr(0);
683         *stakptr(0) = '_';
684         *stakptr(1) = '=';
685         path=stakptr(PATH_OFFSET);
686         sfsync(sfstderr);
687 #ifdef SHOPT_VPIX
688         if(path_inpath(ap,nv_getval(nv_scoped(DOSPATHNOD))))
689         {
690                 char **suffix;
691                 char *savet = argv[0];
692                 argv[0] = path;
693                 argv[-2] = (char*)e_vpix+1;
694                 argv[-1] = "-c";
695                 suffix = suffix_list;
696                 while(**suffix)
697                 {
698                         char *vp;
699                         strcpy(stakptr(suffix_offset),*suffix++);
700                         if(canexecute(path,0)>=0)
701                         {
702                                 stakfreeze(1);
703                                 if(path=nv_getval(nv_scoped(VPIXNOD)))
704                                         stakputs(path);
705                                 else
706                                         stakputs(e_vpixdir);
707                                 stakputs(e_vpix);
708                                 execve(stakptr(0), &argv[-2] ,xecenv);
709                                 switch(errno)
710                                 {
711                                     case ENOENT:
712                                         error(ERROR_system(ERROR_NOENT),e_found,vp);
713                                     default:
714                                         error(ERROR_system(ERROR_NOEXEC),e_exec,vp);
715                                 }
716                         }
717                 }
718                 argv[0] = savet;
719                 *stakptr(suffix_offset) = 0;
720         }
721 #endif /* SHOPT_VPIX */
722         sh_sigcheck();
723         path = path_relative(path);
724 #ifdef SHELLMAGIC
725         if(*path!='/' && path!=stakptr(PATH_OFFSET))
726         {
727                 /*
728                  * The following code because execv(foo,) and execv(./foo,)
729                  * may not yield the same resulst
730                  */
731                 char *sp = (char*)malloc(strlen(path)+3);
732                 sp[0] = '.';
733                 sp[1] = '/';
734                 strcpy(sp+2,path);
735                 path = sp;
736         }
737 #endif /* SHELLMAGIC */
738         execve(path, &argv[0] ,xecenv);
739 #ifdef SHELLMAGIC
740         if(*path=='.' && path!=stakptr(PATH_OFFSET))
741         {
742                 free(path);
743                 path = path_relative(stakptr(PATH_OFFSET));
744         }
745 #endif /* SHELLMAGIC */
746         switch(errno)
747         {
748 #ifdef apollo
749             /* 
750              * On apollo's execve will fail with eacces when
751              * file has execute but not read permissions. So,
752              * for now we will pretend that EACCES and ENOEXEC
753              * mean the same thing.
754              */
755             case EACCES:
756 #endif /* apollo */
757             case ENOEXEC:
758                 exscript(path,argv);
759 #ifndef apollo
760             case EACCES:
761             {
762                 struct stat statb;
763                 if(stat(path,&statb)>=0 && S_ISDIR(statb.st_mode))
764                         errno = EISDIR;
765             }
766                 /* FALL THROUGH */
767 #endif /* !apollo */
768 #ifdef ENAMETOOLONG
769             case ENAMETOOLONG:
770 #endif /* ENAMETOOLONG */
771             case EPERM:
772                 exec_err = errno;
773             case ENOTDIR:
774             case ENOENT:
775             case EINTR:
776 #ifdef EMLINK
777             case EMLINK:
778 #endif /* EMLINK */
779                 return(prefix);
780             default:
781                 error(ERROR_system(ERROR_NOEXEC),e_exec,path);
782         }
783 }
784
785 /*
786  * File is executable but not machine code.
787  * Assume file is a Shell script and execute it.
788  */
789
790
791 static void exscript __PARAM__((register char *path,register char *argv[]), (path, argv)) __OTORP__(register char *path;register char *argv[];){
792         register Sfio_t *sp;
793         sh.comdiv=0;
794         sh.bckpid = 0;
795         sh.st.ioset=0;
796         /* clean up any cooperating processes */
797         if(sh.cpipe[0]>0)
798                 sh_pclose(sh.cpipe);
799         if(sh.cpid)
800                 sh_close(*sh.outpipe);
801         sh.cpid = 0;
802         if(sp=fcfile())
803                 while(sfstack(sp,SF_POPSTACK));
804         job_clear();
805         if(sh.infd>0)
806                 sh_close(sh.infd);
807         sh_setstate(SH_FORKED);
808         sfsync(sfstderr);
809 #ifdef SHOPT_SUID_EXEC
810         /* check if file cannot open for read or script is setuid/setgid  */
811         {
812                 static char name[] = "/tmp/euidXXXXXXXXXX";
813                 register int n;
814                 register uid_t euserid;
815                 char *savet;
816                 struct stat statb;
817                 if((n=sh_open(path,O_RDONLY,0)) >= 0)
818                 {
819                         /* move <n> if n=0,1,2 */
820                         n = sh_iomovefd(n);
821                         if(fstat(n,&statb)>=0 && !(statb.st_mode&(S_ISUID|S_ISGID)))
822                                 goto openok;
823                         sh_close(n);
824                 }
825                 if((euserid=geteuid()) != sh.userid)
826                 {
827                         strncpy(name+9,fmtbase((long)getpid(),10,0),sizeof(name)-10);
828                         /* create a suid open file with owner equal effective uid */
829                         if((n=creat(name,S_ISUID|S_IXUSR)) < 0)
830                                 goto fail;
831                         unlink(name);
832                         /* make sure that file has right owner */
833                         if(fstat(n,&statb)<0 || statb.st_uid != euserid)
834                                 goto fail;
835                         if(n!=10)
836                         {
837                                 sh_close(10);
838                                 fcntl(n, F_DUPFD, 10);
839                                 sh_close(n);
840                                 n=10;
841                         }
842                 }
843                 savet = *--argv;
844                 *argv = path;
845                 execve(e_suidexec,argv,xecenv);
846         fail:
847                 /*
848                  *  The following code is just for compatibility
849                  */
850                 if((n=open(path,O_RDONLY,0)) < 0)
851                         error(ERROR_system(1),e_open,path);
852                 *argv++ = savet;
853         openok:
854                 sh.infd = n;
855         }
856 #else
857         sh.infd = sh_chkopen(path);
858 #endif /* SHOPT_SUID_EXEC */
859 #ifdef SHOPT_ACCT
860         sh_accbegin(path) ;  /* reset accounting */
861 #endif  /* SHOPT_ACCT */
862         sh_reinit(argv);
863         sh.lastarg = strdup(path);
864         /* save name of calling command */
865         sh.readscript = error_info.id;
866         if(sh.heredocs)
867         {
868                 sfclose(sh.heredocs);
869                 sh.heredocs = 0;
870         }
871         /* close history file if name has changed */
872         if(sh.hist_ptr && (path=nv_getval(HISTFILE)) && strcmp(path,sh.hist_ptr->histname))
873         {
874                 hist_close(sh.hist_ptr);
875                 (HISTCUR)->nvalue.lp = 0;
876         }
877         sh_offstate(SH_FORKED);
878         siglongjmp(*sh.jmplist,SH_JMPSCRIPT);
879 }
880
881 #ifdef SHOPT_ACCT
882 #   include <sys/acct.h>
883 #   include "FEATURE/time"
884
885     static struct acct sabuf;
886     static struct tms buffer;
887     static clock_t      before;
888     static char *SHACCT; /* set to value of SHACCT environment variable */
889     static shaccton;    /* non-zero causes accounting record to be written */
890     static int compress __PROTO__((time_t));
891     /*
892      *  initialize accounting, i.e., see if SHACCT variable set
893      */
894     void sh_accinit __PARAM__((void), ()){
895         SHACCT = getenv("SHACCT");
896     }
897     /*
898     * suspend accounting unitl turned on by sh_accbegin()
899     */
900     void sh_accsusp __PARAM__((void), ()){
901         shaccton=0;
902 #ifdef AEXPAND
903         sabuf.ac_flag |= AEXPND;
904 #endif /* AEXPAND */
905     }
906
907     /*
908      * begin an accounting record by recording start time
909      */
910     void sh_accbegin __PARAM__((const char *cmdname), (cmdname)) __OTORP__(const char *cmdname;){
911         if(SHACCT)
912         {
913                 sabuf.ac_btime = time(NIL(time_t *));
914                 before = times(&buffer);
915                 sabuf.ac_uid = getuid();
916                 sabuf.ac_gid = getgid();
917                 strncpy(sabuf.ac_comm, (char*)path_basename(cmdname),
918                         sizeof(sabuf.ac_comm));
919                 shaccton = 1;
920         }
921     }
922     /*
923      * terminate an accounting record and append to accounting file
924      */
925     void        sh_accend __PARAM__((void), ()){
926         int     fd;
927         clock_t after;
928
929         if(shaccton)
930         {
931                 after = times(&buffer);
932                 sabuf.ac_utime = compress(buffer.tms_utime + buffer.tms_cutime);
933                 sabuf.ac_stime = compress(buffer.tms_stime + buffer.tms_cstime);
934                 sabuf.ac_etime = compress( (time_t)(after-before));
935                 fd = open( SHACCT , O_WRONLY | O_APPEND | O_CREAT,RW_ALL);
936                 write(fd, (const char*)&sabuf, sizeof( sabuf ));
937                 close( fd);
938         }
939     }
940  
941     /*
942      * Produce a pseudo-floating point representation
943      * with 3 bits base-8 exponent, 13 bits fraction.
944      */
945     static int compress __PARAM__((register time_t t), (t)) __OTORP__(register time_t t;){
946         register int exp = 0, rund = 0;
947
948         while (t >= 8192)
949         {
950                 exp++;
951                 rund = t&04;
952                 t >>= 3;
953         }
954         if (rund)
955         {
956                 t++;
957                 if (t >= 8192)
958                 {
959                         t >>= 3;
960                         exp++;
961                 }
962         }
963         return((exp<<13) + t);
964     }
965 #endif  /* SHOPT_ACCT */
966