diff --git a/NEWS b/NEWS index afbbe6a83..106e5cf71 100644 --- a/NEWS +++ b/NEWS @@ -3,6 +3,16 @@ For full details, see the git log at: https://github.com/ksh93/ksh Any uppercase BUG_* names are modernish shell bug IDs. +2020-09-20: + +- Bugfix: 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, as documented. + +- 'whence' will now canonicalise paths properly, resolving '.' and '..' + elements in paths given to it. It also no longer prefixes a spurious + double slash when doing something like 'cd / && whence bin/echo'. + 2020-09-18: - Setting the 'posix' option now turns off the 'braceexpand' option, as brace diff --git a/src/cmd/ksh93/bltins/typeset.c b/src/cmd/ksh93/bltins/typeset.c index 4e7e8ea2b..dff817b20 100644 --- a/src/cmd/ksh93/bltins/typeset.c +++ b/src/cmd/ksh93/bltins/typeset.c @@ -641,7 +641,7 @@ static int setall(char **argv,register int flag,Dt_t *troot,struct tdata *tp if(troot==shp->track_tree && tp->aflag=='-') { np = nv_search(name,troot,NV_ADD); - path_alias(np,path_absolute(shp,nv_name(np),NIL(Pathcomp_t*))); + path_alias(np,path_absolute(shp,nv_name(np),NIL(Pathcomp_t*),0)); continue; } 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) tdata.sh = context->shp; stkp = tdata.sh->stk; if(!tdata.sh->pathlist) - path_absolute(tdata.sh,argv[0],NIL(Pathcomp_t*)); + path_absolute(tdata.sh,argv[0],NIL(Pathcomp_t*),0); while (n = optget(argv,sh_optbuiltin)) switch (n) { case 's': diff --git a/src/cmd/ksh93/bltins/whence.c b/src/cmd/ksh93/bltins/whence.c index fb58133e6..fb862ac69 100644 --- a/src/cmd/ksh93/bltins/whence.c +++ b/src/cmd/ksh93/bltins/whence.c @@ -137,16 +137,13 @@ static int whence(Shell_t *shp,char **argv, register int flags) register const char *cp; register int aflag,r=0; register const char *msg; - int tofree; Namval_t *nq; char *notused; Pathcomp_t *pp=0; - int notrack = 1; if(flags&Q_FLAG) flags &= ~A_FLAG; while(name= *argv++) { - tofree=0; aflag = ((flags&A_FLAG)!=0); cp = 0; np = 0; @@ -164,7 +161,7 @@ static int whence(Shell_t *shp,char **argv, register int flags) } /* non-tracked aliases */ if((np=nv_search(name,shp->alias_tree,0)) - && !nv_isnull(np) && !(notrack=nv_isattr(np,NV_TAGGED)) + && !nv_isnull(np) && !nv_isattr(np,NV_TAGGED) && (cp=nv_getval(np))) { if(flags&V_FLAG) @@ -213,74 +210,67 @@ static int whence(Shell_t *shp,char **argv, register int flags) aflag++; } search: - if(sh_isstate(SH_DEFPATH)) - { - cp=0; - notrack=1; - } do { - if(path_search(shp,name,&pp,2+(aflag>1))) + int maybe_undef_fn = 0; /* flag for possible undefined (i.e. autoloadable) function */ + /* + * See comments in sh/path.c for info on what path_search()'s true/false return values mean + */ + if(path_search(shp, name, &pp, aflag>1 ? 3 : 2)) { cp = name; - if((flags&P_FLAG) && *cp!='/') - cp = 0; + if(*cp!='/') + { + if(flags&P_FLAG) + cp = 0; + else + maybe_undef_fn = 1; + } } else { cp = stakptr(PATH_OFFSET); if(*cp==0) cp = 0; - else if(*cp!='/') - { - cp = path_fullname(shp,cp); - tofree=1; - } } if(flags&Q_FLAG) { pp = 0; r |= !cp; } + else if(maybe_undef_fn) + { + /* Skip defined function or builtin (already done above) */ + if(!nv_search(cp,shp->fun_tree,0)) + { + /* Undefined/autoloadable function on FPATH */ + sfputr(sfstdout,sh_fmtq(cp),-1); + if(flags&V_FLAG) + sfputr(sfstdout,sh_translate(is_ufunction),-1); + sfputc(sfstdout,'\n'); + } + } else if(cp) { + cp = path_fullname(shp,cp); /* resolve '.' & '..' */ if(flags&V_FLAG) { - if(*cp!= '/') - { - if(!np && (np=nv_search(name,shp->track_tree,0))) - { - const char *command_path = np->nvalue.pathcomp->name; - sfprintf(sfstdout,"%s %s %s/%s\n",name,sh_translate(is_talias),command_path,cp); - } - continue; - } sfputr(sfstdout,sh_fmtq(name),' '); /* built-in version of program */ - if(*cp=='/' && (np=nv_search(cp,shp->bltin_tree,0))) + if(nv_search(cp,shp->bltin_tree,0)) msg = sh_translate(is_builtver); /* tracked aliases next */ - else if(aflag>1 || !notrack || strchr(name,'/')) - msg = sh_translate("is"); - else + else if(!sh_isstate(SH_DEFPATH) + && (np = nv_search(name,shp->track_tree,0)) + && !nv_isattr(np,NV_NOALIAS) + && strcmp(cp,nv_getval(np))==0) msg = sh_translate(is_talias); + else + msg = sh_translate("is"); sfputr(sfstdout,msg,' '); } sfputr(sfstdout,sh_fmtq(cp),'\n'); - if(aflag) - { - if(aflag<=1) - aflag++; - if (pp) - pp = pp->next; - } - else - pp = 0; - if(tofree) - { - free((char*)cp); - tofree = 0; - } + free((char*)cp); } else if(aflag<=1) { @@ -288,6 +278,16 @@ static int whence(Shell_t *shp,char **argv, register int flags) if(flags&V_FLAG) errormsg(SH_DICT,ERROR_exit(0),e_found,sh_fmtq(name)); } + /* If -a given, continue with next result */ + if(aflag) + { + if(aflag<=1) + aflag++; + if(pp) + pp = pp->next; + } + else + pp = 0; } while(pp); } return(r); diff --git a/src/cmd/ksh93/include/name.h b/src/cmd/ksh93/include/name.h index d75ef67d5..5f75c717a 100644 --- a/src/cmd/ksh93/include/name.h +++ b/src/cmd/ksh93/include/name.h @@ -32,7 +32,6 @@ #include typedef int (*Nambfp_f)(int, char**, void*); -struct pathcomp; /* Nodes can have all kinds of values */ union Value @@ -55,7 +54,6 @@ union Value struct Namfun *funp; /* discipline pointer */ struct Namref *nrp; /* name reference */ Nambfp_f bfp; /* builtin entry point function pointer */ - struct pathcomp *pathcomp; }; #include "nval.h" diff --git a/src/cmd/ksh93/include/path.h b/src/cmd/ksh93/include/path.h index 481b6040c..ab8986846 100644 --- a/src/cmd/ksh93/include/path.h +++ b/src/cmd/ksh93/include/path.h @@ -78,7 +78,7 @@ extern Pathcomp_t *path_addpath(Shell_t*,Pathcomp_t*,const char*,int); extern Pathcomp_t *path_dup(Pathcomp_t*); extern void path_delete(Pathcomp_t*); extern void path_alias(Namval_t*,Pathcomp_t*); -extern Pathcomp_t *path_absolute(Shell_t*, const char*, Pathcomp_t*); +extern Pathcomp_t *path_absolute(Shell_t*, const char*, Pathcomp_t*, int); extern char *path_basename(const char*); extern char *path_fullname(Shell_t*,const char*); extern int path_expand(Shell_t*,const char*, struct argnod**); diff --git a/src/cmd/ksh93/include/version.h b/src/cmd/ksh93/include/version.h index 656a5ac9f..d995111c7 100644 --- a/src/cmd/ksh93/include/version.h +++ b/src/cmd/ksh93/include/version.h @@ -17,4 +17,4 @@ * David Korn * * * ***********************************************************************/ -#define SH_RELEASE "93u+m 2020-09-18" +#define SH_RELEASE "93u+m 2020-09-20" diff --git a/src/cmd/ksh93/sh/main.c b/src/cmd/ksh93/sh/main.c index 322a2b5ce..0a2d1e67f 100644 --- a/src/cmd/ksh93/sh/main.c +++ b/src/cmd/ksh93/sh/main.c @@ -300,7 +300,7 @@ int sh_main(int ac, char *av[], Shinit_f userinit) if(fdin < 0 && !strchr(name,'/')) { #ifdef PATH_BFPATH - if(path_absolute(shp,name,NIL(Pathcomp_t*))) + if(path_absolute(shp,name,NIL(Pathcomp_t*),0)) sp = stakptr(PATH_OFFSET); #else sp = path_absolute(shp,name,NIL(char*)); diff --git a/src/cmd/ksh93/sh/path.c b/src/cmd/ksh93/sh/path.c index ab8c85927..62b5907d0 100644 --- a/src/cmd/ksh93/sh/path.c +++ b/src/cmd/ksh93/sh/path.c @@ -643,12 +643,26 @@ static void funload(Shell_t *shp,int fno, const char *name) /* * 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 flag is 0, or if name not found, then try autoloading function and return 1 if successful. + * If flag is >=1, do a regular path search. If it yields an autoloadable function, load it. + * If flag is 2 or 3, never autoload a function but return 1 if name found on FPATH. + * If flag is 3, no tracked alias will be set (IOW, the result won't be cached in the hash table). * If oldpp is not NULL, it will contain a pointer to the path component * where it was found. + * + * path_search() returns 1/true if: + * - the given absolute path was found executable + * - the given name is a function or non-path-bound builtin, and a path search found nothing external + * - the given name matched an autoloadable function on FPATH + * + * path_search() returns 0/false if: + * - the given relative path was found executable; the PWD is prefixed to make it absolute + * - a tracked alias (a.k.a. hash table entry) was found and used + * - the given name was found on PATH as an executable command or path-bound builtin (irrespective of + * whether it exists as a function or normal builtin); its full path is written on the stack, except + * if the matching $PATH entry is '.' or empty, the simple name is written without prefixing the PWD + * - nothing executable was found */ 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 Pathcomp_t *pp=0; if(name && strchr(name,'/')) { + char *pwd; stakseek(PATH_OFFSET); stakputs(name); 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 if(*name=='/') return(1); stakseek(PATH_OFFSET); - stakputs(path_pwd(shp,1)); + pwd = path_pwd(shp,1); + if(pwd[1]) /* if pwd=="/", avoid starting with "//" */ + stakputs(pwd); stakputc('/'); stakputs(name); stakputc(0); @@ -697,7 +714,7 @@ int path_search(Shell_t *shp,register const char *name,Pathcomp_t **oldpp, int f stakputc(0); return(0); } - pp = path_absolute(shp,name,oldpp?*oldpp:NIL(Pathcomp_t*)); + pp = path_absolute(shp,name,oldpp?*oldpp:NIL(Pathcomp_t*),flag); if(oldpp) *oldpp = pp; 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 pp=sh_isstate(SH_DEFPATH)?shp->defpathlist:shp->pathlist; if(pp && strmatch(name,e_alphanum) && (fno=path_opentype(shp,name,pp,1))>=0) { - if(flag==2) + if(flag >= 2) { sh_close(fno); return(1); @@ -732,8 +749,10 @@ int path_search(Shell_t *shp,register const char *name,Pathcomp_t **oldpp, int f /* * do a path search and find the full pathname of file name + * + * If flag >= 2, do not autoload functions (cf. path_search()). */ -Pathcomp_t *path_absolute(Shell_t *shp,register const char *name, Pathcomp_t *pp) +Pathcomp_t *path_absolute(Shell_t *shp,register const char *name, Pathcomp_t *pp, int flag) { register int f,isfun; int noexec=0; @@ -874,10 +893,12 @@ Pathcomp_t *path_absolute(Shell_t *shp,register const char *name, Pathcomp_t *pp } if(isfun && f>=0) { - nv_onattr(nv_open(name,sh_subfuntree(1),NV_NOARRAY|NV_IDENT|NV_NOSCOPE),NV_LTOU|NV_FUNCTION); - funload(shp,f,name); - close(f); - f = -1; + if(flag < 2) + { + nv_onattr(nv_open(name,sh_subfuntree(1),NV_NOARRAY|NV_IDENT|NV_NOSCOPE),NV_LTOU|NV_FUNCTION); + funload(shp,f,name); + close(f); + } return(0); } else if(f>=0 && (oldpp->flags & PATH_STD_DIR)) diff --git a/src/cmd/ksh93/sh/xec.c b/src/cmd/ksh93/sh/xec.c index b44f68817..02151aef8 100644 --- a/src/cmd/ksh93/sh/xec.c +++ b/src/cmd/ksh93/sh/xec.c @@ -3619,7 +3619,7 @@ static pid_t sh_ntfork(Shell_t *shp,const Shnode_t *t,char *argv[],int *jobid,in && !nv_isattr(np,NV_NOALIAS) && np->nvalue.cp) path = nv_getval(np); - else if(path_absolute(shp,path,NIL(Pathcomp_t*))) + else if(path_absolute(shp,path,NIL(Pathcomp_t*),0)) { path = stkptr(shp->stk,PATH_OFFSET); stkfreeze(shp->stk,0); diff --git a/src/cmd/ksh93/tests/builtins.sh b/src/cmd/ksh93/tests/builtins.sh index 025126950..9b683bc17 100755 --- a/src/cmd/ksh93/tests/builtins.sh +++ b/src/cmd/ksh93/tests/builtins.sh @@ -763,7 +763,7 @@ foo=BUG command eval ':' [[ $foo == BUG ]] && err_exit '`command` fails to disable the special properties of special builtins' # ====== -# 'whence -a' tests +# whence -a/-v tests # wrong path to tracked aliases after loading builtin: https://github.com/ksh93/ksh/pull/25 actual=$("$SHELL" -c ' @@ -771,34 +771,75 @@ actual=$("$SHELL" -c ' builtin chmod whence -a chmod ') -expected="chmod is a shell builtin -$(whence -a -p chmod | sed 's/^/chmod is /') +expect="chmod is a shell builtin chmod is a tracked alias for $(whence -p chmod)" -[[ $actual == $expected ]] || err_exit "'whence -a' does not work correctly with tracked aliases" \ - "(expected $(printf %q "$expected"), got $(printf %q "$actual"))" +[[ $actual == "$expect" ]] || err_exit "'whence -a' does not work correctly with tracked aliases" \ + "(expected $(printf %q "$expect"), got $(printf %q "$actual"))" # spurious 'undefined function' message: https://github.com/ksh93/ksh/issues/26 actual=$("$SHELL" -c 'whence -a printf') -expected="printf is a shell builtin +expect="printf is a shell builtin $(whence -a -p printf | sed 's/^/printf is /')" -[[ $actual == $expected ]] || err_exit "'whence -a': incorrect output" \ - "(expected $(printf %q "$expected"), got $(printf %q "$actual"))" +[[ $actual == "$expect" ]] || err_exit "'whence -a': incorrect output" \ + "(expected $(printf %q "$expect"), got $(printf %q "$actual"))" # 'whence -a'/'type -a' failed to list builtin if function exists: https://github.com/ksh93/ksh/issues/83 actual=$(printf() { :; }; whence -a printf) -expected="printf is a function +expect="printf is a function printf is a shell builtin $(whence -a -p printf | sed 's/^/printf is /')" -[[ $actual == $expected ]] || err_exit "'whence -a': incorrect output for function+builtin" \ - "(expected $(printf %q "$expected"), got $(printf %q "$actual"))" - -# 'whence -a'/'type -a' failed to list builtin if autoload function exists: https://github.com/ksh93/ksh/issues/83 +[[ $actual == "$expect" ]] || err_exit "'whence -a': incorrect output for function+builtin" \ + "(expected $(printf %q "$expect"), got $(printf %q "$actual"))" actual=$(autoload printf; whence -a printf) -expected="printf is an undefined function +expect="printf is an undefined function printf is a shell builtin $(whence -a -p printf | sed 's/^/printf is /')" -[[ $actual == $expected ]] || err_exit "'whence -a': incorrect output for autoload+builtin" \ - "(expected $(printf %q "$expected"), got $(printf %q "$actual"))" +[[ $actual == "$expect" ]] || err_exit "'whence -a': incorrect output for autoload+builtin" \ + "(expected $(printf %q "$expect"), got $(printf %q "$actual"))" + +# 'whence -v' canonicalized paths improperly: https://github.com/ksh93/ksh/issues/84 +cmdpath=${ whence -p printf; } +actual=$(cd /; whence -v "${cmdpath#/}") +expect="${cmdpath#/} is $cmdpath" +[[ $actual == "$expect" ]] || err_exit "'whence -v': incorrect canonicalization of initial /" \ + "(expected $(printf %q "$expect"), got $(printf %q "$actual"))" +dotdot= +num=$(set -f; IFS=/; set -- $PWD; echo $#) +for ((i=1; i$tmp/ls +expect=$'/dev/null\n/dev/null' +actual=$(FPATH=$tmp; ls /dev/null; whence -a ls >/dev/null; ls /dev/null) +[[ $actual == "$expect" ]] || err_exit "'whence -a': mistaken \$FPATH function autoload (non-executable file)" \ + "(expected $(printf %q "$expect"), got $(printf %q "$actual"))" +chmod +x "$tmp/ls" +actual=$(FPATH=$tmp; ls /dev/null; whence -a ls >/dev/null; ls /dev/null) +[[ $actual == "$expect" ]] || err_exit "'whence -a': mistaken \$FPATH function autoload (executable file)" \ + "(expected $(printf %q "$expect"), got $(printf %q "$actual"))" + +# "tracked aliases" (known on other shells as hash table entries) are really just cached PATH search +# results; they should be reported independently from real aliases, as they're actually completely +# different things, and "tracked aliases" are actually used when bypassing an alias (with e.g. \ls). +expect="ls is an alias for 'echo ALL UR F1LEZ R G0N3' +ls is a tracked alias for /bin/ls" +actual=$(hash -r; alias ls='echo ALL UR F1LEZ R G0N3'; hash ls; whence -a ls) +[[ $actual == "$expect" ]] || err_exit "'whence -a' does not report tracked alias if alias exists" \ + "(expected $(printf %q "$expect"), got $(printf %q "$actual"))" # ====== # 'cd ../.foo' should not exclude the '.' in '.foo' diff --git a/src/cmd/ksh93/tests/path.sh b/src/cmd/ksh93/tests/path.sh index 1857d40a6..9191b4ebb 100755 --- a/src/cmd/ksh93/tests/path.sh +++ b/src/cmd/ksh93/tests/path.sh @@ -165,22 +165,28 @@ do exp="$cmd found" print print $exp > $cmd $chmod +x $cmd got=$($SHELL -c "unset FPATH; PATH=/dev/null; $cmd" 2>&1) - [[ $got == $exp ]] && err_exit "$cmd as last command should not find ./$cmd with PATH=/dev/null" - got=$($SHELL -c "unset FPATH; PATH=/dev/null; $cmd" 2>&1) - [[ $got == $exp ]] && err_exit "$cmd should not find ./$cmd with PATH=/dev/null" + [[ $got == "$exp" ]] && err_exit "$cmd as last command should not find ./$cmd with PATH=/dev/null" \ + "(expected $(printf %q "$exp"), got $(printf %q "$got"))" exp=$PWD/./$cmd - got=$(unset FPATH; PATH=/dev/null; whence ./$cmd) - [[ $got == $exp ]] || err_exit "whence $cmd should find ./$cmd with PATH=/dev/null" + got=$($SHELL -c "unset FPATH; PATH=/dev/null; $cmd" 2>&1) + [[ $got == "$exp" ]] && err_exit "$cmd should not find ./$cmd with PATH=/dev/null" \ + "(expected $(printf %q "$exp"), got $(printf %q "$got"))" exp=$PWD/$cmd + got=$(unset FPATH; PATH=/dev/null; whence ./$cmd) + [[ $got == "$exp" ]] || err_exit "whence $cmd should find ./$cmd with PATH=/dev/null" \ + "(expected $(printf %q "$exp"), got $(printf %q "$got"))" got=$(unset FPATH; PATH=/dev/null; whence $PWD/$cmd) - [[ $got == $exp ]] || err_exit "whence \$PWD/$cmd should find ./$cmd with PATH=/dev/null" + [[ $got == "$exp" ]] || err_exit "whence \$PWD/$cmd should find ./$cmd with PATH=/dev/null" \ + "(expected $(printf %q "$exp"), got $(printf %q "$got"))" done exp='' got=$($SHELL -c "unset FPATH; PATH=/dev/null; whence ./notfound" 2>&1) -[[ $got == $exp ]] || err_exit "whence ./$cmd failed -- expected '$exp', got '$got'" +[[ $got == "$exp" ]] || err_exit "whence ./$cmd failed -- expected '$exp', got '$got'" \ + "(expected $(printf %q "$exp"), got $(printf %q "$got"))" got=$($SHELL -c "unset FPATH; PATH=/dev/null; whence $PWD/notfound" 2>&1) -[[ $got == $exp ]] || err_exit "whence \$PWD/$cmd failed -- expected '$exp', got '$got'" +[[ $got == "$exp" ]] || err_exit "whence \$PWD/$cmd failed -- expected '$exp', got '$got'" \ + "(expected $(printf %q "$exp"), got $(printf %q "$got"))" unset FPATH PATH=/dev/null @@ -189,36 +195,46 @@ do exp="$cmd found" print print $exp > $cmd $chmod +x $cmd got=$($cmd 2>&1) - [[ $got == $exp ]] && err_exit "$cmd as last command should not find ./$cmd with PATH=/dev/null" + [[ $got == "$exp" ]] && err_exit "$cmd as last command should not find ./$cmd with PATH=/dev/null" \ + "(expected $(printf %q "$exp"), got $(printf %q "$got"))" got=$($cmd 2>&1; :) - [[ $got == $exp ]] && err_exit "$cmd should not find ./$cmd with PATH=/dev/null" - exp=$PWD/./$cmd - got=$(whence ./$cmd) - [[ $got == $exp ]] || err_exit "whence ./$cmd should find ./$cmd with PATH=/dev/null" + [[ $got == "$exp" ]] && err_exit "$cmd should not find ./$cmd with PATH=/dev/null" \ + "(expected $(printf %q "$exp"), got $(printf %q "$got"))" exp=$PWD/$cmd + got=$(whence ./$cmd) + [[ $got == "$exp" ]] || err_exit "whence ./$cmd should find ./$cmd with PATH=/dev/null" \ + "(expected $(printf %q "$exp"), got $(printf %q "$got"))" got=$(whence $PWD/$cmd) - [[ $got == $exp ]] || err_exit "whence \$PWD/$cmd should find ./$cmd with PATH=/dev/null" + [[ $got == "$exp" ]] || err_exit "whence \$PWD/$cmd should find ./$cmd with PATH=/dev/null" \ + "(expected $(printf %q "$exp"), got $(printf %q "$got"))" + got=$(cd / && whence "${OLDPWD#/}/$cmd") + [[ $got == "$exp" ]] || err_exit "whence output should not start with '//' if PWD is '/'" \ + "(expected $(printf %q "$exp"), got $(printf %q "$got"))" done exp='' got=$(whence ./notfound) -[[ $got == $exp ]] || err_exit "whence ./$cmd failed -- expected '$exp', got '$got'" +[[ $got == "$exp" ]] || err_exit "whence ./$cmd failed -- expected '$exp', got '$got'" \ + "(expected $(printf %q "$exp"), got $(printf %q "$got"))" got=$(whence $PWD/notfound) -[[ $got == $exp ]] || err_exit "whence \$PWD/$cmd failed -- expected '$exp', got '$got'" - +[[ $got == "$exp" ]] || err_exit "whence \$PWD/$cmd failed -- expected '$exp', got '$got'" \ + "(expected $(printf %q "$exp"), got $(printf %q "$got"))" PATH=$d: command -p cp "$rm" kshrm -if [[ $(whence kshrm) != $PWD/kshrm ]] -then err_exit 'trailing : in pathname not working' -fi +got=$(whence kshrm) +exp=$PWD/kshrm +[[ $got == "$exp" ]] || err_exit 'trailing : in pathname not working' \ + "(expected $(printf %q "$exp"), got $(printf %q "$got"))" command -p cp "$rm" rm PATH=:$d -if [[ $(whence rm) != $PWD/rm ]] -then err_exit 'leading : in pathname not working' -fi +got=$(whence rm) +exp=$PWD/rm +[[ $got == "$exp" ]] || err_exit 'leading : in pathname not working' \ + "(expected $(printf %q "$exp"), got $(printf %q "$got"))" PATH=$d: whence rm > /dev/null -if [[ $(whence rm) != $PWD/rm ]] -then err_exit 'pathname not restored after scoping' -fi +got=$(whence rm) +exp=$PWD/rm +[[ $got == "$exp" ]] || err_exit 'pathname not restored after scoping' \ + "(expected $(printf %q "$exp"), got $(printf %q "$got"))" command -p mkdir bin print 'print ok' > bin/tst command -p chmod +x bin/tst