Paul Smith wrote: > Having PATH= be equivalent to PATH=., and even > moreso "unset PATH" be equivalent to PATH=., is quite odd IMO.
It is just as odd as the fact that PATH=:/bin is equivalent to PATH=.:/bin and PATH=/bin: is equivalent to PATH=/bin:. > > > My personal opinion is that it's not difficult to come up with ways > > > findprog can be useful _in addition_ to simply being a precursor to > > > exec > > > > In this case we should probably add a flag argument that tells the > > function to do the complete lookup also when the progname contains a > > slash. For now, until someone claims that this functionality would > > be actually useful, I'll leave it as is. > > I think this will not be needed. In the event that someone does need > this they can simply use the findprog-in module instead. That's not what I meant. Hope it's clearer with this patch (which I'll push once savannah is working again). 2019-09-09 Bruno Haible <br...@clisp.org> findprog-in: Make exec optimization optional. * lib/findprog.h: Add double-inclusion guard. Include <stdbool.h>. (find_in_given_path): Add optimize_for_exec parameter. * lib/findprog-in.c (find_in_given_path): Likewise. diff --git a/lib/findprog.h b/lib/findprog.h index 9bc8a60..f7b4407 100644 --- a/lib/findprog.h +++ b/lib/findprog.h @@ -15,6 +15,10 @@ You should have received a copy of the GNU General Public License along with this program. If not, see <https://www.gnu.org/licenses/>. */ +#ifndef _FINDPROG_H +#define _FINDPROG_H + +#include <stdbool.h> #ifdef __cplusplus extern "C" { @@ -41,10 +45,16 @@ extern const char *find_in_path (const char *progname); or relative to the current directory). The returned string can be used with either execl/execv or execlp/execvp. It is freshly malloc()ed if it is != PROGNAME. - - Otherwise, it returns NULL. */ -extern const char *find_in_given_path (const char *progname, const char *path); + - Otherwise, it returns NULL. + If OPTIMIZE_FOR_EXEC is true, the function saves some work, under the + assumption that the resulting pathname will not be accessed directly, + only through execl/execv or execlp/execvp. */ +extern const char *find_in_given_path (const char *progname, const char *path, + bool optimize_for_exec); #ifdef __cplusplus } #endif + +#endif /* _FINDPROG_H */ diff --git a/lib/findprog-in.c b/lib/findprog-in.c index 3d70b7b..99b3c31 100644 --- a/lib/findprog-in.c +++ b/lib/findprog-in.c @@ -71,26 +71,84 @@ static const char * const suffixes[] = }; const char * -find_in_given_path (const char *progname, const char *path) +find_in_given_path (const char *progname, const char *path, + bool optimize_for_exec) { { bool has_slash = false; - const char *p; + { + const char *p; - for (p = progname; *p != '\0'; p++) - if (ISSLASH (*p)) - { - has_slash = true; - break; - } + for (p = progname; *p != '\0'; p++) + if (ISSLASH (*p)) + { + has_slash = true; + break; + } + } if (has_slash) - /* If progname contains a slash, it is either absolute or relative to - the current directory. PATH is not used. - We could try the various suffixes and see whether one of the files - with such a suffix is actually executable. But this is not needed, - since the execl/execv/execlp/execvp functions will do these tests - anyway. */ - return progname; + { + /* If progname contains a slash, it is either absolute or relative to + the current directory. PATH is not used. */ + if (optimize_for_exec) + /* The execl/execv/execlp/execvp functions will try the various + suffixes anyway and fail if no executable is found. */ + return progname; + else + { + /* Try the various suffixes and see whether one of the files + with such a suffix is actually executable. */ + size_t i; + #if defined _WIN32 && !defined __CYGWIN__ /* Native Windows */ + const char *progbasename; + + { + const char *p; + + progbasename = progname; + for (p = progname; *p != '\0'; p++) + if (ISSLASH (*p)) + progbasename = p + 1; + } + #endif + + /* Try all platform-dependent suffixes. */ + for (i = 0; i < sizeof (suffixes) / sizeof (suffixes[0]); i++) + { + const char *suffix = suffixes[i]; + + #if defined _WIN32 && !defined __CYGWIN__ /* Native Windows */ + /* File names without a '.' are not considered executable. */ + if (*suffix != '\0' || strchr (progbasename, '.') != NULL) + #endif + { + /* Concatenate progname and suffix. */ + char *progpathname = + xconcatenated_filename ("", progname, suffix); + + /* On systems which have the eaccess() system call, let's + use it. On other systems, let's hope that this program + is not installed setuid or setgid, so that it is ok to + call access() despite its design flaw. */ + if (eaccess (progpathname, X_OK) == 0) + { + /* Found! */ + if (strcmp (progpathname, progname) == 0) + { + free (progpathname); + return progname; + } + else + return progpathname; + } + + free (progpathname); + } + } + + return NULL; + } + } } if (path == NULL) @@ -131,14 +189,14 @@ find_in_given_path (const char *progname, const char *path) if (*suffix != '\0' || strchr (progname, '.') != NULL) #endif { - /* Concatenate dir and progname. */ + /* Concatenate dir, progname, and suffix. */ char *progpathname = xconcatenated_filename (dir, progname, suffix); - /* On systems which have the eaccess() system call, let's use - it. On other systems, let's hope that this program is not - installed setuid or setgid, so that it is ok to call - access() despite its design flaw. */ + /* On systems which have the eaccess() system call, let's + use it. On other systems, let's hope that this program + is not installed setuid or setgid, so that it is ok to + call access() despite its design flaw. */ if (eaccess (progpathname, X_OK) == 0) { /* Found! */