diff --git a/NEWS b/NEWS index e6d915d4f..7d9277894 100644 --- a/NEWS +++ b/NEWS @@ -5,6 +5,9 @@ Any uppercase BUG_* names are modernish shell bug IDs. 2020-09-25: +- whence -v/-a now reports the path to the file that an "undefined" (i.e. + autoloadable) function will be loaded from when invoked, if found in $FPATH. + - When ksh invoked a shell script that does not have a leading #!/hashbang/path, 'ps' and /proc//cmdline showed corrupted output if the new script's command line was shorter than that of the invoking script. diff --git a/src/cmd/ksh93/bltins/whence.c b/src/cmd/ksh93/bltins/whence.c index fb862ac69..5bb1fa7b4 100644 --- a/src/cmd/ksh93/bltins/whence.c +++ b/src/cmd/ksh93/bltins/whence.c @@ -139,7 +139,7 @@ static int whence(Shell_t *shp,char **argv, register int flags) register const char *msg; Namval_t *nq; char *notused; - Pathcomp_t *pp=0; + Pathcomp_t *pp; if(flags&Q_FLAG) flags &= ~A_FLAG; while(name= *argv++) @@ -179,16 +179,24 @@ static int whence(Shell_t *shp,char **argv, register int flags) bltins: if(!(flags&F_FLAG) && (np = nv_bfsearch(name, shp->fun_tree, &nq, ¬used)) && !is_abuiltin(np)) { - if(flags&V_FLAG) - if(nv_isnull(np)) - cp = sh_translate(is_ufunction); - else - cp = sh_translate(is_function); - else - cp = ""; if(flags&Q_FLAG) continue; - sfprintf(sfstdout,"%s%s\n",name,cp); + sfputr(sfstdout,name,-1); + if(flags&V_FLAG) + { + if(nv_isnull(np)) + { + sfprintf(sfstdout,sh_translate(is_ufunction)); + pp = 0; + while(!path_search(shp,name,&pp,3) && pp && (pp = pp->next)) + ; + if(*stakptr(PATH_OFFSET)=='/') + sfprintf(sfstdout,sh_translate(e_autoloadfrom),sh_fmtq(stakptr(PATH_OFFSET))); + } + else + sfprintf(sfstdout,sh_translate(is_function)); + } + sfputc(sfstdout,'\n'); if(!aflag) continue; aflag++; @@ -210,6 +218,7 @@ static int whence(Shell_t *shp,char **argv, register int flags) aflag++; } search: + pp = 0; do { int maybe_undef_fn = 0; /* flag for possible undefined (i.e. autoloadable) function */ @@ -246,7 +255,10 @@ static int whence(Shell_t *shp,char **argv, register int flags) /* Undefined/autoloadable function on FPATH */ sfputr(sfstdout,sh_fmtq(cp),-1); if(flags&V_FLAG) - sfputr(sfstdout,sh_translate(is_ufunction),-1); + { + sfprintf(sfstdout,sh_translate(is_ufunction)); + sfprintf(sfstdout,sh_translate(e_autoloadfrom),sh_fmtq(stakptr(PATH_OFFSET))); + } sfputc(sfstdout,'\n'); } } diff --git a/src/cmd/ksh93/data/builtins.c b/src/cmd/ksh93/data/builtins.c index 291f533f7..46130c91c 100644 --- a/src/cmd/ksh93/data/builtins.c +++ b/src/cmd/ksh93/data/builtins.c @@ -1988,7 +1988,7 @@ _JOB_ ; const char sh_optwhence[] = -"[-1c?\n@(#)$Id: whence (AT&T Research/ksh93) 2020-07-20 $\n]" +"[-1c?\n@(#)$Id: whence (AT&T Research/ksh93) 2020-09-25 $\n]" USAGE_LICENSE "[+NAME?whence, type - locate a command and describe its type]" "[+DESCRIPTION?Without \b-v\b, \bwhence\b writes on standard output an " diff --git a/src/cmd/ksh93/data/msg.c b/src/cmd/ksh93/data/msg.c index c3269c6c0..99268bfc7 100644 --- a/src/cmd/ksh93/data/msg.c +++ b/src/cmd/ksh93/data/msg.c @@ -141,6 +141,7 @@ const char is_alias[] = "%s is an alias for "; const char is_talias[] = "is a tracked alias for"; const char is_function[] = " is a function"; const char is_ufunction[] = " is an undefined function"; +const char e_autoloadfrom[] = " (autoload from %s)"; #ifdef JOBS # ifdef SIGTSTP const char e_newtty[] = "Switching to new tty driver..."; diff --git a/src/cmd/ksh93/include/path.h b/src/cmd/ksh93/include/path.h index ab8986846..1ddfe093e 100644 --- a/src/cmd/ksh93/include/path.h +++ b/src/cmd/ksh93/include/path.h @@ -129,6 +129,7 @@ extern const char is_reserved[]; extern const char is_talias[]; extern const char is_function[]; extern const char is_ufunction[]; +extern const char e_autoloadfrom[]; #ifdef SHELLMAGIC extern const char e_prohibited[]; #endif /* SHELLMAGIC */ diff --git a/src/cmd/ksh93/tests/path.sh b/src/cmd/ksh93/tests/path.sh index 9191b4ebb..c58f8a9ec 100755 --- a/src/cmd/ksh93/tests/path.sh +++ b/src/cmd/ksh93/tests/path.sh @@ -29,6 +29,7 @@ Command=${0##*/} integer Errors=0 [[ -d $tmp && -w $tmp && $tmp == "$PWD" ]] || { err\_exit "$LINENO" '$tmp not set; run this from shtests. Aborting.'; exit 1; } +PATH_orig=$PATH type /xxxxxx > out1 2> out2 [[ -s out1 ]] && err_exit 'type should not write on stdout for not found case' @@ -417,6 +418,8 @@ ${.sh.version} END ) || err_exit '${.sh.xxx} variables causes cat not be found' +PATH=$PATH_orig + # ====== # Check that 'command -p' searches the default OS utility PATH. expect=/dev/null @@ -448,6 +451,39 @@ actual=$(PATH=$tmp; redirect 2>&1; hash ls; command -p -v ls) [[ $actual == "$expect" ]] || err_exit "'command -p -v' fails to search default path if tracked alias exists" \ "(expected $(printf %q "$expect"), got $(printf %q "$actual"))" +# ====== +# Unlike pdksh, ksh93 didn't report the path to autoloadable functions, which was an annoying omission. +if ((.sh.version >= 20200925)) +then fundir=$tmp/whencefun + mkdir $fundir + echo "whence_FPATH_test() { echo I\'m just on FPATH; }" >$fundir/whence_FPATH_test + echo "whence_autoload_test() { echo I was explicitly autoloaded; }" >$fundir/whence_autoload_test + echo "function chmod { echo Hi, I\'m your new chmod!; }" >$fundir/chmod + echo "function ls { echo Hi, I\'m your new ls!; }" >$fundir/ls + hash -r + actual=$("$SHELL" -c 'FPATH=$1 + autoload chmod whence_autoload_test + whence -a chmod whence_FPATH_test whence_autoload_test ls cp + whence_FPATH_test + whence_autoload_test + cp --totally-invalid-option 2>/dev/null + ls --totally-invalid-option 2>/dev/null + chmod --totally-invalid-option' \ + whence_autoload_test "$fundir" 2>&1) + expect="chmod is an undefined function (autoload from $fundir/chmod)" + expect+=$'\n'"chmod is ${ whence -p chmod; }" + expect+=$'\n'"whence_FPATH_test is an undefined function (autoload from $fundir/whence_FPATH_test)" + expect+=$'\n'"whence_autoload_test is an undefined function (autoload from $fundir/whence_autoload_test)" + expect+=$'\n'"ls is a tracked alias for ${ whence -p ls; }" + expect+=$'\n'"ls is an undefined function (autoload from $fundir/ls)" + expect+=$'\n'"cp is a tracked alias for ${ whence -p cp; }" + expect+=$'\n'"I'm just on FPATH" + expect+=$'\n'"I was explicitly autoloaded" + expect+=$'\n'"Hi, I'm your new chmod!" + [[ $actual == "$expect" ]] || err_exit "failure in reporting or running autoloadable functions" \ + "(expected $(printf %q "$expect"), got $(printf %q "$actual"))" +fi + # ====== exit $((Errors<125?Errors:125))