Skip to content

Commit a329c22

Browse files
committed
Multiple 'whence' and path search fixes
Hopefully this doesn't introduce new bugs, but it does fix at least the following: 1. When whence -v/-a found an "undefined" (i.e. autoloadable) function in $FPATH, it actually loaded the function as a side effect of reporting on its existence (!). Now it only reports. 2. 'whence' will now canonicalise paths properly. Examples: $ whence ///usr/lib/../bin//./env /usr/bin/env $ (cd /; whence -v dev/../usr/bin//./env) dev/../usr/bin//./env is /usr/bin/env 3. 'whence' no longer prefixes a spurious double slash when doing something like 'cd / && whence bin/echo'. On Cygwin, an initial double slash denotes a network server, so this was not just a cosmetic problem. 4. 'whence -a' now reports a "tracked alias" (a.k.a. hash table entry, i.e. cached $PATH search) even if an actual alias by the same name exists. This needed fixing because in fact the hash table entry continues to be used when bypassing the alias. Aliases and "tracked aliases" are not remotely the same thing; confusing nomenclature is not a reason to report wrong results. 5. When using 'hash' or 'alias -t' on a command that is also a builtin to force caching a $PATH search for the external command, 'whence -a' double-reported the path: $ hash printf; whence -a printf printf is a shell builtin printf is /usr/bin/printf printf is a tracked alias for /usr/bin/printf This is now fixed so that the second output line is gone. Plus, if there were multiple versions of the command on $PATH, the tracked alias was reported at the end, which is the wrong order. This is also fixed. src/cmd/ksh93/bltins/whence.c: whence(): - Refactor the do...while loop that handles whence -v/-a for path searches in such a way that the code actually makes sense and stops looking like higher esotericism. Just doing this fixed ksh-community#2, ksh-community#4 and ksh-community#5 above (the latter two before I even noticed them). For instance, the path_fullname() call to canonicalise paths was already there; it was just never used. - Remove broken 'notrack' flaggery for deciding whether to report a hash table entry a.k.a. "tracked alias"; instead, check the hash table (shp->track_tree). src/cmd/ksh93/sh/path.c: - path_search(): Re ksh-community#3: When prefixing the PWD, first check if we're in '/' and if so, don't prefix it; otherwise, adding the next slash causes an initial double slash. (Since '/' is the only valid single-character absolute path, all we need to do is check if the second character pwd[1] is non-null.) - path_search(): Re ksh-community#1: Stop autoloading when called by 'whence': * The 'flag==2' check to avoid autoloading a function was broken. The flag value is 2 on the first whence() loop iteration, but 3 on subsequent ones. Change to 'flag >= 2'. * However, this only fixes it if the function file does not have the x permission bit, as executable files are handled by path_absolute() which unconditionally autoloads functions! So, pass on our flag parameter when callling path_absolute(). - path_absolute(): Re ksh-community#1: Add flag parameter. Do not autoload functions if flag >= 2. src/cmd/ksh93/include/path.h, src/cmd/ksh93/bltins/typeset.c, src/cmd/ksh93/sh/main.c, src/cmd/ksh93/sh/xec.c: - Re ksh-community#1: Update path_absolute() calls, adding a 0 flag parameter. src/cmd/ksh93/include/name.h: - Remove now-unused pathcomp member from union Value. It was introduced in 9906535 to allow examining the value of a tracked alias. This commit uses nv_getval() instead. src/cmd/ksh93/tests/builtins.sh, src/cmd/ksh93/tests/path.sh: - Add and tweak various related tests. Fixes: ksh93/ksh#84
1 parent 95fc175 commit a329c22

File tree

11 files changed

+192
-106
lines changed

11 files changed

+192
-106
lines changed

NEWS

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,16 @@ For full details, see the git log at: https://github.com/ksh93/ksh
33

44
Any uppercase BUG_* names are modernish shell bug IDs.
55

6+
2020-09-20:
7+
8+
- Bugfix: when whence -v/-a found an "undefined" (i.e. autoloadable) function
9+
in $FPATH, it actually loaded the function as a side effect of reporting on
10+
its existence. Now it only reports, as documented.
11+
12+
- 'whence' will now canonicalise paths properly, resolving '.' and '..'
13+
elements in paths given to it. It also no longer prefixes a spurious
14+
double slash when doing something like 'cd / && whence bin/echo'.
15+
616
2020-09-18:
717

818
- Setting the 'posix' option now turns off the 'braceexpand' option, as brace

src/cmd/ksh93/bltins/typeset.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -641,7 +641,7 @@ static int setall(char **argv,register int flag,Dt_t *troot,struct tdata *tp
641641
if(troot==shp->track_tree && tp->aflag=='-')
642642
{
643643
np = nv_search(name,troot,NV_ADD);
644-
path_alias(np,path_absolute(shp,nv_name(np),NIL(Pathcomp_t*)));
644+
path_alias(np,path_absolute(shp,nv_name(np),NIL(Pathcomp_t*),0));
645645
continue;
646646
}
647647
np = nv_open(name,troot,nvflags|((nvflags&NV_ASSIGN)?0:NV_ARRAY)|((iarray|(nvflags&(NV_REF|NV_NOADD)==NV_REF))?NV_FARRAY:0));
@@ -990,7 +990,7 @@ int b_builtin(int argc,char *argv[],Shbltin_t *context)
990990
tdata.sh = context->shp;
991991
stkp = tdata.sh->stk;
992992
if(!tdata.sh->pathlist)
993-
path_absolute(tdata.sh,argv[0],NIL(Pathcomp_t*));
993+
path_absolute(tdata.sh,argv[0],NIL(Pathcomp_t*),0);
994994
while (n = optget(argv,sh_optbuiltin)) switch (n)
995995
{
996996
case 's':

src/cmd/ksh93/bltins/whence.c

Lines changed: 44 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -137,16 +137,13 @@ static int whence(Shell_t *shp,char **argv, register int flags)
137137
register const char *cp;
138138
register int aflag,r=0;
139139
register const char *msg;
140-
int tofree;
141140
Namval_t *nq;
142141
char *notused;
143142
Pathcomp_t *pp=0;
144-
int notrack = 1;
145143
if(flags&Q_FLAG)
146144
flags &= ~A_FLAG;
147145
while(name= *argv++)
148146
{
149-
tofree=0;
150147
aflag = ((flags&A_FLAG)!=0);
151148
cp = 0;
152149
np = 0;
@@ -164,7 +161,7 @@ static int whence(Shell_t *shp,char **argv, register int flags)
164161
}
165162
/* non-tracked aliases */
166163
if((np=nv_search(name,shp->alias_tree,0))
167-
&& !nv_isnull(np) && !(notrack=nv_isattr(np,NV_TAGGED))
164+
&& !nv_isnull(np) && !nv_isattr(np,NV_TAGGED)
168165
&& (cp=nv_getval(np)))
169166
{
170167
if(flags&V_FLAG)
@@ -213,81 +210,84 @@ static int whence(Shell_t *shp,char **argv, register int flags)
213210
aflag++;
214211
}
215212
search:
216-
if(sh_isstate(SH_DEFPATH))
217-
{
218-
cp=0;
219-
notrack=1;
220-
}
221213
do
222214
{
223-
if(path_search(shp,name,&pp,2+(aflag>1)))
215+
int maybe_undef_fn = 0; /* flag for possible undefined (i.e. autoloadable) function */
216+
/*
217+
* See comments in sh/path.c for info on what path_search()'s true/false return values mean
218+
*/
219+
if(path_search(shp, name, &pp, aflag>1 ? 3 : 2))
224220
{
225221
cp = name;
226-
if((flags&P_FLAG) && *cp!='/')
227-
cp = 0;
222+
if(*cp!='/')
223+
{
224+
if(flags&P_FLAG)
225+
cp = 0;
226+
else
227+
maybe_undef_fn = 1;
228+
}
228229
}
229230
else
230231
{
231232
cp = stakptr(PATH_OFFSET);
232233
if(*cp==0)
233234
cp = 0;
234-
else if(*cp!='/')
235-
{
236-
cp = path_fullname(shp,cp);
237-
tofree=1;
238-
}
239235
}
240236
if(flags&Q_FLAG)
241237
{
242238
pp = 0;
243239
r |= !cp;
244240
}
241+
else if(maybe_undef_fn)
242+
{
243+
/* Skip defined function or builtin (already done above) */
244+
if(!nv_search(cp,shp->fun_tree,0))
245+
{
246+
/* Undefined/autoloadable function on FPATH */
247+
sfputr(sfstdout,sh_fmtq(cp),-1);
248+
if(flags&V_FLAG)
249+
sfputr(sfstdout,sh_translate(is_ufunction),-1);
250+
sfputc(sfstdout,'\n');
251+
}
252+
}
245253
else if(cp)
246254
{
255+
cp = path_fullname(shp,cp); /* resolve '.' & '..' */
247256
if(flags&V_FLAG)
248257
{
249-
if(*cp!= '/')
250-
{
251-
if(!np && (np=nv_search(name,shp->track_tree,0)))
252-
{
253-
const char *command_path = np->nvalue.pathcomp->name;
254-
sfprintf(sfstdout,"%s %s %s/%s\n",name,sh_translate(is_talias),command_path,cp);
255-
}
256-
continue;
257-
}
258258
sfputr(sfstdout,sh_fmtq(name),' ');
259259
/* built-in version of program */
260-
if(*cp=='/' && (np=nv_search(cp,shp->bltin_tree,0)))
260+
if(nv_search(cp,shp->bltin_tree,0))
261261
msg = sh_translate(is_builtver);
262262
/* tracked aliases next */
263-
else if(aflag>1 || !notrack || strchr(name,'/'))
264-
msg = sh_translate("is");
265-
else
263+
else if(!sh_isstate(SH_DEFPATH)
264+
&& (np = nv_search(name,shp->track_tree,0))
265+
&& !nv_isattr(np,NV_NOALIAS)
266+
&& strcmp(cp,nv_getval(np))==0)
266267
msg = sh_translate(is_talias);
268+
else
269+
msg = sh_translate("is");
267270
sfputr(sfstdout,msg,' ');
268271
}
269272
sfputr(sfstdout,sh_fmtq(cp),'\n');
270-
if(aflag)
271-
{
272-
if(aflag<=1)
273-
aflag++;
274-
if (pp)
275-
pp = pp->next;
276-
}
277-
else
278-
pp = 0;
279-
if(tofree)
280-
{
281-
free((char*)cp);
282-
tofree = 0;
283-
}
273+
free((char*)cp);
284274
}
285275
else if(aflag<=1)
286276
{
287277
r |= 1;
288278
if(flags&V_FLAG)
289279
errormsg(SH_DICT,ERROR_exit(0),e_found,sh_fmtq(name));
290280
}
281+
/* If -a given, continue with next result */
282+
if(aflag)
283+
{
284+
if(aflag<=1)
285+
aflag++;
286+
if(pp)
287+
pp = pp->next;
288+
}
289+
else
290+
pp = 0;
291291
} while(pp);
292292
}
293293
return(r);

src/cmd/ksh93/include/name.h

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,6 @@
3232
#include <cdt.h>
3333

3434
typedef int (*Nambfp_f)(int, char**, void*);
35-
struct pathcomp;
3635

3736
/* Nodes can have all kinds of values */
3837
union Value
@@ -55,7 +54,6 @@ union Value
5554
struct Namfun *funp; /* discipline pointer */
5655
struct Namref *nrp; /* name reference */
5756
Nambfp_f bfp; /* builtin entry point function pointer */
58-
struct pathcomp *pathcomp;
5957
};
6058

6159
#include "nval.h"

src/cmd/ksh93/include/path.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ extern Pathcomp_t *path_addpath(Shell_t*,Pathcomp_t*,const char*,int);
7878
extern Pathcomp_t *path_dup(Pathcomp_t*);
7979
extern void path_delete(Pathcomp_t*);
8080
extern void path_alias(Namval_t*,Pathcomp_t*);
81-
extern Pathcomp_t *path_absolute(Shell_t*, const char*, Pathcomp_t*);
81+
extern Pathcomp_t *path_absolute(Shell_t*, const char*, Pathcomp_t*, int);
8282
extern char *path_basename(const char*);
8383
extern char *path_fullname(Shell_t*,const char*);
8484
extern int path_expand(Shell_t*,const char*, struct argnod**);

src/cmd/ksh93/include/version.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,4 +17,4 @@
1717
* David Korn <[email protected]> *
1818
* *
1919
***********************************************************************/
20-
#define SH_RELEASE "93u+m 2020-09-18"
20+
#define SH_RELEASE "93u+m 2020-09-20"

src/cmd/ksh93/sh/main.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -300,7 +300,7 @@ int sh_main(int ac, char *av[], Shinit_f userinit)
300300
if(fdin < 0 && !strchr(name,'/'))
301301
{
302302
#ifdef PATH_BFPATH
303-
if(path_absolute(shp,name,NIL(Pathcomp_t*)))
303+
if(path_absolute(shp,name,NIL(Pathcomp_t*),0))
304304
sp = stakptr(PATH_OFFSET);
305305
#else
306306
sp = path_absolute(shp,name,NIL(char*));

src/cmd/ksh93/sh/path.c

Lines changed: 33 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -643,12 +643,26 @@ static void funload(Shell_t *shp,int fno, const char *name)
643643

644644
/*
645645
* do a path search and track alias if requested
646-
* if flag is 0, or if name not found, then try autoloading function
647-
* if flag==2 or 3, returns 1 if name found on FPATH
648-
* if flag==3 no tracked alias will be set
649-
* returns 1, if function was autoloaded.
646+
*
647+
* If flag is 0, or if name not found, then try autoloading function and return 1 if successful.
648+
* If flag is >=1, do a regular path search. If it yields an autoloadable function, load it.
649+
* If flag is 2 or 3, never autoload a function but return 1 if name found on FPATH.
650+
* If flag is 3, no tracked alias will be set (IOW, the result won't be cached in the hash table).
650651
* If oldpp is not NULL, it will contain a pointer to the path component
651652
* where it was found.
653+
*
654+
* path_search() returns 1/true if:
655+
* - the given absolute path was found executable
656+
* - the given name is a function or non-path-bound builtin, and a path search found nothing external
657+
* - the given name matched an autoloadable function on FPATH
658+
*
659+
* path_search() returns 0/false if:
660+
* - the given relative path was found executable; the PWD is prefixed to make it absolute
661+
* - a tracked alias (a.k.a. hash table entry) was found and used
662+
* - the given name was found on PATH as an executable command or path-bound builtin (irrespective of
663+
* whether it exists as a function or normal builtin); its full path is written on the stack, except
664+
* if the matching $PATH entry is '.' or empty, the simple name is written without prefixing the PWD
665+
* - nothing executable was found
652666
*/
653667

654668
int path_search(Shell_t *shp,register const char *name,Pathcomp_t **oldpp, int flag)
@@ -658,6 +672,7 @@ int path_search(Shell_t *shp,register const char *name,Pathcomp_t **oldpp, int f
658672
Pathcomp_t *pp=0;
659673
if(name && strchr(name,'/'))
660674
{
675+
char *pwd;
661676
stakseek(PATH_OFFSET);
662677
stakputs(name);
663678
if(canexecute(shp,stakptr(PATH_OFFSET),0)<0)
@@ -668,7 +683,9 @@ int path_search(Shell_t *shp,register const char *name,Pathcomp_t **oldpp, int f
668683
if(*name=='/')
669684
return(1);
670685
stakseek(PATH_OFFSET);
671-
stakputs(path_pwd(shp,1));
686+
pwd = path_pwd(shp,1);
687+
if(pwd[1]) /* if pwd=="/", avoid starting with "//" */
688+
stakputs(pwd);
672689
stakputc('/');
673690
stakputs(name);
674691
stakputc(0);
@@ -697,7 +714,7 @@ int path_search(Shell_t *shp,register const char *name,Pathcomp_t **oldpp, int f
697714
stakputc(0);
698715
return(0);
699716
}
700-
pp = path_absolute(shp,name,oldpp?*oldpp:NIL(Pathcomp_t*));
717+
pp = path_absolute(shp,name,oldpp?*oldpp:NIL(Pathcomp_t*),flag);
701718
if(oldpp)
702719
*oldpp = pp;
703720
if(!pp && (np=nv_search(name,shp->fun_tree,0))&&np->nvalue.ip)
@@ -711,7 +728,7 @@ int path_search(Shell_t *shp,register const char *name,Pathcomp_t **oldpp, int f
711728
pp=sh_isstate(SH_DEFPATH)?shp->defpathlist:shp->pathlist;
712729
if(pp && strmatch(name,e_alphanum) && (fno=path_opentype(shp,name,pp,1))>=0)
713730
{
714-
if(flag==2)
731+
if(flag >= 2)
715732
{
716733
sh_close(fno);
717734
return(1);
@@ -732,8 +749,10 @@ int path_search(Shell_t *shp,register const char *name,Pathcomp_t **oldpp, int f
732749

733750
/*
734751
* do a path search and find the full pathname of file name
752+
*
753+
* If flag >= 2, do not autoload functions (cf. path_search()).
735754
*/
736-
Pathcomp_t *path_absolute(Shell_t *shp,register const char *name, Pathcomp_t *pp)
755+
Pathcomp_t *path_absolute(Shell_t *shp,register const char *name, Pathcomp_t *pp, int flag)
737756
{
738757
register int f,isfun;
739758
int noexec=0;
@@ -874,10 +893,12 @@ Pathcomp_t *path_absolute(Shell_t *shp,register const char *name, Pathcomp_t *pp
874893
}
875894
if(isfun && f>=0)
876895
{
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;
896+
if(flag < 2)
897+
{
898+
nv_onattr(nv_open(name,sh_subfuntree(1),NV_NOARRAY|NV_IDENT|NV_NOSCOPE),NV_LTOU|NV_FUNCTION);
899+
funload(shp,f,name);
900+
close(f);
901+
}
881902
return(0);
882903
}
883904
else if(f>=0 && (oldpp->flags & PATH_STD_DIR))

src/cmd/ksh93/sh/xec.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3619,7 +3619,7 @@ static pid_t sh_ntfork(Shell_t *shp,const Shnode_t *t,char *argv[],int *jobid,in
36193619
&& !nv_isattr(np,NV_NOALIAS)
36203620
&& np->nvalue.cp)
36213621
path = nv_getval(np);
3622-
else if(path_absolute(shp,path,NIL(Pathcomp_t*)))
3622+
else if(path_absolute(shp,path,NIL(Pathcomp_t*),0))
36233623
{
36243624
path = stkptr(shp->stk,PATH_OFFSET);
36253625
stkfreeze(shp->stk,0);

0 commit comments

Comments
 (0)