Logo Search packages:      
Sourcecode: ksh version File versions  Download package

path.c

/***********************************************************************
*                                                                      *
*               This software is part of the ast package               *
*          Copyright (c) 1982-2009 AT&T Intellectual Property          *
*                      and is licensed under the                       *
*                  Common Public License, Version 1.0                  *
*                    by AT&T Intellectual Property                     *
*                                                                      *
*                A copy of the License is available at                 *
*            http://www.opensource.org/licenses/cpl1.0.txt             *
*         (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9)         *
*                                                                      *
*              Information and Software Systems Research               *
*                            AT&T Research                             *
*                           Florham Park NJ                            *
*                                                                      *
*                  David Korn <dgk@research.att.com>                   *
*                                                                      *
***********************************************************************/
#pragma prototyped
/*
 * David Korn
 * AT&T Labs
 *
 */

#include    "defs.h"
#include    <fcin.h>
#include    <ls.h>
#include    <nval.h>
#include    "variables.h"
#include    "path.h"
#include    "io.h"
#include    "jobs.h"
#include    "history.h"
#include    "test.h"
#include    "FEATURE/dynamic"
#include    "FEATURE/externs"
#if SHOPT_PFSH 
#   ifdef _hdr_exec_attr
#     include     <exec_attr.h>
#   else
#     undef SHOPT_PFSH
#   endif
#   if     _lib_vfork
#     include     <ast_vfork.h>
#   else
#     define vfork()      fork()
#   endif
#endif

#define RW_ALL    (S_IRUSR|S_IRGRP|S_IROTH|S_IWUSR|S_IWGRP|S_IWOTH)
#define LIBCMD    "cmd"


static int        canexecute(char*,int);
static void       funload(Shell_t*,int,const char*);
static void       exscript(Shell_t*,char*, char*[], char**);
static int        path_chkpaths(Pathcomp_t*,Pathcomp_t*,Pathcomp_t*,int);

static const char *std_path;

static int onstdpath(const char *name)
{
      register const char *cp = std_path, *sp;
      if(cp)
            while(*cp)
            {
                  for(sp=name; *sp && (*cp == *sp); sp++,cp++);
                  if(*sp==0 && (*cp==0 || *cp==':'))
                        return(1);
                  while(*cp && *cp++!=':');
            }
      return(0);
}

static pid_t path_pfexecve(const char *path, char *argv[],char *const envp[],int spawn)
{
#if SHOPT_PFSH 
      pid_t pid;
      char  resolvedpath[PATH_MAX + 1];
      if(spawn)
      {
            while((pid = vfork()) < 0)
                  _sh_fork(pid, 0, (int*)0);
            if(pid)
                  return(pid);
      }
      if(!sh_isoption(SH_PFSH))
            return(execve(path, argv, envp));
      /* Solaris implements realpath(3C) using the resolvepath(2) */
      /* system call so we can save us to call access(2) first */
      if (!realpath(path, resolvedpath))
            return -1;

      /* we can exec the command directly instead of via pfexec(1) if */
      /* there is a matching entry without attributes in exec_attr(4) */
      if (sh.user && *sh.user)
      {
            execattr_t *pf;
            if(pf=getexecuser(sh.user, KV_COMMAND, resolvedpath, GET_ONE))
            {
                  if (!pf->attr || pf->attr->length == 0)
                  {
                        int r = execve(path, argv, envp);
                        free_execattr(pf);
                        return r;
                  }
                  free_execattr(pf);
            }
            else
            {
                  errno = ENOENT;
                  return -1;
            }
      }
      --argv;
      argv[0] = argv[1];
      argv[1] = resolvedpath;
      return(execve("/usr/bin/pfexec", argv, envp));
#else
      return(execve(path, argv, envp));
#endif
}


static pid_t _spawnveg(const char *path, char* const argv[], char* const envp[], pid_t pgid)
{
      int waitsafe = job.waitsafe;
      pid_t pid;
      job_lock();
      while(1)
      {
            sh_stats(STAT_SPAWN);
            pid = spawnveg(path,argv,envp,pgid);
            if(pid>=0 || errno!=EAGAIN)
                  break;
            _sh_fork(pid, 0, (int*)0);
      }
      job.waitsafe = waitsafe;
      job_unlock();
      return(pid);
}
/*
 * used with command -x to run the command in multiple passes
 * spawn is non-zero when invoked via spawn
 * the exitval is set to the maximum for each execution
 */
static pid_t path_xargs(const char *path, char *argv[],char *const envp[], int spawn)
{
      register char *cp, **av, **xv;
      char **avlast= &argv[sh.xargmax], **saveargs=0;
      char *const *ev;
      long size, left;
      int nlast=1,n,exitval=0;
      pid_t pid;
      if(sh.xargmin < 0)
            return((pid_t)-1);
      size = sh.lim.arg_max-1024;
      for(ev=envp; cp= *ev; ev++)
            size -= strlen(cp)-1;
      for(av=argv; (cp= *av) && av< &argv[sh.xargmin]; av++)  
            size -= strlen(cp)-1;
      for(av=avlast; cp= *av; av++,nlast++)  
            size -= strlen(cp)-1;
      av =  &argv[sh.xargmin];
      if(!spawn)
            job_clear();
      sh.exitval = 0;
      while(av<avlast)
      {
            for(xv=av,left=size; left>0 && av<avlast;)
                  left -= strlen(*av++)+1;
            /* leave at least two for last */
            if(left<0 && (avlast-av)<2)
                  av--;
            if(xv==&argv[sh.xargmin])
            {
                  n = nlast*sizeof(char*);
                  saveargs = (char**)malloc(n);
                  memcpy((void*)saveargs, (void*)av, n);
                  memcpy((void*)av,(void*)avlast,n);
            }
            else
            {
                  for(n=sh.xargmin; xv < av; xv++)
                        argv[n++] = *xv;
                  for(xv=avlast; cp=  *xv; xv++)
                        argv[n++] = cp;
                  argv[n] = 0;
            }
            if(saveargs || av<avlast || (exitval && !spawn))
            {
                  if((pid=_spawnveg(path,argv,envp,0)) < 0)
                        return(-1);
                  job_post(pid,0);
                  job_wait(pid);
                  if(sh.exitval>exitval)
                        exitval = sh.exitval;
                  if(saveargs)
                  {
                        memcpy((void*)av,saveargs,n);
                        free((void*)saveargs);
                        saveargs = 0;
                  }
            }
            else if(spawn && !sh_isoption(SH_PFSH))
            {
                  sh.xargexit = exitval;
                  return(_spawnveg(path,argv,envp,spawn>>1));
            }
            else
                  return(path_pfexecve(path,argv,envp,spawn));
      }
      if(!spawn)
            exit(exitval);
      return((pid_t)-1);
}

/*
 * make sure PWD is set up correctly
 * Return the present working directory
 * Invokes getcwd() if flag==0 and if necessary
 * Sets the PWD variable to this value
 */
char *path_pwd(int flag)
{
      register char *cp;
      register char *dfault = (char*)e_dot;
      register int count = 0;
      Shell_t *shp = &sh;
      if(shp->pwd)
            return((char*)shp->pwd);
      while(1) 
      {
            /* try from lowest to highest */
            switch(count++)
            {
                  case 0:
                        cp = nv_getval(PWDNOD);
                        break;
                  case 1:
                        cp = nv_getval(HOME);
                        break;
                  case 2:
                        cp = "/";
                        break;
                  case 3:
                        cp = (char*)e_crondir;
                        if(flag) /* skip next case when non-zero flag */
                              ++count;
                        break;
                  case 4:
                  {
                        if(cp=getcwd(NIL(char*),0))
                        {  
                              nv_offattr(PWDNOD,NV_NOFREE);
                              nv_unset(PWDNOD);
                              PWDNOD->nvalue.cp = cp;
                              goto skip;
                        }
                        break;
                  }
                  case 5:
                        return(dfault);
            }
            if(cp && *cp=='/' && test_inode(cp,e_dot))
                  break;
      }
      if(count>1)
      {
            nv_offattr(PWDNOD,NV_NOFREE);
            nv_putval(PWDNOD,cp,NV_RDONLY);
      }
skip:
      nv_onattr(PWDNOD,NV_NOFREE|NV_EXPORT);
      shp->pwd = (char*)(PWDNOD->nvalue.cp);
      return(cp);
}

static void free_bltin(Namval_t *np,void *data)
{
      register Pathcomp_t *pp= (Pathcomp_t*)data;
      if(pp->flags&PATH_STD_DIR)
      {
            int offset=staktell();;
            if(strcmp(pp->name,"/bin")==0 || memcmp(pp->name,np->nvname,pp->len) || np->nvname[pp->len]!='/')
                  return;
            stakputs("/bin");
            stakputs(np->nvname+pp->len+1);
            stakputc(0);
            sh_addbuiltin(stakptr(offset),np->nvalue.bfp,NiL);
            stakseek(offset);
            return;
      }
      if((void*)np->nvenv==pp->bltin_lib)
            nv_delete(np,sh_bltin_tree(),NV_NOFREE);
}

/*
 * delete current Pathcomp_t structure
 */
void  path_delete(Pathcomp_t *first)
{
      register Pathcomp_t *pp=first, *old=0, *ppnext;
      while(pp)
      {
            ppnext = pp->next;
            if(--pp->refcount<=0)
            {
                  if(pp->lib)
                        free((void*)pp->lib);
                  if(pp->blib)
                        free((void*)pp->blib);
                  if(pp->bltin_lib || (pp->flags&PATH_STD_DIR))
                  {
                        nv_scan(sh_bltin_tree(),free_bltin,pp,0,0);
#if SHOPT_DYNAMIC
                        if(pp->bltin_lib)
                              dlclose(pp->bltin_lib);
#endif /* SHOPT_DYNAMIC */
                  }
                  free((void*)pp);
                  if(old)
                        old->next = ppnext;
            }
            else
                  old = pp;
            pp = ppnext; 
      }
}

/*
 * returns library variable from .paths
 * The value might be returned on the stack overwriting path
 */
static char *path_lib(Pathcomp_t *pp, char *path)
{
      register char *last = strrchr(path,'/');
      register int r;
      struct stat statb;
      if(last)
            *last = 0;
      else
            path = ".";
      r = stat(path,&statb);
      if(last)
            *last = '/';
      if(r>=0)
      {
            Pathcomp_t pcomp;
            char save[8];
            for( ;pp; pp=pp->next)
            {
                  if(pp->ino==statb.st_ino && pp->dev==statb.st_dev && pp->mtime==statb.st_mtime)
                        return(pp->lib);
            }
            pcomp.len = 0;
            if(last)
                  pcomp.len = last-path;
            memcpy((void*)save, (void*)stakptr(PATH_OFFSET+pcomp.len),sizeof(save));
            if(path_chkpaths((Pathcomp_t*)0,(Pathcomp_t*)0,&pcomp,PATH_OFFSET))
                  return(stakfreeze(1));
            memcpy((void*)stakptr(PATH_OFFSET+pcomp.len),(void*)save,sizeof(save));
      }
      return(0);
}

#if 0
void path_dump(register Pathcomp_t *pp)
{
      sfprintf(sfstderr,"dump\n");
      while(pp)
      {
            sfprintf(sfstderr,"pp=%x dev=%d ino=%d len=%d flags=%o name=%.*s\n",
                  pp,pp->dev,pp->ino,pp->len,pp->flags,pp->len,pp->name);
            pp = pp->next;
      }
}
#endif

/*
 * write the next path to search on the current stack
 * if last is given, all paths that come before <last> are skipped
 * the next pathcomp is returned.
 */
Pathcomp_t *path_nextcomp(register Pathcomp_t *pp, const char *name, Pathcomp_t *last)
{
      stakseek(PATH_OFFSET);
      if(*name=='/')
            pp = 0;
      else
      {
            for(;pp && pp!=last;pp=pp->next)
            {
                  if(pp->flags&PATH_SKIP)
                        continue;
                  if(!last || *pp->name!='/')
                        break;
            }
            if(!pp)           /* this should not happen */
                  pp = last;
      }
      if(pp && (pp->name[0]!='.' || pp->name[1]))
      {
            if(*pp->name!='/')
            {
                  stakputs(path_pwd(1));
                  if(*stakptr(staktell()-1)!='/')
                        stakputc('/');
            }
            stakwrite(pp->name,pp->len);
            if(pp->name[pp->len-1]!='/')
                  stakputc('/');
      }
      stakputs(name);
      stakputc(0);
      while(pp && pp!=last && (pp=pp->next))
      {
            if(!(pp->flags&PATH_SKIP))
                  return(pp);
      }
      return((Pathcomp_t*)0);
}

static Pathcomp_t* defpath_init(Shell_t *shp)
{
      Pathcomp_t *pp = (void*)path_addpath((Pathcomp_t*)0,(std_path),PATH_PATH);
      if(shp->defpathlist = (void*)pp)
            pp->shp = shp;
      return(pp);
}

static void path_init(Shell_t *shp)
{
      const char *val;
      Pathcomp_t *pp;
      if(!std_path && !(std_path=astconf("PATH",NIL(char*),NIL(char*))))
            std_path = e_defpath;
      if(val=sh_scoped(shp,(PATHNOD))->nvalue.cp)
      {
            pp = (void*)path_addpath((Pathcomp_t*)shp->pathlist,val,PATH_PATH);
            if(shp->pathlist = (void*)pp)
                  pp->shp = shp;
      }
      else
      {
            if(!(pp=(Pathcomp_t*)shp->defpathlist))
                  pp = defpath_init(shp);
            shp->pathlist = (void*)path_dup(pp);
      }
      if(val=sh_scoped(shp,(FPATHNOD))->nvalue.cp)
      {
            pp = (void*)path_addpath((Pathcomp_t*)shp->pathlist,val,PATH_FPATH);
            if(shp->pathlist = (void*)pp)
                  pp->shp = shp;
      }
}

/*
 * returns that pathlist to search
 */
Pathcomp_t *path_get(register const char *name)
{
      register Shell_t *shp = &sh;
      register Pathcomp_t *pp=0;
      if(*name && strchr(name,'/'))
            return(0);
      if(!sh_isstate(SH_DEFPATH))
      {
            if(!shp->pathlist)
                  path_init(shp);
            pp = (Pathcomp_t*)shp->pathlist;
      }
      if(!pp && (!(PATHNOD)->nvalue.cp) || sh_isstate(SH_DEFPATH))
      {
            if(!(pp=(Pathcomp_t*)shp->defpathlist))
                  pp = defpath_init(shp);
      }
      return(pp);
}

/*
 * open file corresponding to name using path give by <pp>
 */
static int  path_opentype(const char *name, register Pathcomp_t *pp, int fun)
{
      register int fd= -1;
      struct stat statb;
      Pathcomp_t *oldpp;
      Shell_t *shp;
      if(pp)
            shp = pp->shp;
      else
      {
            shp = sh_getinterp();
            if(!shp->pathlist)
                  path_init(shp);
      }
      if(!fun && strchr(name,'/'))
      {
            if(sh_isoption(SH_RESTRICTED))
                  errormsg(SH_DICT,ERROR_exit(1),e_restricted,name);
      }
      do
      {
            pp = path_nextcomp(oldpp=pp,name,0);
            while(oldpp && (oldpp->flags&PATH_SKIP))
                  oldpp = oldpp->next;
            if(fun && (!oldpp || !(oldpp->flags&PATH_FPATH)))
                  continue;
            if((fd = sh_open(path_relative(stakptr(PATH_OFFSET)),O_RDONLY,0)) >= 0)
            {
                  if(fstat(fd,&statb)<0 || S_ISDIR(statb.st_mode))
                  {
                        errno = EISDIR;
                        sh_close(fd);
                        fd = -1;
                  }
            }
      }
      while( fd<0 && pp);
      if(fd>=0 && (fd = sh_iomovefd(fd)) > 0)
      {
            fcntl(fd,F_SETFD,FD_CLOEXEC);
            shp->fdstatus[fd] |= IOCLEX;
      }
      return(fd);
}

/*
 * open file corresponding to name using path give by <pp>
 */
int   path_open(const char *name, register Pathcomp_t *pp)
{
      return(path_opentype(name,pp,0));
}

/*
 * given a pathname return the base name
 */

char  *path_basename(register const char *name)
{
      register const char *start = name;
      while (*name)
            if ((*name++ == '/') && *name)      /* don't trim trailing / */
                  start = name;
      return ((char*)start);
}

char *path_fullname(const char *name)
{
      int len=strlen(name)+1,dirlen=0;
      char *path,*pwd;
      if(*name!='/')
      {
            pwd = path_pwd(1);
            dirlen = strlen(pwd)+1;
      }
      path = (char*)malloc(len+dirlen);
      if(dirlen)
      {
            memcpy((void*)path,(void*)pwd,dirlen);
            path[dirlen-1] = '/';
      }
      memcpy((void*)&path[dirlen],(void*)name,len);
      pathcanon(path,0);
      return(path);
}

/*
 * load functions from file <fno>
 */
static void funload(Shell_t *shp,int fno, const char *name)
{
      char        *pname,*oldname=shp->st.filename, buff[IOBSIZE+1];
      Namval_t    *np;
      struct Ufunction *rp;
      int          savestates = sh_getstate(), oldload=shp->funload;
      pname = path_fullname(stakptr(PATH_OFFSET));
      if(shp->fpathdict && (rp = dtmatch(shp->fpathdict,(void*)pname)))
      {
            do
            {
                  if((np = dtsearch(shp->fun_tree,rp->np)) && is_afunction(np))
                  {
                        if(np->nvalue.rp)
                              np->nvalue.rp->fdict = 0;
                        nv_delete(np,shp->fun_tree,NV_NOFREE);
                  }
                  dtinsert(shp->fun_tree,rp->np);
                  rp->fdict = shp->fun_tree;
            }
            while((rp=dtnext(shp->fpathdict,rp)) && strcmp(pname,rp->fname)==0);
            return;
      }
      sh_onstate(SH_NOLOG);
      sh_onstate(SH_NOALIAS);
      shp->readscript = (char*)name;
      shp->st.filename = pname;
      shp->funload = 1;
      error_info.line = 0;
      sh_eval(sfnew(NIL(Sfio_t*),buff,IOBSIZE,fno,SF_READ),SH_FUNEVAL);
      shp->readscript = 0;
      free((void*)shp->st.filename);
      shp->funload = oldload;
      shp->st.filename = oldname;
      sh_setstate(savestates);
}

/*
 * do a path search and track alias if requested
 * if flag is 0, or if name not found, then try autoloading function
 * if flag==2 or 3, returns 1 if name found on FPATH
 * if flag==3 no tracked alias will be set
 * returns 1, if function was autoloaded.
 * If oldpp is not NULL, it will contain a pointer to the path component
 *    where it was found.
 */

int   path_search(register const char *name,Pathcomp_t **oldpp, int flag)
{
      register Namval_t *np;
      register int fno;
      Pathcomp_t *pp=0;
      Shell_t *shp = &sh;
      if(name && strchr(name,'/'))
      {
            stakseek(PATH_OFFSET);
            stakputs(name);
            if(canexecute(stakptr(PATH_OFFSET),0)<0)
            {
                  *stakptr(PATH_OFFSET) = 0;
                  return(0);
            }
            if(*name=='/')
                  return(1);
            stakseek(PATH_OFFSET);
            stakputs(path_pwd(1));
            stakputc('/');
            stakputs(name);
            stakputc(0);
            return(0);
      }
      if(sh_isstate(SH_DEFPATH))
      {
            if(!shp->defpathlist)
                  defpath_init(shp);
      }
      else if(!shp->pathlist)
            path_init(shp);
      if(flag)
      {
            pp = path_absolute(name,oldpp?*oldpp:NIL(Pathcomp_t*));
            if(oldpp)
                  *oldpp = pp;
            if(!pp && (np=nv_search(name,sh.fun_tree,HASH_NOSCOPE))&&np->nvalue.ip)
                  return(1);
            if(!pp)
                  *stakptr(PATH_OFFSET) = 0;
      }
      if(flag==0 || !pp || (pp->flags&PATH_FPATH))
      {
            if(!pp)
                  pp=sh_isstate(SH_DEFPATH)?shp->defpathlist:shp->pathlist;
            if(pp && strmatch(name,e_alphanum)  && (fno=path_opentype(name,pp,1))>=0)
            {
                  if(flag==2)
                  {
                        sh_close(fno);
                        return(1);
                  }
                  funload(shp,fno,name);
                  return(1);
            }
            *stakptr(PATH_OFFSET) = 0;
            return(0);
      }
      else if(pp && !sh_isstate(SH_DEFPATH) && *name!='/' && flag<3)
      {
            if(np=nv_search(name,shp->track_tree,NV_ADD))
                  path_alias(np,pp);
      }
      return(0);
}


/*
 * do a path search and find the full pathname of file name
 */

Pathcomp_t *path_absolute(register const char *name, Pathcomp_t *pp)
{
      register int      f,isfun;
      int         noexec=0;
      Pathcomp_t  *oldpp;
      Shell_t           *shp = &sh;
      Namval_t    *np;
      shp->path_err = ENOENT;
      if(!pp && !(pp=path_get("")))
            return(0);
      shp->path_err = 0;
      while(1)
      {
            sh_sigcheck();
            isfun = (pp->flags&PATH_FPATH);
            if(oldpp=pp)
                  pp = path_nextcomp(pp,name,0);
            if(!isfun && !sh_isoption(SH_RESTRICTED))
            {
                  if(*stakptr(PATH_OFFSET)=='/' && nv_search(stakptr(PATH_OFFSET),sh.bltin_tree,0))
                        return(oldpp);
#if SHOPT_DYNAMIC
                  if(oldpp->blib)
                  {
                        typedef int (*Fptr_t)(int, char*[], void*);
                        Fptr_t addr;
                        int n = staktell();
                        int libcmd;
                        char *cp;
                        stakputs("b_");
                        stakputs(name);
                        stakputc(0);
                        if(!oldpp->bltin_lib)
                        {
                              if(cp = strrchr(oldpp->blib,'/'))
                                    cp++;
                              else
                                    cp = oldpp->blib;
                              if((libcmd = !strcmp(cp,LIBCMD)) && (addr=(Fptr_t)dlllook((void*)0,stakptr(n))))
                              {
                                    if((np = sh_addbuiltin(stakptr(PATH_OFFSET),addr,NiL)) && nv_isattr(np,NV_BLTINOPT))
                                          return(oldpp);
                              }
#if (_AST_VERSION>=20040404)
                              if (oldpp->bltin_lib = dllplug(SH_ID, oldpp->blib, NiL, RTLD_LAZY, NiL, 0))
#else
                              if (oldpp->bltin_lib = dllfind(oldpp->blib, NiL, RTLD_LAZY, NiL, 0))
#endif
                              {
                                    /*
                                     * this detects the 2007-05-11 builtin context change and also
                                     * the 2008-03-30 opt_info.num change that hit libcmd::b_head
                                     */

                                    if (libcmd && !dlllook(oldpp->bltin_lib, "b_pids"))
                                    {
                                          dlclose(oldpp->bltin_lib);
                                          oldpp->bltin_lib = 0;
                                          oldpp->blib = 0;
                                    }
                                    else
                                          sh_addlib(oldpp->bltin_lib);
                              }
                        }
                        if((addr=(Fptr_t)dlllook(oldpp->bltin_lib,stakptr(n))) &&
                           (!(np = sh_addbuiltin(stakptr(PATH_OFFSET),NiL,NiL)) || np->nvalue.bfp!=addr) &&
                           (np = sh_addbuiltin(stakptr(PATH_OFFSET),addr,NiL)))
                        {
                              np->nvenv = oldpp->bltin_lib;
                              return(oldpp);
                        }
                  }
#endif /* SHOPT_DYNAMIC */
            }
            sh_stats(STAT_PATHS);
            f = canexecute(stakptr(PATH_OFFSET),isfun);
            if(isfun && f>=0)
            {
                  nv_onattr(nv_open(name,shp->fun_tree,NV_NOARRAY|NV_IDENT|NV_NOSCOPE),NV_LTOU|NV_FUNCTION);
                  close(f);
                  f = -1;
                  return(0);
            }
            else if(f>=0 && (oldpp->flags & PATH_STD_DIR))
            {
                  int n = staktell();
                  stakputs("/bin/");
                  stakputs(name);
                  stakputc(0);
                  np = nv_search(stakptr(n),sh.bltin_tree,0);
                  stakseek(n);
                  if(np)
                  {
                        n = np->nvflag;
                        np = sh_addbuiltin(stakptr(PATH_OFFSET),np->nvalue.bfp,nv_context(np));
                        np->nvflag = n;
                  }
            }
            if(!pp || f>=0)
                  break;
            if(errno!=ENOENT)
                  noexec = errno;
      }
      if(f<0)
      {
            shp->path_err = (noexec?noexec:ENOENT);
            return(0);
      }
      stakputc(0);
      return(oldpp);
}

/*
 * returns 0 if path can execute
 * sets exec_err if file is found but can't be executable
 */
#undef S_IXALL
#ifdef S_IXUSR
#   define S_IXALL      (S_IXUSR|S_IXGRP|S_IXOTH)
#else
#   ifdef S_IEXEC
#     define S_IXALL    (S_IEXEC|(S_IEXEC>>3)|(S_IEXEC>>6))
#   else
#     define S_IXALL    0111
#   endif /*S_EXEC */
#endif /* S_IXUSR */

static int canexecute(register char *path, int isfun)
{
      struct stat statb;
      register int fd=0;
      path = path_relative(path);
      if(isfun)
      {
            if((fd=open(path,O_RDONLY,0))<0 || fstat(fd,&statb)<0)
                  goto err;
      }
      else if(stat(path,&statb) < 0)
      {
#if _WINIX
            /* check for .exe or .bat suffix */
            char *cp;
            if(errno==ENOENT && (!(cp=strrchr(path,'.')) || strlen(cp)>4 || strchr(cp,'/')))
            {
                  int offset = staktell()-1;
                  stakseek(offset);
                  stakputs(".bat");
                  path = stakptr(PATH_OFFSET);
                  if(stat(path,&statb) < 0)
                  {
                        if(errno!=ENOENT)
                              goto err;
                        memcpy(stakptr(offset),".sh",4);
                        if(stat(path,&statb) < 0)
                              goto err;
                  }
            }
            else
#endif /* _WINIX */
            goto err;
      }
      errno = EPERM;
      if(S_ISDIR(statb.st_mode))
            errno = EISDIR;
      else if((statb.st_mode&S_IXALL)==S_IXALL || sh_access(path,X_OK)>=0)
            return(fd);
      if(isfun && fd>=0)
            sh_close(fd);
err:
      return(-1);
}

/*
 * Return path relative to present working directory
 */

char *path_relative(register const char* file)
{
      register const char *pwd;
      register const char *fp = file;
      /* can't relpath when sh.pwd not set */
      if(!(pwd=sh.pwd))
            return((char*)fp);
      while(*pwd==*fp)
      {
            if(*pwd++==0)
                  return((char*)e_dot);
            fp++;
      }
      if(*pwd==0 && *fp == '/')
      {
            while(*++fp=='/');
            if(*fp)
                  return((char*)fp);
            return((char*)e_dot);
      }
      return((char*)file);
}

void  path_exec(register const char *arg0,register char *argv[],struct argnod *local)
{
      char **envp;
      const char *opath;
      Pathcomp_t *libpath, *pp=0;
      Shell_t *shp = &sh;
      int slash=0;
      nv_setlist(local,NV_EXPORT|NV_IDENT|NV_ASSIGN);
      envp = sh_envgen();
      if(strchr(arg0,'/'))
      {
            slash=1;
            /* name containing / not allowed for restricted shell */
            if(sh_isoption(SH_RESTRICTED))
                  errormsg(SH_DICT,ERROR_exit(1),e_restricted,arg0);
      }
      else
            pp=path_get(arg0);
      shp->path_err= ENOENT;
      sfsync(NIL(Sfio_t*));
      timerdel(NIL(void*));
      /* find first path that has a library component */
      if(pp || slash) do
      {
            sh_sigcheck();
            if(libpath=pp)
            {
                  pp = path_nextcomp(pp,arg0,0);
                  opath = stakfreeze(1)+PATH_OFFSET;
            }
            else
                  opath = arg0;
            path_spawn(opath,argv,envp,libpath,0);
            while(pp && (pp->flags&PATH_FPATH))
                  pp = path_nextcomp(pp,arg0,0);
      }
      while(pp);
      /* force an exit */
      ((struct checkpt*)shp->jmplist)->mode = SH_JMPEXIT;
      if((errno=shp->path_err)==ENOENT)
            errormsg(SH_DICT,ERROR_exit(ERROR_NOENT),e_found,arg0);
      else
            errormsg(SH_DICT,ERROR_system(ERROR_NOEXEC),e_exec,arg0);
}

pid_t path_spawn(const char *opath,register char **argv, char **envp, Pathcomp_t *libpath, int spawn)
{
      Shell_t *shp = sh_getinterp();
      register char *path;
      char **xp=0, *xval, *libenv = (libpath?libpath->lib:0); 
      Namval_t*   np;
      char        *s, *v;
      int         r, n, pidsize;
      pid_t       pid= -1;
      /* leave room for inserting _= pathname in environment */
      envp--;
#if _lib_readlink
      /* save original pathname */
      stakseek(PATH_OFFSET);
      pidsize = sfprintf(stkstd,"*%d*",spawn?getpid():getppid());
      stakputs(opath);
      opath = stakfreeze(1)+PATH_OFFSET+pidsize;
      np=nv_search(argv[0],shp->track_tree,0);
      while(libpath && !libpath->lib)
            libpath=libpath->next;
      if(libpath && (!np || nv_size(np)>0))
      {
            /* check for symlink and use symlink name */
            char buff[PATH_MAX+1];
            char save[PATH_MAX+1];
            stakseek(PATH_OFFSET);
            stakputs(opath);
            path = stakptr(PATH_OFFSET);
            while((n=readlink(path,buff,PATH_MAX))>0)
            {
                  buff[n] = 0;
                  n = PATH_OFFSET;
                  r = 0;
                  if((v=strrchr(path,'/')) && *buff!='/')
                  {
                        if(buff[0]=='.' && buff[1]=='.' && (r = strlen(path) + 1) <= PATH_MAX)
                              memcpy(save, path, r);
                        else
                              r = 0;
                        n += (v+1-path);
                  }
                  stakseek(n);
                  stakputs(buff);
                  stakputc(0);
                  path = stakptr(PATH_OFFSET);
                  if(v && buff[0]=='.' && buff[1]=='.')
                  {
                        pathcanon(path, 0);
                        if(r && access(path,X_OK))
                        {
                              memcpy(path, save, r);
                              break;
                        }
                  }
                  if(libenv = path_lib(libpath,path))
                        break;
            }
            stakseek(0);
      }
#endif
      if(libenv && (v = strchr(libenv,'=')))
      {
            n = v - libenv;
            *v = 0;
            np = nv_open(libenv,shp->var_tree,0);
            *v = '=';
            s = nv_getval(np);
            stakputs(libenv);
            if(s)
            {
                  stakputc(':');
                  stakputs(s);
            }
            v = stakfreeze(1);
            r = 1;
            xp = envp + 1;
            while (s = *xp++)
            {
                  if (strneq(s, v, n) && s[n] == '=')
                  {
                        xval = *--xp;
                        *xp = v;
                        r = 0;
                        break;
                  }
            }
            if (r)
            {
                  *envp-- = v;
                  xp = 0;
            }
      }
      if(!opath)
            opath = stakptr(PATH_OFFSET);
      envp[0] =  (char*)opath-(PATH_OFFSET+pidsize);
      envp[0][0] =  '_';
      envp[0][1] =  '=';
      sfsync(sfstderr);
      sh_sigcheck();
      path = path_relative(opath);
#ifdef SHELLMAGIC
      if(*path!='/' && path!=opath)
      {
            /*
             * The following code because execv(foo,) and execv(./foo,)
             * may not yield the same results
             */
            char *sp = (char*)malloc(strlen(path)+3);
            sp[0] = '.';
            sp[1] = '/';
            strcpy(sp+2,path);
            path = sp;
      }
#endif /* SHELLMAGIC */
      if(spawn && !sh_isoption(SH_PFSH))
            pid = _spawnveg(opath, &argv[0],envp, spawn>>1);
      else
            pid = path_pfexecve(opath, &argv[0] ,envp,spawn);
      if(xp)
            *xp = xval;
#ifdef SHELLMAGIC
      if(*path=='.' && path!=opath)
      {
            free(path);
            path = path_relative(opath);
      }
#endif /* SHELLMAGIC */
      if(pid>0)
            return(pid);
retry:
      switch(sh.path_err = errno)
      {
#ifdef apollo
          /* 
           * On apollo's execve will fail with eacces when
           * file has execute but not read permissions. So,
           * for now we will pretend that EACCES and ENOEXEC
           * mean the same thing.
           */
          case EACCES:
#endif /* apollo */
          case ENOEXEC:
#if SHOPT_SUID_EXEC
          case EPERM:
            /* some systems return EPERM if setuid bit is on */
#endif
            errno = ENOEXEC;
            if(spawn)
            {
#ifdef _lib_fork
                  if(sh.subshell)
                        return(-1);
                  do
                  {
                        if((pid=fork())>0)
                              return(pid);
                  }
                  while(_sh_fork(pid,0,(int*)0) < 0);
                  ((struct checkpt*)shp->jmplist)->mode = SH_JMPEXIT;
#else
                  return(-1);
#endif
            }
            exscript(shp,path,argv,envp);
#ifndef apollo
          case EACCES:
          {
            struct stat statb;
            if(stat(path,&statb)>=0)
            {
                  if(S_ISDIR(statb.st_mode))
                        errno = EISDIR;
#ifdef S_ISSOCK
                  if(S_ISSOCK(statb.st_mode))
                        exscript(shp,path,argv,envp);
#endif
            }
          }
            /* FALL THROUGH */
#endif /* !apollo */
#ifdef ENAMETOOLONG
          case ENAMETOOLONG:
#endif /* ENAMETOOLONG */
#if !SHOPT_SUID_EXEC
          case EPERM:
#endif
            shp->path_err = errno;
            return(-1);
          case ENOTDIR:
          case ENOENT:
          case EINTR:
#ifdef EMLINK
          case EMLINK:
#endif /* EMLINK */
            return(-1);
          case E2BIG:
            if(sh.xargmin)
            {
                  pid = path_xargs(opath, &argv[0] ,envp,spawn);
                  if(pid<0)
                        goto retry;
                  return(pid);
            }
          default:
            errormsg(SH_DICT,ERROR_system(ERROR_NOEXEC),e_exec,path);
      }
      return 0;
}

/*
 * File is executable but not machine code.
 * Assume file is a Shell script and execute it.
 */

static void exscript(Shell_t *shp,register char *path,register char *argv[],char **envp)
{
      register Sfio_t *sp;
      path = path_relative(path);
      shp->comdiv=0;
      shp->bckpid = 0;
      shp->st.ioset=0;
      /* clean up any cooperating processes */
      if(shp->cpipe[0]>0)
            sh_pclose(shp->cpipe);
      if(shp->cpid && shp->outpipe)
            sh_close(*shp->outpipe);
      shp->cpid = 0;
      if(sp=fcfile())
            while(sfstack(sp,SF_POPSTACK));
      job_clear();
      if(shp->infd>0 && (shp->fdstatus[shp->infd]&IOCLEX))
            sh_close(shp->infd);
      sh_setstate(sh_state(SH_FORKED));
      sfsync(sfstderr);
#if SHOPT_SUID_EXEC && !SHOPT_PFSH
      /* check if file cannot open for read or script is setuid/setgid  */
      {
            static char name[] = "/tmp/euidXXXXXXXXXX";
            register int n;
            register uid_t euserid;
            char *savet=0;
            struct stat statb;
            if((n=sh_open(path,O_RDONLY,0)) >= 0)
            {
                  /* move <n> if n=0,1,2 */
                  n = sh_iomovefd(n);
                  if(fstat(n,&statb)>=0 && !(statb.st_mode&(S_ISUID|S_ISGID)))
                        goto openok;
                  sh_close(n);
            }
            if((euserid=geteuid()) != shp->userid)
            {
                  strncpy(name+9,fmtbase((long)getpid(),10,0),sizeof(name)-10);
                  /* create a suid open file with owner equal effective uid */
                  if((n=open(name,O_CREAT|O_TRUNC|O_WRONLY,S_ISUID|S_IXUSR)) < 0)
                        goto fail;
                  unlink(name);
                  /* make sure that file has right owner */
                  if(fstat(n,&statb)<0 || statb.st_uid != euserid)
                        goto fail;
                  if(n!=10)
                  {
                        sh_close(10);
                        fcntl(n, F_DUPFD, 10);
                        sh_close(n);
                        n=10;
                  }
            }
            savet = *--argv;
            *argv = path;
            path_pfexecve(e_suidexec,argv,envp,0);
      fail:
            /*
             *  The following code is just for compatibility
             */
            if((n=open(path,O_RDONLY,0)) < 0)
                  errormsg(SH_DICT,ERROR_system(ERROR_NOEXEC),e_exec,path);
            if(savet)
                  *argv++ = savet;
      openok:
            shp->infd = n;
      }
#else
      if((shp->infd = sh_open(path,O_RDONLY,0)) < 0)
            errormsg(SH_DICT,ERROR_system(ERROR_NOEXEC),e_exec,path);
#endif
      shp->infd = sh_iomovefd(shp->infd);
#if SHOPT_ACCT
      sh_accbegin(path) ;  /* reset accounting */
#endif      /* SHOPT_ACCT */
      shp->arglist = sh_argcreate(argv);
      shp->lastarg = strdup(path);
      /* save name of calling command */
      shp->readscript = error_info.id;
      /* close history file if name has changed */
      if(shp->hist_ptr && (path=nv_getval(HISTFILE)) && strcmp(path,shp->hist_ptr->histname))
      {
            hist_close(shp->hist_ptr);
            (HISTCUR)->nvalue.lp = 0;
      }
      sh_offstate(SH_FORKED);
      siglongjmp(*shp->jmplist,SH_JMPSCRIPT);
}

#if SHOPT_ACCT
#   include <sys/acct.h>
#   include "FEATURE/time"

    static struct acct sabuf;
    static struct tms buffer;
    static clock_t      before;
    static char *SHACCT; /* set to value of SHACCT environment variable */
    static shaccton;    /* non-zero causes accounting record to be written */
    static int compress(time_t);
    /*
     *      initialize accounting, i.e., see if SHACCT variable set
     */
    void sh_accinit(void)
    {
      SHACCT = getenv("SHACCT");
    }
    /*
    * suspend accounting until turned on by sh_accbegin()
    */
    void sh_accsusp(void)
    {
      shaccton=0;
#ifdef AEXPAND
      sabuf.ac_flag |= AEXPND;
#endif /* AEXPAND */
    }

    /*
     * begin an accounting record by recording start time
     */
    void sh_accbegin(const char *cmdname)
    {
      if(SHACCT)
      {
            sabuf.ac_btime = time(NIL(time_t *));
            before = times(&buffer);
            sabuf.ac_uid = getuid();
            sabuf.ac_gid = getgid();
            strncpy(sabuf.ac_comm, (char*)path_basename(cmdname),
                  sizeof(sabuf.ac_comm));
            shaccton = 1;
      }
    }
    /*
     * terminate an accounting record and append to accounting file
     */
    void    sh_accend(void)
    {
      int   fd;
      clock_t     after;

      if(shaccton)
      {
            after = times(&buffer);
            sabuf.ac_utime = compress(buffer.tms_utime + buffer.tms_cutime);
            sabuf.ac_stime = compress(buffer.tms_stime + buffer.tms_cstime);
            sabuf.ac_etime = compress( (time_t)(after-before));
            fd = open( SHACCT , O_WRONLY | O_APPEND | O_CREAT,RW_ALL);
            write(fd, (const char*)&sabuf, sizeof( sabuf ));
            close( fd);
      }
    }
 
    /*
     * Produce a pseudo-floating point representation
     * with 3 bits base-8 exponent, 13 bits fraction.
     */
    static int compress(register time_t t)
    {
      register int exp = 0, rund = 0;

      while (t >= 8192)
      {
            exp++;
            rund = t&04;
            t >>= 3;
      }
      if (rund)
      {
            t++;
            if (t >= 8192)
            {
                  t >>= 3;
                  exp++;
            }
      }
      return((exp<<13) + t);
    }
#endif      /* SHOPT_ACCT */



/*
 * add a pathcomponent to the path search list and eliminate duplicates
 * and non-existing absolute paths.
 */
static Pathcomp_t *path_addcomp(Pathcomp_t *first, Pathcomp_t *old,const char *name, int flag)
{
      register Pathcomp_t *pp, *oldpp;
      struct stat statb;
      int len, offset=staktell();
      if(!(flag&PATH_BFPATH))
      {
            register const char *cp = name;
            while(*cp && *cp!=':')
                  stakputc(*cp++);
            len = staktell()-offset;
            stakputc(0);
            stakseek(offset);
            name = (const char*)stakptr(offset);
      }
      else
            len = strlen(name);
      for(pp=first; pp; pp=pp->next)
      {
            if(memcmp(name,pp->name,len)==0 && (pp->name[len]==':' || pp->name[len]==0))
            {
                  pp->flags |= flag;
                  return(first);
            }
      }
      if(old && (old=path_dirfind(old,name,0)))
      {
            statb.st_ino = old->ino;
            statb.st_dev = old->dev;
            statb.st_mtime = old->mtime;
            if(old->ino==0 && old->dev==0)
                  flag |= PATH_SKIP;
      }
      else if(stat(name,&statb)<0 || !S_ISDIR(statb.st_mode))
      {
            if(*name=='/')
            {
                  if(strcmp(name,SH_CMDLIB_DIR))
                        return(first);
                  statb.st_dev = 1;
            }
            else
            {
                  flag |= PATH_SKIP;
                  statb.st_dev = 0;
            }
            statb.st_ino = 0;
            statb.st_mtime = 0;
      }
      if(*name=='/' && onstdpath(name))
            flag |= PATH_STD_DIR;
      for(pp=first, oldpp=0; pp; oldpp=pp, pp=pp->next)
      {
            if(pp->ino==statb.st_ino && pp->dev==statb.st_dev && pp->mtime==statb.st_mtime)
            {
                  /* if both absolute paths, eliminate second */
                  pp->flags |= flag;
                  if(*name=='/' && *pp->name=='/')
                        return(first);
                  /* keep the path but mark it as skip */
                  flag |= PATH_SKIP;
            }
      }
      pp = newof((Pathcomp_t*)0,Pathcomp_t,1,len+1);
      pp->refcount = 1;
      memcpy((char*)(pp+1),name,len+1);
      pp->name = (char*)(pp+1);
      pp->len = len;
      pp->dev = statb.st_dev;
      pp->ino = statb.st_ino;
      pp->mtime = statb.st_mtime;
      if(oldpp)
            oldpp->next = pp;
      else
            first = pp;
      pp->flags = flag;
      if(pp->ino==0 && pp->dev==1)
      {
            pp->flags |= PATH_BUILTIN_LIB;
            pp->blib = malloc(4);
            strcpy(pp->blib,LIBCMD);
            return(first);
      }
      if((flag&(PATH_PATH|PATH_SKIP))==PATH_PATH)
            path_chkpaths(first,old,pp,offset);
      return(first);
}

/*
 * This function checks for the .paths file in directory in <pp>
 * it assumes that the directory is on the stack at <offset> 
 */
static int path_chkpaths(Pathcomp_t *first, Pathcomp_t* old,Pathcomp_t *pp, int offset)
{
      struct stat statb;
      int k,m,n,fd;
      char *sp,*cp,*ep;
      stakseek(offset+pp->len);
      if(pp->len==1 && *stakptr(offset)=='/')
            stakseek(offset);
      stakputs("/.paths");
      if((fd=open(stakptr(offset),O_RDONLY))>=0)
      {
            fstat(fd,&statb);
            n = statb.st_size;
            stakseek(offset+pp->len+n+2);
            sp = stakptr(offset+pp->len);
            *sp++ = '/';
            n=read(fd,cp=sp,n);
            sp[n] = 0;
            close(fd);
            for(ep=0; n--; cp++)
            {
                  if(*cp=='=')
                  {
                        ep = cp+1;
                        continue;
                  }
                  else if(*cp!='\r' &&  *cp!='\n')
                        continue;
                  if(*sp=='#' || sp==cp)
                  {
                        sp = cp+1;
                        continue;
                  }
                  *cp = 0;
                  m = ep ? (ep-sp) : 0;
                  if(!m || m==6 && memcmp((void*)sp,(void*)"FPATH=",6)==0)
                  {
                        if(first)
                        {
                              char *ptr = stakptr(offset+pp->len+1);
                              if(ep)
                                    strcpy(ptr,ep);
                              path_addcomp(first,old,stakptr(offset),PATH_FPATH|PATH_BFPATH);
                        }
                  }
                  else if(m==12 && memcmp((void*)sp,(void*)"BUILTIN_LIB=",12)==0)
                  {
                        if(!(pp->flags & PATH_BUILTIN_LIB))
                        {
                              pp->flags |= PATH_BUILTIN_LIB;
                              if (*ep == '.' && !*(ep + 1))
                                    pp->flags |= PATH_STD_DIR;
                              else
                              {
                                    k = strlen(ep)+1;
                                    if (*ep != '/')
                                          k +=  pp->len+1;
                                    pp->blib = sp = malloc(k);
                                    if (*ep != '/')
                                    {
                                          strcpy(pp->blib,pp->name);
                                          sp += pp->len;
                                          *sp++ = '/';
                                    }
                                    strcpy(sp,ep);
                              }
                        }
                  }
                  else if(m)
                  {
                        pp->lib = (char*)malloc(cp-sp+pp->len+2);
                        memcpy((void*)pp->lib,(void*)sp,m);
                        memcpy((void*)&pp->lib[m],stakptr(offset),pp->len);
                        pp->lib[k=m+pp->len] = '/';
                        strcpy((void*)&pp->lib[k+1],ep);
                        pathcanon(&pp->lib[m],0);
                        if(!first)
                        {
                              stakseek(0);
                              stakputs(pp->lib);
                              free((void*)pp->lib);
                              return(1);
                        }
                  }
                  sp = cp+1;
                  ep = 0;
            }
      }
      return(0);
}


Pathcomp_t *path_addpath(Pathcomp_t *first, register const char *path,int type)
{
      register const char *cp;
      Pathcomp_t *old=0;
      int offset = staktell();
      char *savptr;
      
      if(!path && type!=PATH_PATH)
            return(first);
      if(type!=PATH_FPATH)
      {
            old = first;
            first = 0;
      }
      if(offset)
            savptr = stakfreeze(0);
      if(path) while(*(cp=path))
      {
            if(*cp==':')
            {
                  if(type!=PATH_FPATH)
                        first = path_addcomp(first,old,".",type);
                  while(*++path == ':');
            }
            else
            {
                  int c;
                  while(*path && *path!=':')
                        path++;
                  c = *path++;
                  first = path_addcomp(first,old,cp,type);
                  if(c==0)
                        break;
                  if(*path==0)
                        path--;
            }
      }
      if(old)
      {
            if(!first && !path)
            {
                  Pathcomp_t *pp = (Pathcomp_t*)old->shp->defpathlist;
                  if(!pp)
                        pp = defpath_init(old->shp);
                  first = path_dup(pp);
            }
            if(cp=(FPATHNOD)->nvalue.cp)
                  first = (void*)path_addpath((Pathcomp_t*)first,cp,PATH_FPATH);
            path_delete(old);
      }
      if(offset)
            stakset(savptr,offset);
      else
            stakseek(0);
      return(first);
}

/*
 * duplicate the path give by <first> by incremented reference counts
 */
Pathcomp_t *path_dup(Pathcomp_t *first)
{
      register Pathcomp_t *pp=first;
      while(pp)
      {
            pp->refcount++;
            pp = pp->next;
      }
      return(first);
}

/*
 * called whenever the directory is changed
 */
void path_newdir(Pathcomp_t *first)
{
      register Pathcomp_t *pp=first, *next, *pq;
      struct stat statb;
      for(pp=first; pp; pp=pp->next)
      {
            pp->flags &= ~PATH_SKIP;
            if(*pp->name=='/')
                  continue;
            /* delete .paths component */
            if((next=pp->next) && (next->flags&PATH_BFPATH))
            {
                  pp->next = next->next;
                  if(--next->refcount<=0)
                        free((void*)next);
            }
            if(stat(pp->name,&statb)<0 || !S_ISDIR(statb.st_mode))
            {
                  pp->dev = 0;
                  pp->ino = 0;
                  continue;
            }
            pp->dev = statb.st_dev;
            pp->ino = statb.st_ino;
            pp->mtime = statb.st_mtime;
            for(pq=first;pq!=pp;pq=pq->next)
            {
                  if(pp->ino==pq->ino && pp->dev==pq->dev)
                        pp->flags |= PATH_SKIP;
            }
            for(pq=pp;pq=pq->next;)
            {
                  if(pp->ino==pq->ino && pp->dev==pq->dev)
                        pq->flags |= PATH_SKIP;
            }
            if((pp->flags&(PATH_PATH|PATH_SKIP))==PATH_PATH)
            {
                  /* try to insert .paths component */
                  int offset = staktell();
                  stakputs(pp->name);
                  stakseek(offset);
                  next = pp->next;
                  pp->next = 0;
                  path_chkpaths(first,(Pathcomp_t*)0,pp,offset);
                  if(pp->next)
                        pp = pp->next;
                  pp->next = next;
            }
      }
#if 0
      path_dump(first);
#endif
}

Pathcomp_t *path_unsetfpath(Pathcomp_t *first)
{
      register Pathcomp_t *pp=first, *old=0;
      Shell_t     *shp = &sh;
      if(shp->fpathdict)
      {
            struct Ufunction  *rp, *rpnext;
            for(rp=(struct Ufunction*)dtfirst(shp->fpathdict);rp;rp=rpnext)
            {
                  rpnext = (struct Ufunction*)dtnext(shp->fpathdict,rp);
                  if(rp->fdict)
                        nv_delete(rp->np,rp->fdict,NV_NOFREE);
                  rp->fdict = 0;
            }
      }
      while(pp)
      {
            if((pp->flags&PATH_FPATH) && !(pp->flags&PATH_BFPATH))
            {
                  if(pp->flags&PATH_PATH)
                        pp->flags &= ~PATH_FPATH;
                  else
                  {
                        Pathcomp_t *ppsave=pp;
                        if(old)
                              old->next = pp->next;
                        else
                              first = pp->next;
                        pp = pp->next;
                        if(--ppsave->refcount<=0)
                        {
                              if(ppsave->lib)
                                    free((void*)ppsave->lib);
                              free((void*)ppsave);
                        }
                        continue;
                  }
                  
            }
            old = pp;
            pp = pp->next;
      }
      return(first);
}

Pathcomp_t *path_dirfind(Pathcomp_t *first,const char *name,int c)
{
      register Pathcomp_t *pp=first;
      while(pp)
      {
            if(memcmp(name,pp->name,pp->len)==0 && name[pp->len]==c) 
                  return(pp);
            pp = pp->next;
      }
      return(0);
}

/*
 * get discipline for tracked alias
 */
static char *talias_get(Namval_t *np, Namfun_t *nvp)
{
      Pathcomp_t *pp = (Pathcomp_t*)np->nvalue.cp;
      char *ptr;
      if(!pp)
            return(NULL);
      path_nextcomp(pp,nv_name(np),pp);
      ptr = stakfreeze(0);
      return(ptr+PATH_OFFSET);
}

static void talias_put(register Namval_t* np,const char *val,int flags,Namfun_t *fp)
{
      if(!val && np->nvalue.cp)
      {
            Pathcomp_t *pp = (Pathcomp_t*)np->nvalue.cp;
            if(--pp->refcount<=0)
                  free((void*)pp);
      }
      nv_putv(np,val,flags,fp);
}

static const Namdisc_t talias_disc   = { 0, talias_put, talias_get   };
static Namfun_t  talias_init = { &talias_disc, 1 };

/*
 *  set tracked alias node <np> to value <pp>
 */
void path_alias(register Namval_t *np,register Pathcomp_t *pp)
{
      if(pp)
      {
            struct stat statb;
            char *sp;
            nv_offattr(np,NV_NOPRINT);
            nv_stack(np,&talias_init);
            np->nvalue.cp = (char*)pp;
            pp->refcount++;
            nv_setattr(np,NV_TAGGED|NV_NOFREE);
            path_nextcomp(pp,nv_name(np),pp);
            sp = stakptr(PATH_OFFSET);
            if(sp && lstat(sp,&statb)>=0 && S_ISLNK(statb.st_mode))
                  nv_setsize(np,statb.st_size+1);
            else
                  nv_setsize(np,0);
      }
      else
            nv_unset(np);
}


Generated by  Doxygen 1.6.0   Back to index