LCOV - code coverage report
Current view: top level - bash-4.4.23 - findcmd.c (source / functions) Hit Total Coverage
Test: cov-bash.info Lines: 89 195 45.6 %
Date: 2020-10-29 14:49:28 Functions: 10 16 62.5 %

          Line data    Source code
       1             : /* findcmd.c -- Functions to search for commands by name. */
       2             : 
       3             : /* Copyright (C) 1997-2015 Free Software Foundation, Inc.
       4             : 
       5             :    This file is part of GNU Bash, the Bourne Again SHell.
       6             : 
       7             :    Bash is free software: you can redistribute it and/or modify
       8             :    it under the terms of the GNU General Public License as published by
       9             :    the Free Software Foundation, either version 3 of the License, or
      10             :    (at your option) any later version.
      11             : 
      12             :    Bash is distributed in the hope that it will be useful,
      13             :    but WITHOUT ANY WARRANTY; without even the implied warranty of
      14             :    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      15             :    GNU General Public License for more details.
      16             : 
      17             :    You should have received a copy of the GNU General Public License
      18             :    along with Bash.  If not, see <http://www.gnu.org/licenses/>.
      19             : */
      20             : 
      21             : #include "config.h"
      22             : 
      23             : #include <stdio.h>
      24             : #include "chartypes.h"
      25             : #include "bashtypes.h"
      26             : #if !defined (_MINIX) && defined (HAVE_SYS_FILE_H)
      27             : #  include <sys/file.h>
      28             : #endif
      29             : #include "filecntl.h"
      30             : #include "posixstat.h"
      31             : 
      32             : #if defined (HAVE_UNISTD_H)
      33             : #  include <unistd.h>
      34             : #endif
      35             : #include <errno.h>
      36             : 
      37             : #include "bashansi.h"
      38             : 
      39             : #include "memalloc.h"
      40             : #include "shell.h"
      41             : #include "flags.h"
      42             : #include "hashlib.h"
      43             : #include "pathexp.h"
      44             : #include "hashcmd.h"
      45             : #include "findcmd.h"  /* matching prototypes and declarations */
      46             : 
      47             : #include <glob/strmatch.h>
      48             : 
      49             : #if !defined (errno)
      50             : extern int errno;
      51             : #endif
      52             : 
      53             : extern int posixly_correct;
      54             : extern int last_command_exit_value;
      55             : 
      56             : /* Static functions defined and used in this file. */
      57             : static char *_find_user_command_internal __P((const char *, int));
      58             : static char *find_user_command_internal __P((const char *, int));
      59             : static char *find_user_command_in_path __P((const char *, char *, int));
      60             : static char *find_in_path_element __P((const char *, char *, int, int, struct stat *));
      61             : static char *find_absolute_program __P((const char *, int));
      62             : 
      63             : static char *get_next_path_element __P((char *, int *));
      64             : 
      65             : /* The file name which we would try to execute, except that it isn't
      66             :    possible to execute it.  This is the first file that matches the
      67             :    name that we are looking for while we are searching $PATH for a
      68             :    suitable one to execute.  If we cannot find a suitable executable
      69             :    file, then we use this one. */
      70             : static char *file_to_lose_on;
      71             : 
      72             : /* Non-zero if we should stat every command found in the hash table to
      73             :    make sure it still exists. */
      74             : int check_hashed_filenames = CHECKHASH_DEFAULT;
      75             : 
      76             : /* DOT_FOUND_IN_SEARCH becomes non-zero when find_user_command ()
      77             :    encounters a `.' as the directory pathname while scanning the
      78             :    list of possible pathnames; i.e., if `.' comes before the directory
      79             :    containing the file of interest. */
      80             : int dot_found_in_search = 0;
      81             : 
      82             : /* Set up EXECIGNORE; a blacklist of patterns that executable files should not
      83             :    match. */
      84             : static struct ignorevar execignore =
      85             : {
      86             :   "EXECIGNORE",
      87             :   NULL,
      88             :   0,
      89             :   NULL,
      90             :   NULL
      91             : };
      92             : 
      93             : void
      94           0 : setup_exec_ignore (varname)
      95             :      char *varname;
      96             : {
      97           0 :   setup_ignore_patterns (&execignore);
      98           0 : }
      99             : 
     100             : static int
     101       95190 : exec_name_should_ignore (name)
     102             :      const char *name;
     103             : {
     104       95190 :   struct ign *p;
     105             : 
     106       95190 :   for (p = execignore.ignores; p && p->val; p++)
     107           0 :     if (strmatch (p->val, (char *)name, FNMATCH_EXTFLAG|FNM_CASEFOLD) != FNM_NOMATCH)
     108             :       return 1;
     109             :   return 0;
     110             : }
     111             : 
     112             : /* Return some flags based on information about this file.
     113             :    The EXISTS bit is non-zero if the file is found.
     114             :    The EXECABLE bit is non-zero the file is executble.
     115             :    Zero is returned if the file is not found. */
     116             : int
     117    26948868 : file_status (name)
     118             :      const char *name;
     119             : {
     120    26948868 :   struct stat finfo;
     121    26948868 :   int r;
     122             : 
     123             :   /* Determine whether this file exists or not. */
     124    26948868 :   if (stat (name, &finfo) < 0)
     125             :     return (0);
     126             : 
     127             :   /* If the file is a directory, then it is not "executable" in the
     128             :      sense of the shell. */
     129       97369 :   if (S_ISDIR (finfo.st_mode))
     130             :     return (FS_EXISTS|FS_DIRECTORY);
     131             : 
     132       94569 :   r = FS_EXISTS;
     133             : 
     134             : #if defined (HAVE_EACCESS)
     135             :   /* Use eaccess(2) if we have it to take things like ACLs and other
     136             :      file access mechanisms into account.  eaccess uses the effective
     137             :      user and group IDs, not the real ones.  We could use sh_eaccess,
     138             :      but we don't want any special treatment for /dev/fd. */
     139       94569 :   if (exec_name_should_ignore (name) == 0 && eaccess (name, X_OK) == 0)
     140       94190 :     r |= FS_EXECABLE;
     141       94569 :   if (eaccess (name, R_OK) == 0)
     142       94106 :     r |= FS_READABLE;
     143             : 
     144             :   return r;
     145             : #elif defined (AFS)
     146             :   /* We have to use access(2) to determine access because AFS does not
     147             :      support Unix file system semantics.  This may produce wrong
     148             :      answers for non-AFS files when ruid != euid.  I hate AFS. */
     149             :   if (exec_name_should_ignore (name) == 0 && access (name, X_OK) == 0)
     150             :     r |= FS_EXECABLE;
     151             :   if (access (name, R_OK) == 0)
     152             :     r |= FS_READABLE;
     153             : 
     154             :   return r;
     155             : #else /* !HAVE_EACCESS && !AFS */
     156             : 
     157             :   /* Find out if the file is actually executable.  By definition, the
     158             :      only other criteria is that the file has an execute bit set that
     159             :      we can use.  The same with whether or not a file is readable. */
     160             : 
     161             :   /* Root only requires execute permission for any of owner, group or
     162             :      others to be able to exec a file, and can read any file. */
     163             :   if (current_user.euid == (uid_t)0)
     164             :     {
     165             :       r |= FS_READABLE;
     166             :       if (exec_name_should_ignore (name) == 0 && (finfo.st_mode & S_IXUGO))
     167             :         r |= FS_EXECABLE;
     168             :       return r;
     169             :     }
     170             : 
     171             :   /* If we are the owner of the file, the owner bits apply. */
     172             :   if (current_user.euid == finfo.st_uid)
     173             :     {
     174             :       if (exec_name_should_ignore (name) == 0 && (finfo.st_mode & S_IXUSR))
     175             :         r |= FS_EXECABLE;
     176             :       if (finfo.st_mode & S_IRUSR)
     177             :         r |= FS_READABLE;
     178             :     }
     179             : 
     180             :   /* If we are in the owning group, the group permissions apply. */
     181             :   else if (group_member (finfo.st_gid))
     182             :     {
     183             :       if (exec_name_should_ignore (name) == 0 && (finfo.st_mode & S_IXGRP))
     184             :         r |= FS_EXECABLE;
     185             :       if (finfo.st_mode & S_IRGRP)
     186             :         r |= FS_READABLE;
     187             :     }
     188             : 
     189             :   /* Else we check whether `others' have permission to execute the file */
     190             :   else
     191             :     {
     192             :       if (exec_name_should_ignore (name) == 0 && finfo.st_mode & S_IXOTH)
     193             :         r |= FS_EXECABLE;
     194             :       if (finfo.st_mode & S_IROTH)
     195             :         r |= FS_READABLE;
     196             :     }
     197             : 
     198             :   return r;
     199             : #endif /* !AFS */
     200             : }
     201             : 
     202             : /* Return non-zero if FILE exists and is executable.
     203             :    Note that this function is the definition of what an
     204             :    executable file is; do not change this unless YOU know
     205             :    what an executable file is. */
     206             : int
     207      151257 : executable_file (file)
     208             :      const char *file;
     209             : {
     210      151257 :   int s;
     211             : 
     212      151257 :   s = file_status (file);
     213             : #if defined (EISDIR)
     214      151257 :   if (s & FS_DIRECTORY)
     215           0 :     errno = EISDIR;     /* let's see if we can improve error messages */
     216             : #endif
     217      151257 :   return ((s & FS_EXECABLE) && ((s & FS_DIRECTORY) == 0));
     218             : }
     219             : 
     220             : int
     221           0 : is_directory (file)
     222             :      const char *file;
     223             : {
     224           0 :   return (file_status (file) & FS_DIRECTORY);
     225             : }
     226             : 
     227             : int
     228           0 : executable_or_directory (file)
     229             :      const char *file;
     230             : {
     231           0 :   int s;
     232             : 
     233           0 :   s = file_status (file);
     234           0 :   return ((s & FS_EXECABLE) || (s & FS_DIRECTORY));
     235             : }
     236             : 
     237             : /* Locate the executable file referenced by NAME, searching along
     238             :    the contents of the shell PATH variable.  Return a new string
     239             :    which is the full pathname to the file, or NULL if the file
     240             :    couldn't be found.  If a file is found that isn't executable,
     241             :    and that is the only match, then return that. */
     242             : char *
     243         131 : find_user_command (name)
     244             :      const char *name;
     245             : {
     246         262 :   return (find_user_command_internal (name, FS_EXEC_PREFERRED|FS_NODIRS));
     247             : }
     248             : 
     249             : /* Locate the file referenced by NAME, searching along the contents
     250             :    of the shell PATH variable.  Return a new string which is the full
     251             :    pathname to the file, or NULL if the file couldn't be found.  This
     252             :    returns the first readable file found; designed to be used to look
     253             :    for shell scripts or files to source. */
     254             : char *
     255       12498 : find_path_file (name)
     256             :      const char *name;
     257             : {
     258       24996 :   return (find_user_command_internal (name, FS_READABLE));
     259             : }
     260             : 
     261             : static char *
     262       12629 : _find_user_command_internal (name, flags)
     263             :      const char *name;
     264             :      int flags;
     265             : {
     266       12629 :   char *path_list, *cmd;
     267       12629 :   SHELL_VAR *var;
     268             : 
     269             :   /* Search for the value of PATH in both the temporary environments and
     270             :      in the regular list of variables. */
     271       12629 :   if (var = find_variable_tempenv ("PATH"))   /* XXX could be array? */
     272       12629 :     path_list = value_cell (var);
     273             :   else
     274             :     path_list = (char *)NULL;
     275             : 
     276       12629 :   if (path_list == 0 || *path_list == '\0')
     277           0 :     return (savestring (name));
     278             : 
     279       12629 :   cmd = find_user_command_in_path (name, path_list, flags);
     280             : 
     281       12629 :   return (cmd);
     282             : }
     283             : 
     284             : static char *
     285             : find_user_command_internal (name, flags)
     286             :      const char *name;
     287             :      int flags;
     288             : {
     289             : #ifdef __WIN32__
     290             :   char *res, *dotexe;
     291             : 
     292             :   dotexe = (char *)xmalloc (strlen (name) + 5);
     293             :   strcpy (dotexe, name);
     294             :   strcat (dotexe, ".exe");
     295             :   res = _find_user_command_internal (dotexe, flags);
     296             :   free (dotexe);
     297             :   if (res == 0)
     298             :     res = _find_user_command_internal (name, flags);
     299             :   return res;
     300             : #else
     301       12629 :   return (_find_user_command_internal (name, flags));
     302             : #endif
     303             : }
     304             : 
     305             : /* Return the next element from PATH_LIST, a colon separated list of
     306             :    paths.  PATH_INDEX_POINTER is the address of an index into PATH_LIST;
     307             :    the index is modified by this function.
     308             :    Return the next element of PATH_LIST or NULL if there are no more. */
     309             : static char *
     310    26738014 : get_next_path_element (path_list, path_index_pointer)
     311             :      char *path_list;
     312             :      int *path_index_pointer;
     313             : {
     314    26738014 :   char *path;
     315             : 
     316    26738014 :   path = extract_colon_unit (path_list, path_index_pointer);
     317             : 
     318    26738014 :   if (path == 0)
     319             :     return (path);
     320             : 
     321    26738014 :   if (*path == '\0')
     322             :     {
     323           0 :       free (path);
     324           0 :       path = savestring (".");
     325             :     }
     326             : 
     327             :   return (path);
     328             : }
     329             : 
     330             : /* Look for PATHNAME in $PATH.  Returns either the hashed command
     331             :    corresponding to PATHNAME or the first instance of PATHNAME found
     332             :    in $PATH.  If (FLAGS&CMDSRCH_HASH) is non-zero, insert the instance of
     333             :    PATHNAME found in $PATH into the command hash table.  If (FLAGS&CMDSRCH_STDPATH)
     334             :    is non-zero, we are running in a `command -p' environment and should use
     335             :    the Posix standard path.
     336             :    Returns a newly-allocated string. */
     337             : char *
     338     7040326 : search_for_command (pathname, flags)
     339             :      const char *pathname;
     340             :      int flags;
     341             : {
     342     7040326 :   char *hashed_file, *command, *pathlist;
     343     7040326 :   int temp_path, st;
     344     7040326 :   SHELL_VAR *path;
     345             : 
     346     7040326 :   hashed_file = command = (char *)NULL;
     347             : 
     348             :   /* If PATH is in the temporary environment for this command, don't use the
     349             :      hash table to search for the full pathname. */
     350     7040326 :   path = find_variable_tempenv ("PATH");
     351     7040326 :   temp_path = path && tempvar_p (path);
     352             : 
     353             :   /* Don't waste time trying to find hashed data for a pathname
     354             :      that is already completely specified or if we're using a command-
     355             :      specific value for PATH. */
     356     7040326 :   if (temp_path == 0 && absolute_program (pathname) == 0)
     357     5374899 :     hashed_file = phash_search (pathname);
     358             : 
     359             :   /* If a command found in the hash table no longer exists, we need to
     360             :      look for it in $PATH.  Thank you Posix.2.  This forces us to stat
     361             :      every command found in the hash table. */
     362             : 
     363     5374899 :   if (hashed_file && (posixly_correct || check_hashed_filenames))
     364             :     {
     365           0 :       st = file_status (hashed_file);
     366           0 :       if ((st & (FS_EXISTS|FS_EXECABLE)) != (FS_EXISTS|FS_EXECABLE))
     367             :         {
     368           0 :           phash_remove (pathname);
     369           0 :           free (hashed_file);
     370           0 :           hashed_file = (char *)NULL;
     371             :         }
     372             :     }
     373             : 
     374     7040326 :   if (hashed_file)
     375             :     command = hashed_file;
     376     7040234 :   else if (absolute_program (pathname))
     377             :     /* A command containing a slash is not looked up in PATH or saved in
     378             :        the hash table. */
     379     1665427 :     command = savestring (pathname);
     380             :   else
     381             :     {
     382     5374807 :       if (flags & CMDSRCH_STDPATH)
     383           0 :         pathlist = conf_standard_path ();
     384     5374807 :       else if (temp_path || path)
     385     5374807 :         pathlist = value_cell (path);
     386             :       else
     387             :         pathlist = 0;
     388             : 
     389     5374807 :       command = find_user_command_in_path (pathname, pathlist, FS_EXEC_PREFERRED|FS_NODIRS);
     390             : 
     391     5374807 :       if (command && hashing_enabled && temp_path == 0 && (flags & CMDSRCH_HASH))
     392             :         {
     393             :           /* If we found the full pathname the same as the command name, the
     394             :              command probably doesn't exist.  Don't put it into the hash
     395             :              table. */
     396       47129 :           if (STREQ (command, pathname))
     397             :             {
     398           0 :               st = file_status (command);
     399           0 :               if (st & FS_EXECABLE)
     400           0 :                 phash_insert ((char *)pathname, command, dot_found_in_search, 1);
     401             :             }
     402             :           else
     403       47129 :             phash_insert ((char *)pathname, command, dot_found_in_search, 1);
     404             :         }
     405             : 
     406     5374807 :       if (flags & CMDSRCH_STDPATH)
     407           0 :         free (pathlist);
     408             :     }
     409             : 
     410     7040326 :   return (command);
     411             : }
     412             : 
     413             : char *
     414           0 : user_command_matches (name, flags, state)
     415             :      const char *name;
     416             :      int flags, state;
     417             : {
     418           0 :   register int i;
     419           0 :   int  path_index, name_len;
     420           0 :   char *path_list, *path_element, *match;
     421           0 :   struct stat dotinfo;
     422           0 :   static char **match_list = NULL;
     423           0 :   static int match_list_size = 0;
     424           0 :   static int match_index = 0;
     425             : 
     426           0 :   if (state == 0)
     427             :     {
     428             :       /* Create the list of matches. */
     429           0 :       if (match_list == 0)
     430             :         {
     431           0 :           match_list_size = 5;
     432           0 :           match_list = strvec_create (match_list_size);
     433             :         }
     434             : 
     435             :       /* Clear out the old match list. */
     436           0 :       for (i = 0; i < match_list_size; i++)
     437           0 :         match_list[i] = 0;
     438             : 
     439             :       /* We haven't found any files yet. */
     440           0 :       match_index = 0;
     441             : 
     442           0 :       if (absolute_program (name))
     443             :         {
     444           0 :           match_list[0] = find_absolute_program (name, flags);
     445           0 :           match_list[1] = (char *)NULL;
     446           0 :           path_list = (char *)NULL;
     447             :         }
     448             :       else
     449             :         {
     450           0 :           name_len = strlen (name);
     451           0 :           file_to_lose_on = (char *)NULL;
     452           0 :           dot_found_in_search = 0;
     453           0 :           if (stat (".", &dotinfo) < 0)
     454           0 :             dotinfo.st_dev = dotinfo.st_ino = 0;        /* so same_file won't match */
     455           0 :           path_list = get_string_value ("PATH");
     456           0 :           path_index = 0;
     457             :         }
     458             : 
     459           0 :       while (path_list && path_list[path_index])
     460             :         {
     461           0 :           path_element = get_next_path_element (path_list, &path_index);
     462             : 
     463           0 :           if (path_element == 0)
     464             :             break;
     465             : 
     466           0 :           match = find_in_path_element (name, path_element, flags, name_len, &dotinfo);
     467             : 
     468           0 :           free (path_element);
     469             : 
     470           0 :           if (match == 0)
     471             :             continue;
     472             : 
     473           0 :           if (match_index + 1 == match_list_size)
     474             :             {
     475           0 :               match_list_size += 10;
     476           0 :               match_list = strvec_resize (match_list, (match_list_size + 1));
     477             :             }
     478             : 
     479           0 :           match_list[match_index++] = match;
     480           0 :           match_list[match_index] = (char *)NULL;
     481           0 :           FREE (file_to_lose_on);
     482           0 :           file_to_lose_on = (char *)NULL;
     483             :         }
     484             : 
     485             :       /* We haven't returned any strings yet. */
     486           0 :       match_index = 0;
     487             :     }
     488             : 
     489           0 :   match = match_list[match_index];
     490             : 
     491           0 :   if (match)
     492           0 :     match_index++;
     493             : 
     494           0 :   return (match);
     495             : }
     496             : 
     497             : static char *
     498       11881 : find_absolute_program (name, flags)
     499             :      const char *name;
     500             :      int flags;
     501             : {
     502       11881 :   int st;
     503             : 
     504       11881 :   st = file_status (name);
     505             : 
     506             :   /* If the file doesn't exist, quit now. */
     507       11881 :   if ((st & FS_EXISTS) == 0)
     508             :     return ((char *)NULL);
     509             : 
     510             :   /* If we only care about whether the file exists or not, return
     511             :      this filename.  Otherwise, maybe we care about whether this
     512             :      file is executable.  If it is, and that is what we want, return it. */
     513           0 :   if ((flags & FS_EXISTS) || ((flags & FS_EXEC_ONLY) && (st & FS_EXECABLE)))
     514           0 :     return (savestring (name));
     515             : 
     516             :   return (NULL);
     517             : }
     518             : 
     519             : static char *
     520           0 : find_in_path_element (name, path, flags, name_len, dotinfop)
     521             :      const char *name;
     522             :      char *path;
     523             :      int flags, name_len;
     524             :      struct stat *dotinfop;
     525             : {
     526           0 :   int status;
     527           0 :   char *full_path, *xpath;
     528             : 
     529           0 :   xpath = (posixly_correct == 0 && *path == '~') ? bash_tilde_expand (path, 0) : path;
     530             : 
     531             :   /* Remember the location of "." in the path, in all its forms
     532             :      (as long as they begin with a `.', e.g. `./.') */
     533           0 :   if (dot_found_in_search == 0 && *xpath == '.')
     534           0 :     dot_found_in_search = same_file (".", xpath, dotinfop, (struct stat *)NULL);
     535             : 
     536           0 :   full_path = sh_makepath (xpath, name, 0);
     537             : 
     538           0 :   status = file_status (full_path);
     539             : 
     540           0 :   if (xpath != path)
     541           0 :     free (xpath);
     542             : 
     543           0 :   if ((status & FS_EXISTS) == 0)
     544             :     {
     545           0 :       free (full_path);
     546           0 :       return ((char *)NULL);
     547             :     }
     548             : 
     549             :   /* The file exists.  If the caller simply wants the first file, here it is. */
     550           0 :   if (flags & FS_EXISTS)
     551           0 :     return (full_path);
     552             : 
     553             :   /* If we have a readable file, and the caller wants a readable file, this
     554             :      is it. */
     555           0 :   if ((flags & FS_READABLE) && (status & FS_READABLE))
     556           0 :     return (full_path);
     557             : 
     558             :   /* If the file is executable, then it satisfies the cases of
     559             :       EXEC_ONLY and EXEC_PREFERRED.  Return this file unconditionally. */
     560           0 :   if ((status & FS_EXECABLE) && (flags & (FS_EXEC_ONLY|FS_EXEC_PREFERRED)) &&
     561           0 :       (((flags & FS_NODIRS) == 0) || ((status & FS_DIRECTORY) == 0)))
     562             :     {
     563           0 :       FREE (file_to_lose_on);
     564           0 :       file_to_lose_on = (char *)NULL;
     565           0 :       return (full_path);
     566             :     }
     567             : 
     568             :   /* The file is not executable, but it does exist.  If we prefer
     569             :      an executable, then remember this one if it is the first one
     570             :      we have found. */
     571           0 :   if ((flags & FS_EXEC_PREFERRED) && file_to_lose_on == 0 && exec_name_should_ignore (full_path) == 0)
     572           0 :     file_to_lose_on = savestring (full_path);
     573             : 
     574             :   /* If we want only executable files, or we don't want directories and
     575             :      this file is a directory, or we want a readable file and this file
     576             :      isn't readable, fail. */
     577           0 :   if ((flags & (FS_EXEC_ONLY|FS_EXEC_PREFERRED)) ||
     578           0 :       ((flags & FS_NODIRS) && (status & FS_DIRECTORY)) ||
     579           0 :       ((flags & FS_READABLE) && (status & FS_READABLE) == 0))
     580             :     {
     581           0 :       free (full_path);
     582           0 :       return ((char *)NULL);
     583             :     }
     584             :   else
     585           0 :     return (full_path);
     586             : }
     587             : 
     588             : /* This does the dirty work for find_user_command_internal () and
     589             :    user_command_matches ().
     590             :    NAME is the name of the file to search for.
     591             :    PATH_LIST is a colon separated list of directories to search.
     592             :    FLAGS contains bit fields which control the files which are eligible.
     593             :    Some values are:
     594             :       FS_EXEC_ONLY:             The file must be an executable to be found.
     595             :       FS_EXEC_PREFERRED:        If we can't find an executable, then the
     596             :                                 the first file matching NAME will do.
     597             :       FS_EXISTS:                The first file found will do.
     598             :       FS_NODIRS:                Don't find any directories.
     599             : */
     600             : static char *
     601     5387436 : find_user_command_in_path (name, path_list, flags)
     602             :      const char *name;
     603             :      char *path_list;
     604             :      int flags;
     605             : {
     606     5387436 :   char *full_path, *path;
     607     5387436 :   int path_index, name_len;
     608     5387436 :   struct stat dotinfo;
     609             : 
     610             :   /* We haven't started looking, so we certainly haven't seen
     611             :      a `.' as the directory path yet. */
     612     5387436 :   dot_found_in_search = 0;
     613             : 
     614     5387436 :   if (absolute_program (name))
     615             :     {
     616       11881 :       full_path = find_absolute_program (name, flags);
     617       11881 :       return (full_path);
     618             :     }
     619             : 
     620     5375555 :   if (path_list == 0 || *path_list == '\0')
     621           0 :     return (savestring (name));         /* XXX */
     622             : 
     623     5375555 :   file_to_lose_on = (char *)NULL;
     624     5375555 :   name_len = strlen (name);
     625     5375555 :   if (stat (".", &dotinfo) < 0)
     626           0 :     dotinfo.st_dev = dotinfo.st_ino = 0;
     627     5375555 :   path_index = 0;
     628             : 
     629    32066474 :   while (path_list[path_index])
     630             :     {
     631             :       /* Allow the user to interrupt out of a lengthy path search. */
     632    26738014 :       QUIT;
     633             : 
     634    26738014 :       path = get_next_path_element (path_list, &path_index);
     635    26738014 :       if (path == 0)
     636             :         break;
     637             : 
     638             :       /* Side effects: sets dot_found_in_search, possibly sets
     639             :          file_to_lose_on. */
     640    26738014 :       full_path = find_in_path_element (name, path, flags, name_len, &dotinfo);
     641    26738014 :       free (path);
     642             : 
     643             :       /* This should really be in find_in_path_element, but there isn't the
     644             :          right combination of flags. */
     645    26738014 :       if (full_path && is_directory (full_path))
     646             :         {
     647           0 :           free (full_path);
     648           0 :           continue;
     649             :         }
     650             : 
     651    26738014 :       if (full_path)
     652             :         {
     653       47095 :           FREE (file_to_lose_on);
     654       47095 :           return (full_path);
     655             :         }
     656             :     }
     657             : 
     658             :   /* We didn't find exactly what the user was looking for.  Return
     659             :      the contents of FILE_TO_LOSE_ON which is NULL when the search
     660             :      required an executable, or non-NULL if a file was found and the
     661             :      search would accept a non-executable as a last resort.  If the
     662             :      caller specified FS_NODIRS, and file_to_lose_on is a directory,
     663             :      return NULL. */
     664     5328460 :   if (file_to_lose_on && (flags & FS_NODIRS) && is_directory (file_to_lose_on))
     665             :     {
     666         560 :       free (file_to_lose_on);
     667         560 :       file_to_lose_on = (char *)NULL;
     668             :     }
     669             : 
     670     5328460 :   return (file_to_lose_on);
     671             : }
     672             : 
     673             : /* External interface to find a command given a $PATH.  Separate from
     674             :    find_user_command_in_path to allow future customization. */
     675             : char *
     676           0 : find_in_path (name, path_list, flags)
     677             :      const char *name;
     678             :      char *path_list;
     679             :      int flags;
     680             : {
     681           0 :   return (find_user_command_in_path (name, path_list, flags));
     682             : }

Generated by: LCOV version 1.14.0.6.4058