root/maint/gnulib/lib/findprog-in.c

/* [previous][next][first][last][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. find_in_given_path

   1 /* Locating a program in a given path.
   2    Copyright (C) 2001-2004, 2006-2021 Free Software Foundation, Inc.
   3    Written by Bruno Haible <haible@clisp.cons.org>, 2001, 2019.
   4 
   5    This file is free software: you can redistribute it and/or modify
   6    it under the terms of the GNU Lesser General Public License as
   7    published by the Free Software Foundation; either version 2.1 of the
   8    License, or (at your option) any later version.
   9 
  10    This file is distributed in the hope that it will be useful,
  11    but WITHOUT ANY WARRANTY; without even the implied warranty of
  12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13    GNU Lesser General Public License for more details.
  14 
  15    You should have received a copy of the GNU Lesser General Public License
  16    along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
  17 
  18 
  19 #include <config.h>
  20 
  21 /* Specification.  */
  22 #include "findprog.h"
  23 
  24 #include <errno.h>
  25 #include <stdbool.h>
  26 #include <stdlib.h>
  27 #include <string.h>
  28 #include <unistd.h>
  29 #include <sys/stat.h>
  30 
  31 #include "filename.h"
  32 #include "concat-filename.h"
  33 
  34 #if (defined _WIN32 && !defined __CYGWIN__) || defined __EMX__ || defined __DJGPP__
  35   /* Native Windows, OS/2, DOS */
  36 # define NATIVE_SLASH '\\'
  37 #else
  38   /* Unix */
  39 # define NATIVE_SLASH '/'
  40 #endif
  41 
  42 /* Separator in PATH like lists of pathnames.  */
  43 #if (defined _WIN32 && !defined __CYGWIN__) || defined __EMX__ || defined __DJGPP__
  44   /* Native Windows, OS/2, DOS */
  45 # define PATH_SEPARATOR ';'
  46 #else
  47   /* Unix */
  48 # define PATH_SEPARATOR ':'
  49 #endif
  50 
  51 /* The list of suffixes that the execlp/execvp function tries when searching
  52    for the program.  */
  53 static const char * const suffixes[] =
  54   {
  55     #if defined _WIN32 && !defined __CYGWIN__ /* Native Windows */
  56     "", ".com", ".exe", ".bat", ".cmd"
  57     /* Note: Files without any suffix are not considered executable.  */
  58     /* Note: The cmd.exe program does a different lookup: It searches according
  59        to the PATHEXT environment variable.
  60        See <https://stackoverflow.com/questions/7839150/>.
  61        Also, it executes files ending in .bat and .cmd directly without letting
  62        the kernel interpret the program file.  */
  63     #elif defined __CYGWIN__
  64     "", ".exe", ".com"
  65     #elif defined __EMX__
  66     "", ".exe"
  67     #elif defined __DJGPP__
  68     "", ".com", ".exe", ".bat"
  69     #else /* Unix */
  70     ""
  71     #endif
  72   };
  73 
  74 const char *
  75 find_in_given_path (const char *progname, const char *path,
     /* [previous][next][first][last][top][bottom][index][help] */
  76                     const char *directory, bool optimize_for_exec)
  77 {
  78   {
  79     bool has_slash = false;
  80     {
  81       const char *p;
  82 
  83       for (p = progname; *p != '\0'; p++)
  84         if (ISSLASH (*p))
  85           {
  86             has_slash = true;
  87             break;
  88           }
  89     }
  90     if (has_slash)
  91       {
  92         /* If progname contains a slash, it is either absolute or relative to
  93            the current directory.  PATH is not used.  */
  94         if (optimize_for_exec)
  95           /* The execl/execv/execlp/execvp functions will try the various
  96              suffixes anyway and fail if no executable is found.  */
  97           return progname;
  98         else
  99           {
 100             /* Try the various suffixes and see whether one of the files
 101                with such a suffix is actually executable.  */
 102             int failure_errno;
 103             size_t i;
 104 
 105             const char *directory_as_prefix =
 106               (directory != NULL && IS_RELATIVE_FILE_NAME (progname)
 107                ? directory
 108                : "");
 109 
 110             #if defined _WIN32 && !defined __CYGWIN__ /* Native Windows */
 111             const char *progbasename;
 112 
 113             {
 114               const char *p;
 115 
 116               progbasename = progname;
 117               for (p = progname; *p != '\0'; p++)
 118                 if (ISSLASH (*p))
 119                   progbasename = p + 1;
 120             }
 121 
 122             bool progbasename_has_dot = (strchr (progbasename, '.') != NULL);
 123             #endif
 124 
 125             /* Try all platform-dependent suffixes.  */
 126             failure_errno = ENOENT;
 127             for (i = 0; i < sizeof (suffixes) / sizeof (suffixes[0]); i++)
 128               {
 129                 const char *suffix = suffixes[i];
 130 
 131                 #if defined _WIN32 && !defined __CYGWIN__ /* Native Windows */
 132                 /* File names without a '.' are not considered executable, and
 133                    for file names with a '.' no additional suffix is tried.  */
 134                 if ((*suffix != '\0') != progbasename_has_dot)
 135                 #endif
 136                   {
 137                     /* Concatenate directory_as_prefix, progname, suffix.  */
 138                     char *progpathname =
 139                       concatenated_filename (directory_as_prefix, progname,
 140                                              suffix);
 141 
 142                     if (progpathname == NULL)
 143                       return NULL; /* errno is set here */
 144 
 145                     /* On systems which have the eaccess() system call, let's
 146                        use it.  On other systems, let's hope that this program
 147                        is not installed setuid or setgid, so that it is ok to
 148                        call access() despite its design flaw.  */
 149                     if (eaccess (progpathname, X_OK) == 0)
 150                       {
 151                         /* Check that the progpathname does not point to a
 152                            directory.  */
 153                         struct stat statbuf;
 154 
 155                         if (stat (progpathname, &statbuf) >= 0)
 156                           {
 157                             if (! S_ISDIR (statbuf.st_mode))
 158                               {
 159                                 /* Found!  */
 160                                 if (strcmp (progpathname, progname) == 0)
 161                                   {
 162                                     free (progpathname);
 163                                     return progname;
 164                                   }
 165                                 else
 166                                   return progpathname;
 167                               }
 168 
 169                             errno = EACCES;
 170                           }
 171                       }
 172 
 173                     if (errno != ENOENT)
 174                       failure_errno = errno;
 175 
 176                     free (progpathname);
 177                   }
 178               }
 179             #if defined _WIN32 && !defined __CYGWIN__ /* Native Windows */
 180             if (failure_errno == ENOENT && !progbasename_has_dot)
 181               {
 182                 /* In the loop above, we skipped suffix = "".  Do this loop
 183                    round now, merely to provide a better errno than ENOENT.  */
 184 
 185                 char *progpathname =
 186                   concatenated_filename (directory_as_prefix, progname, "");
 187 
 188                 if (progpathname == NULL)
 189                   return NULL; /* errno is set here */
 190 
 191                 if (eaccess (progpathname, X_OK) == 0)
 192                   {
 193                     struct stat statbuf;
 194 
 195                     if (stat (progpathname, &statbuf) >= 0)
 196                       {
 197                         if (! S_ISDIR (statbuf.st_mode))
 198                           errno = ENOEXEC;
 199                         else
 200                           errno = EACCES;
 201                       }
 202                   }
 203 
 204                 failure_errno = errno;
 205 
 206                 free (progpathname);
 207               }
 208             #endif
 209 
 210             errno = failure_errno;
 211             return NULL;
 212           }
 213       }
 214   }
 215 
 216   if (path == NULL)
 217     /* If PATH is not set, the default search path is implementation dependent.
 218        In practice, it is treated like an empty PATH.  */
 219     path = "";
 220 
 221   {
 222     /* Make a copy, to prepare for destructive modifications.  */
 223     char *path_copy = strdup (path);
 224     if (path_copy == NULL)
 225       return NULL; /* errno is set here */
 226 
 227     int failure_errno;
 228     char *path_rest;
 229     char *cp;
 230 
 231     #if defined _WIN32 && !defined __CYGWIN__ /* Native Windows */
 232     bool progname_has_dot = (strchr (progname, '.') != NULL);
 233     #endif
 234 
 235     failure_errno = ENOENT;
 236     for (path_rest = path_copy; ; path_rest = cp + 1)
 237       {
 238         const char *dir;
 239         bool last;
 240         char *dir_as_prefix_to_free;
 241         const char *dir_as_prefix;
 242         size_t i;
 243 
 244         /* Extract next directory in PATH.  */
 245         dir = path_rest;
 246         for (cp = path_rest; *cp != '\0' && *cp != PATH_SEPARATOR; cp++)
 247           ;
 248         last = (*cp == '\0');
 249         *cp = '\0';
 250 
 251         /* Empty PATH components designate the current directory.  */
 252         if (dir == cp)
 253           dir = ".";
 254 
 255         /* Concatenate directory and dir.  */
 256         if (directory != NULL && IS_RELATIVE_FILE_NAME (dir))
 257           {
 258             dir_as_prefix_to_free =
 259               concatenated_filename (directory, dir, NULL);
 260             if (dir_as_prefix_to_free == NULL)
 261               {
 262                 /* errno is set here.  */
 263                 failure_errno = errno;
 264                 goto failed;
 265               }
 266             dir_as_prefix = dir_as_prefix_to_free;
 267           }
 268         else
 269           {
 270             dir_as_prefix_to_free = NULL;
 271             dir_as_prefix = dir;
 272           }
 273 
 274         /* Try all platform-dependent suffixes.  */
 275         for (i = 0; i < sizeof (suffixes) / sizeof (suffixes[0]); i++)
 276           {
 277             const char *suffix = suffixes[i];
 278 
 279             #if defined _WIN32 && !defined __CYGWIN__ /* Native Windows */
 280             /* File names without a '.' are not considered executable, and
 281                for file names with a '.' no additional suffix is tried.  */
 282             if ((*suffix != '\0') != progname_has_dot)
 283             #endif
 284               {
 285                 /* Concatenate dir_as_prefix, progname, and suffix.  */
 286                 char *progpathname =
 287                   concatenated_filename (dir_as_prefix, progname, suffix);
 288 
 289                 if (progpathname == NULL)
 290                   {
 291                     /* errno is set here.  */
 292                     failure_errno = errno;
 293                     free (dir_as_prefix_to_free);
 294                     goto failed;
 295                   }
 296 
 297                 /* On systems which have the eaccess() system call, let's
 298                    use it.  On other systems, let's hope that this program
 299                    is not installed setuid or setgid, so that it is ok to
 300                    call access() despite its design flaw.  */
 301                 if (eaccess (progpathname, X_OK) == 0)
 302                   {
 303                     /* Check that the progpathname does not point to a
 304                        directory.  */
 305                     struct stat statbuf;
 306 
 307                     if (stat (progpathname, &statbuf) >= 0)
 308                       {
 309                         if (! S_ISDIR (statbuf.st_mode))
 310                           {
 311                             /* Found!  */
 312                             if (strcmp (progpathname, progname) == 0)
 313                               {
 314                                 free (progpathname);
 315 
 316                                 /* Add the "./" prefix for real, that
 317                                    concatenated_filename() optimized away.
 318                                    This avoids a second PATH search when the
 319                                    caller uses execl/execv/execlp/execvp.  */
 320                                 progpathname =
 321                                   (char *) malloc (2 + strlen (progname) + 1);
 322                                 if (progpathname == NULL)
 323                                   {
 324                                     /* errno is set here.  */
 325                                     failure_errno = errno;
 326                                     free (dir_as_prefix_to_free);
 327                                     goto failed;
 328                                   }
 329                                 progpathname[0] = '.';
 330                                 progpathname[1] = NATIVE_SLASH;
 331                                 memcpy (progpathname + 2, progname,
 332                                         strlen (progname) + 1);
 333                               }
 334 
 335                             free (dir_as_prefix_to_free);
 336                             free (path_copy);
 337                             return progpathname;
 338                           }
 339 
 340                         errno = EACCES;
 341                       }
 342                   }
 343 
 344                 if (errno != ENOENT)
 345                   failure_errno = errno;
 346 
 347                 free (progpathname);
 348               }
 349           }
 350         #if defined _WIN32 && !defined __CYGWIN__ /* Native Windows */
 351         if (failure_errno == ENOENT && !progname_has_dot)
 352           {
 353             /* In the loop above, we skipped suffix = "".  Do this loop
 354                round now, merely to provide a better errno than ENOENT.  */
 355 
 356             char *progpathname =
 357               concatenated_filename (dir_as_prefix, progname, "");
 358 
 359             if (progpathname == NULL)
 360               {
 361                 /* errno is set here.  */
 362                 failure_errno = errno;
 363                 free (dir_as_prefix_to_free);
 364                 goto failed;
 365               }
 366 
 367             if (eaccess (progpathname, X_OK) == 0)
 368               {
 369                 struct stat statbuf;
 370 
 371                 if (stat (progpathname, &statbuf) >= 0)
 372                   {
 373                     if (! S_ISDIR (statbuf.st_mode))
 374                       errno = ENOEXEC;
 375                     else
 376                       errno = EACCES;
 377                   }
 378               }
 379 
 380             failure_errno = errno;
 381 
 382             free (progpathname);
 383           }
 384         #endif
 385 
 386         free (dir_as_prefix_to_free);
 387 
 388         if (last)
 389           break;
 390       }
 391 
 392    failed:
 393     /* Not found in PATH.  */
 394     free (path_copy);
 395 
 396     errno = failure_errno;
 397     return NULL;
 398   }
 399 }

/* [previous][next][first][last][top][bottom][index][help] */