bug-gnulib
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [PATCH] findprog: Support searching in a specified path string


From: Bruno Haible
Subject: Re: [PATCH] findprog: Support searching in a specified path string
Date: Sun, 08 Sep 2019 18:25:40 +0200
User-agent: KMail/5.1.3 (Linux/4.4.0-159-generic; KDE/5.18.0; x86_64; ; )

Hi Paul,

Here's the implementation I'm committing.

I feel that merging the two functions in a single file would add more
contortions than benefit.


2019-09-08  Bruno Haible  <address@hidden>

        findprog-in: New module.
        Suggested by Paul Smith <address@hidden>.
        * lib/findprog.h (find_in_given_path): New declaration.
        * lib/findprog-in.c: New file, based on lib/findprog.c.
        * m4/findprog-in.m4: New file, based on m4/findprog.m4.
        * modules/findprog-in: New file.

diff --git a/lib/findprog.h b/lib/findprog.h
index a354f67..9bc8a60 100644
--- a/lib/findprog.h
+++ b/lib/findprog.h
@@ -21,16 +21,29 @@ extern "C" {
 #endif
 
 
-/* Look up a program in the PATH.
-   Attempt to determine the pathname that would be called by execlp/execvp
-   of PROGNAME.  If successful, return a pathname containing a slash
-   (either absolute or relative to the current directory).  Otherwise,
-   return PROGNAME unmodified.
+/* Looks up a program in the PATH.
+   Attempts to determine the pathname that would be called by execlp/execvp
+   of PROGNAME.  If successful, it returns a pathname containing a slash
+   (either absolute or relative to the current directory).  Otherwise, it
+   returns PROGNAME unmodified.
    Because of the latter case, callers should use execlp/execvp, not
    execl/execv on the returned pathname.
    The returned string is freshly malloc()ed if it is != PROGNAME.  */
 extern const char *find_in_path (const char *progname);
 
+/* Looks up a program in the given PATH-like string.
+   The PATH argument consists of a list of directories, separated by ':' or
+   (on native Windows) by ';'.  An empty PATH element designates the current
+   directory.  A null PATH is equivalent to an empty PATH, that is, to the
+   singleton list that contains only the current directory.
+   Determines the pathname that would be called by execlp/execvp of PROGNAME.
+   - If successful, it returns a pathname containing a slash (either absolute
+     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);
+
 
 #ifdef __cplusplus
 }
diff --git a/lib/findprog-in.c b/lib/findprog-in.c
new file mode 100644
index 0000000..3d70b7b
--- /dev/null
+++ b/lib/findprog-in.c
@@ -0,0 +1,178 @@
+/* Locating a program in a given path.
+   Copyright (C) 2001-2004, 2006-2019 Free Software Foundation, Inc.
+   Written by Bruno Haible <address@hidden>, 2001, 2019.
+
+   This program is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
+
+
+#include <config.h>
+
+/* Specification.  */
+#include "findprog.h"
+
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "filename.h"
+#include "concat-filename.h"
+#include "xalloc.h"
+
+#if (defined _WIN32 && !defined __CYGWIN__) || defined __EMX__ || defined 
__DJGPP__
+  /* Native Windows, OS/2, DOS */
+# define NATIVE_SLASH '\\'
+#else
+  /* Unix */
+# define NATIVE_SLASH '/'
+#endif
+
+/* Separator in PATH like lists of pathnames.  */
+#if (defined _WIN32 && !defined __CYGWIN__) || defined __EMX__ || defined 
__DJGPP__
+  /* Native Windows, OS/2, DOS */
+# define PATH_SEPARATOR ';'
+#else
+  /* Unix */
+# define PATH_SEPARATOR ':'
+#endif
+
+/* The list of suffixes that the execlp/execvp function tries when searching
+   for the program.  */
+static const char * const suffixes[] =
+  {
+    #if defined _WIN32 && !defined __CYGWIN__ /* Native Windows */
+    "", ".com", ".exe", ".bat", ".cmd"
+    /* Note: Files without any suffix are not considered executable.  */
+    /* Note: The cmd.exe program does a different lookup: It searches according
+       to the PATHEXT environment variable.
+       See <https://stackoverflow.com/questions/7839150/>.
+       Also, it executes files ending .bat and .cmd directly without letting 
the
+       kernel interpret the program file.  */
+    #elif defined __CYGWIN__
+    "", ".exe", ".com"
+    #elif defined __EMX__
+    "", ".exe"
+    #elif defined __DJGPP__
+    "", ".com", ".exe", ".bat"
+    #else /* Unix */
+    ""
+    #endif
+  };
+
+const char *
+find_in_given_path (const char *progname, const char *path)
+{
+  {
+    bool has_slash = false;
+    const char *p;
+
+    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 (path == NULL)
+    /* If PATH is not set, the default search path is implementation dependent.
+       In practice, it is treated like an empty PATH.  */
+    path = "";
+
+  {
+    /* Make a copy, to prepare for destructive modifications.  */
+    char *path_copy = xstrdup (path);
+    char *path_rest;
+    char *cp;
+
+    for (path_rest = path_copy; ; path_rest = cp + 1)
+      {
+        const char *dir;
+        bool last;
+        size_t i;
+
+        /* Extract next directory in PATH.  */
+        dir = path_rest;
+        for (cp = path_rest; *cp != '\0' && *cp != PATH_SEPARATOR; cp++)
+          ;
+        last = (*cp == '\0');
+        *cp = '\0';
+
+        /* Empty PATH components designate the current directory.  */
+        if (dir == cp)
+          dir = ".";
+
+        /* 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 (progname, '.') != NULL)
+            #endif
+              {
+                /* Concatenate dir and progname.  */
+                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.  */
+                if (eaccess (progpathname, X_OK) == 0)
+                  {
+                    /* Found!  */
+                    if (strcmp (progpathname, progname) == 0)
+                      {
+                        free (progpathname);
+
+                        /* Add the "./" prefix for real, that
+                           xconcatenated_filename() optimized away.  This
+                           avoids a second PATH search when the caller uses
+                           execl/execv/execlp/execvp.  */
+                        progpathname =
+                          XNMALLOC (2 + strlen (progname) + 1, char);
+                        progpathname[0] = '.';
+                        progpathname[1] = NATIVE_SLASH;
+                        memcpy (progpathname + 2, progname,
+                                strlen (progname) + 1);
+                      }
+
+                    free (path_copy);
+                    return progpathname;
+                  }
+
+                free (progpathname);
+              }
+          }
+
+        if (last)
+          break;
+      }
+
+    /* Not found in PATH.  */
+    free (path_copy);
+  }
+
+  return NULL;
+}
diff --git a/m4/findprog-in.m4 b/m4/findprog-in.m4
new file mode 100644
index 0000000..baa7e6b
--- /dev/null
+++ b/m4/findprog-in.m4
@@ -0,0 +1,11 @@
+# findprog-in.m4 serial 1
+dnl Copyright (C) 2003, 2009-2019 Free Software Foundation, Inc.
+dnl This file is free software; the Free Software Foundation
+dnl gives unlimited permission to copy and/or distribute it,
+dnl with or without modifications, as long as this notice is preserved.
+
+AC_DEFUN([gl_FINDPROG_IN],
+[
+  dnl Prerequisites of lib/findprog-in.c.
+  AC_REQUIRE([gl_FUNC_EACCESS])
+])
diff --git a/modules/findprog-in b/modules/findprog-in
new file mode 100644
index 0000000..ce7faa5
--- /dev/null
+++ b/modules/findprog-in
@@ -0,0 +1,30 @@
+Description:
+Locating a program in a given path.
+
+Files:
+lib/findprog.h
+lib/findprog-in.c
+m4/findprog-in.m4
+m4/eaccess.m4
+
+Depends-on:
+stdbool
+filename
+xalloc
+xconcat-filename
+unistd
+
+configure.ac:
+gl_FINDPROG_IN
+
+Makefile.am:
+lib_SOURCES += findprog.h findprog-in.c
+
+Include:
+"findprog.h"
+
+License:
+GPL
+
+Maintainer:
+all




reply via email to

[Prev in Thread] Current Thread [Next in Thread]