1 /* findcmd.c -- Functions to search for commands by name. */
3 /* Copyright (C) 1997-2020 Free Software Foundation, Inc.
5 This file is part of GNU Bush, the Bourne Again SHell.
7 Bush 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.
12 Bush 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.
17 You should have received a copy of the GNU General Public License
18 along with Bush. If not, see <http://www.gnu.org/licenses/>.
24 #include "chartypes.h"
25 #include "bushtypes.h"
26 #if !defined (_MINIX) && defined (HAVE_SYS_FILE_H)
27 # include <sys/file.h>
30 #include "posixstat.h"
32 #if defined (HAVE_UNISTD_H)
41 #include "runner/execute_cmd.h"
44 #include "impl/pathexp.h"
46 #include "impl/findcmd.h" /* matching prototypes and declarations */
48 #include <glob/strmatch.h>
54 /* Static functions defined and used in this file. */
55 static char *_find_user_command_internal
PARAMS((const char *, int));
56 static char *find_user_command_internal
PARAMS((const char *, int));
57 static char *find_user_command_in_path
PARAMS((const char *, char *, int));
58 static char *find_in_path_element
PARAMS((const char *, char *, int, int, struct stat
*));
59 static char *find_absolute_program
PARAMS((const char *, int));
61 static char *get_next_path_element
PARAMS((char *, int *));
63 /* The file name which we would try to execute, except that it isn't
64 possible to execute it. This is the first file that matches the
65 name that we are looking for while we are searching $PATH for a
66 suitable one to execute. If we cannot find a suitable executable
67 file, then we use this one. */
68 static char *file_to_lose_on
;
70 /* Non-zero if we should stat every command found in the hash table to
71 make sure it still exists. */
72 int check_hashed_filenames
= CHECKHASH_DEFAULT
;
74 /* DOT_FOUND_IN_SEARCH becomes non-zero when find_user_command ()
75 encounters a `.' as the directory pathname while scanning the
76 list of possible pathnames; i.e., if `.' comes before the directory
77 containing the file of interest. */
78 int dot_found_in_search
= 0;
80 /* Set up EXECIGNORE; a blacklist of patterns that executable files should not
82 static struct ignorevar execignore
=
92 setup_exec_ignore (varname
)
95 setup_ignore_patterns (&execignore
);
99 exec_name_should_ignore (name
)
104 for (p
= execignore
.ignores
; p
&& p
->val
; p
++)
105 if (strmatch (p
->val
, (char *)name
, FNMATCH_EXTFLAG
|FNM_CASEFOLD
) != FNM_NOMATCH
)
110 /* Return some flags based on information about this file.
111 The EXISTS bit is non-zero if the file is found.
112 The EXECABLE bit is non-zero the file is executble.
113 Zero is returned if the file is not found. */
121 /* Determine whether this file exists or not. */
122 if (stat (name
, &finfo
) < 0)
125 /* If the file is a directory, then it is not "executable" in the
126 sense of the shell. */
127 if (S_ISDIR (finfo
.st_mode
))
128 return (FS_EXISTS
|FS_DIRECTORY
);
132 #if defined (HAVE_EACCESS)
133 /* Use eaccess(2) if we have it to take things like ACLs and other
134 file access mechanisms into account. eaccess uses the effective
135 user and group IDs, not the real ones. We could use sh_eaccess,
136 but we don't want any special treatment for /dev/fd. */
137 if (exec_name_should_ignore (name
) == 0 && eaccess (name
, X_OK
) == 0)
139 if (eaccess (name
, R_OK
) == 0)
144 /* We have to use access(2) to determine access because AFS does not
145 support Unix file system semantics. This may produce wrong
146 answers for non-AFS files when ruid != euid. I hate AFS. */
147 if (exec_name_should_ignore (name
) == 0 && access (name
, X_OK
) == 0)
149 if (access (name
, R_OK
) == 0)
153 #else /* !HAVE_EACCESS && !AFS */
155 /* Find out if the file is actually executable. By definition, the
156 only other criteria is that the file has an execute bit set that
157 we can use. The same with whether or not a file is readable. */
159 /* Root only requires execute permission for any of owner, group or
160 others to be able to exec a file, and can read any file. */
161 if (current_user
.euid
== (uid_t
)0)
164 if (exec_name_should_ignore (name
) == 0 && (finfo
.st_mode
& S_IXUGO
))
169 /* If we are the owner of the file, the owner bits apply. */
170 if (current_user
.euid
== finfo
.st_uid
)
172 if (exec_name_should_ignore (name
) == 0 && (finfo
.st_mode
& S_IXUSR
))
174 if (finfo
.st_mode
& S_IRUSR
)
178 /* If we are in the owning group, the group permissions apply. */
179 else if (group_member (finfo
.st_gid
))
181 if (exec_name_should_ignore (name
) == 0 && (finfo
.st_mode
& S_IXGRP
))
183 if (finfo
.st_mode
& S_IRGRP
)
187 /* Else we check whether `others' have permission to execute the file */
190 if (exec_name_should_ignore (name
) == 0 && finfo
.st_mode
& S_IXOTH
)
192 if (finfo
.st_mode
& S_IROTH
)
200 /* Return non-zero if FILE exists and is executable.
201 Note that this function is the definition of what an
202 executable file is; do not change this unless YOU know
203 what an executable file is. */
205 executable_file (file
)
210 s
= file_status (file
);
212 if (s
& FS_DIRECTORY
)
213 errno
= EISDIR
; /* let's see if we can improve error messages */
215 return ((s
& FS_EXECABLE
) && ((s
& FS_DIRECTORY
) == 0));
222 return (file_status (file
) & FS_DIRECTORY
);
226 executable_or_directory (file
)
231 s
= file_status (file
);
232 return ((s
& FS_EXECABLE
) || (s
& FS_DIRECTORY
));
235 /* Locate the executable file referenced by NAME, searching along
236 the contents of the shell PATH variable. Return a new string
237 which is the full pathname to the file, or NULL if the file
238 couldn't be found. If a file is found that isn't executable,
239 and that is the only match, then return that. */
241 find_user_command (name
)
244 return (find_user_command_internal (name
, FS_EXEC_PREFERRED
|FS_NODIRS
));
247 /* Locate the file referenced by NAME, searching along the contents
248 of the shell PATH variable. Return a new string which is the full
249 pathname to the file, or NULL if the file couldn't be found. This
250 returns the first readable file found; designed to be used to look
251 for shell scripts or files to source. */
253 find_path_file (name
)
256 return (find_user_command_internal (name
, FS_READABLE
));
260 _find_user_command_internal (name
, flags
)
264 char *path_list
, *cmd
;
267 /* Search for the value of PATH in both the temporary environments and
268 in the regular list of variables. */
269 if (var
= find_variable_tempenv ("PATH")) /* XXX could be array? */
270 path_list
= value_cell (var
);
272 path_list
= (char *)NULL
;
274 if (path_list
== 0 || *path_list
== '\0')
275 return (savestring (name
));
277 cmd
= find_user_command_in_path (name
, path_list
, flags
);
283 find_user_command_internal (name
, flags
)
290 dotexe
= (char *)xmalloc (strlen (name
) + 5);
291 strcpy (dotexe
, name
);
292 strcat (dotexe
, ".exe");
293 res
= _find_user_command_internal (dotexe
, flags
);
296 res
= _find_user_command_internal (name
, flags
);
299 return (_find_user_command_internal (name
, flags
));
303 /* Return the next element from PATH_LIST, a colon separated list of
304 paths. PATH_INDEX_POINTER is the address of an index into PATH_LIST;
305 the index is modified by this function.
306 Return the next element of PATH_LIST or NULL if there are no more. */
308 get_next_path_element (path_list
, path_index_pointer
)
310 int *path_index_pointer
;
314 path
= extract_colon_unit (path_list
, path_index_pointer
);
322 path
= savestring (".");
328 /* Look for PATHNAME in $PATH. Returns either the hashed command
329 corresponding to PATHNAME or the first instance of PATHNAME found
330 in $PATH. If (FLAGS&CMDSRCH_HASH) is non-zero, insert the instance of
331 PATHNAME found in $PATH into the command hash table. If (FLAGS&CMDSRCH_STDPATH)
332 is non-zero, we are running in a `command -p' environment and should use
333 the Posix standard path.
334 Returns a newly-allocated string. */
336 search_for_command (pathname
, flags
)
337 const char *pathname
;
340 char *hashed_file
, *command
, *path_list
;
344 hashed_file
= command
= (char *)NULL
;
346 /* If PATH is in the temporary environment for this command, don't use the
347 hash table to search for the full pathname. */
348 path
= find_variable_tempenv ("PATH");
349 temp_path
= path
&& tempvar_p (path
);
351 /* Don't waste time trying to find hashed data for a pathname
352 that is already completely specified or if we're using a command-
353 specific value for PATH. */
354 if (temp_path
== 0 && absolute_program (pathname
) == 0)
355 hashed_file
= phash_search (pathname
);
357 /* If a command found in the hash table no longer exists, we need to
358 look for it in $PATH. Thank you Posix.2. This forces us to stat
359 every command found in the hash table. */
361 if (hashed_file
&& (posixly_correct
|| check_hashed_filenames
))
363 st
= file_status (hashed_file
);
364 if ((st
& (FS_EXISTS
|FS_EXECABLE
)) != (FS_EXISTS
|FS_EXECABLE
))
366 phash_remove (pathname
);
368 hashed_file
= (char *)NULL
;
373 command
= hashed_file
;
374 else if (absolute_program (pathname
))
375 /* A command containing a slash is not looked up in PATH or saved in
377 command
= savestring (pathname
);
380 if (flags
& CMDSRCH_STDPATH
)
381 path_list
= conf_standard_path ();
382 else if (temp_path
|| path
)
383 path_list
= value_cell (path
);
387 command
= find_user_command_in_path (pathname
, path_list
, FS_EXEC_PREFERRED
|FS_NODIRS
);
389 if (command
&& hashing_enabled
&& temp_path
== 0 && (flags
& CMDSRCH_HASH
))
391 /* If we found the full pathname the same as the command name, the
392 command probably doesn't exist. Don't put it into the hash
394 if (STREQ (command
, pathname
))
396 st
= file_status (command
);
397 if (st
& FS_EXECABLE
)
398 phash_insert ((char *)pathname
, command
, dot_found_in_search
, 1);
400 /* If we're in posix mode, don't add files without the execute bit
401 to the hash table. */
402 else if (posixly_correct
)
404 st
= file_status (command
);
405 if (st
& FS_EXECABLE
)
406 phash_insert ((char *)pathname
, command
, dot_found_in_search
, 1);
409 phash_insert ((char *)pathname
, command
, dot_found_in_search
, 1);
412 if (flags
& CMDSRCH_STDPATH
)
420 user_command_matches (name
, flags
, state
)
425 int path_index
, name_len
;
426 char *path_list
, *path_element
, *match
;
428 static char **match_list
= NULL
;
429 static int match_list_size
= 0;
430 static int match_index
= 0;
434 /* Create the list of matches. */
438 match_list
= strvec_create (match_list_size
);
441 /* Clear out the old match list. */
442 for (i
= 0; i
< match_list_size
; i
++)
445 /* We haven't found any files yet. */
448 if (absolute_program (name
))
450 match_list
[0] = find_absolute_program (name
, flags
);
451 match_list
[1] = (char *)NULL
;
452 path_list
= (char *)NULL
;
456 name_len
= strlen (name
);
457 file_to_lose_on
= (char *)NULL
;
458 dot_found_in_search
= 0;
459 if (stat (".", &dotinfo
) < 0)
460 dotinfo
.st_dev
= dotinfo
.st_ino
= 0; /* so same_file won't match */
461 path_list
= get_string_value ("PATH");
465 while (path_list
&& path_list
[path_index
])
467 path_element
= get_next_path_element (path_list
, &path_index
);
469 if (path_element
== 0)
472 match
= find_in_path_element (name
, path_element
, flags
, name_len
, &dotinfo
);
479 if (match_index
+ 1 == match_list_size
)
481 match_list_size
+= 10;
482 match_list
= strvec_resize (match_list
, (match_list_size
+ 1));
485 match_list
[match_index
++] = match
;
486 match_list
[match_index
] = (char *)NULL
;
487 FREE (file_to_lose_on
);
488 file_to_lose_on
= (char *)NULL
;
491 /* We haven't returned any strings yet. */
495 match
= match_list
[match_index
];
504 find_absolute_program (name
, flags
)
510 st
= file_status (name
);
512 /* If the file doesn't exist, quit now. */
513 if ((st
& FS_EXISTS
) == 0)
514 return ((char *)NULL
);
516 /* If we only care about whether the file exists or not, return
517 this filename. Otherwise, maybe we care about whether this
518 file is executable. If it is, and that is what we want, return it. */
519 if ((flags
& FS_EXISTS
) || ((flags
& FS_EXEC_ONLY
) && (st
& FS_EXECABLE
)))
520 return (savestring (name
));
526 find_in_path_element (name
, path
, flags
, name_len
, dotinfop
)
530 struct stat
*dotinfop
;
533 char *full_path
, *xpath
;
535 xpath
= (posixly_correct
== 0 && *path
== '~') ? bush_tilde_expand (path
, 0) : path
;
537 /* Remember the location of "." in the path, in all its forms
538 (as long as they begin with a `.', e.g. `./.') */
539 if (dot_found_in_search
== 0 && *xpath
== '.')
540 dot_found_in_search
= same_file (".", xpath
, dotinfop
, (struct stat
*)NULL
);
542 full_path
= sh_makepath (xpath
, name
, 0);
544 status
= file_status (full_path
);
549 if ((status
& FS_EXISTS
) == 0)
552 return ((char *)NULL
);
555 /* The file exists. If the caller simply wants the first file, here it is. */
556 if (flags
& FS_EXISTS
)
559 /* If we have a readable file, and the caller wants a readable file, this
561 if ((flags
& FS_READABLE
) && (status
& FS_READABLE
))
564 /* If the file is executable, then it satisfies the cases of
565 EXEC_ONLY and EXEC_PREFERRED. Return this file unconditionally. */
566 if ((status
& FS_EXECABLE
) && (flags
& (FS_EXEC_ONLY
|FS_EXEC_PREFERRED
)) &&
567 (((flags
& FS_NODIRS
) == 0) || ((status
& FS_DIRECTORY
) == 0)))
569 FREE (file_to_lose_on
);
570 file_to_lose_on
= (char *)NULL
;
574 /* The file is not executable, but it does exist. If we prefer
575 an executable, then remember this one if it is the first one
577 if ((flags
& FS_EXEC_PREFERRED
) && file_to_lose_on
== 0 && exec_name_should_ignore (full_path
) == 0)
578 file_to_lose_on
= savestring (full_path
);
580 /* If we want only executable files, or we don't want directories and
581 this file is a directory, or we want a readable file and this file
582 isn't readable, fail. */
583 if ((flags
& (FS_EXEC_ONLY
|FS_EXEC_PREFERRED
)) ||
584 ((flags
& FS_NODIRS
) && (status
& FS_DIRECTORY
)) ||
585 ((flags
& FS_READABLE
) && (status
& FS_READABLE
) == 0))
588 return ((char *)NULL
);
594 /* This does the dirty work for find_user_command_internal () and
595 user_command_matches ().
596 NAME is the name of the file to search for.
597 PATH_LIST is a colon separated list of directories to search.
598 FLAGS contains bit fields which control the files which are eligible.
600 FS_EXEC_ONLY: The file must be an executable to be found.
601 FS_EXEC_PREFERRED: If we can't find an executable, then the
602 the first file matching NAME will do.
603 FS_EXISTS: The first file found will do.
604 FS_NODIRS: Don't find any directories.
607 find_user_command_in_path (name
, path_list
, flags
)
612 char *full_path
, *path
;
613 int path_index
, name_len
;
616 /* We haven't started looking, so we certainly haven't seen
617 a `.' as the directory path yet. */
618 dot_found_in_search
= 0;
620 if (absolute_program (name
))
622 full_path
= find_absolute_program (name
, flags
);
626 if (path_list
== 0 || *path_list
== '\0')
627 return (savestring (name
)); /* XXX */
629 file_to_lose_on
= (char *)NULL
;
630 name_len
= strlen (name
);
631 if (stat (".", &dotinfo
) < 0)
632 dotinfo
.st_dev
= dotinfo
.st_ino
= 0;
635 while (path_list
[path_index
])
637 /* Allow the user to interrupt out of a lengthy path search. */
640 path
= get_next_path_element (path_list
, &path_index
);
644 /* Side effects: sets dot_found_in_search, possibly sets
646 full_path
= find_in_path_element (name
, path
, flags
, name_len
, &dotinfo
);
649 /* This should really be in find_in_path_element, but there isn't the
650 right combination of flags. */
651 if (full_path
&& is_directory (full_path
))
659 FREE (file_to_lose_on
);
664 /* We didn't find exactly what the user was looking for. Return
665 the contents of FILE_TO_LOSE_ON which is NULL when the search
666 required an executable, or non-NULL if a file was found and the
667 search would accept a non-executable as a last resort. If the
668 caller specified FS_NODIRS, and file_to_lose_on is a directory,
670 if (file_to_lose_on
&& (flags
& FS_NODIRS
) && is_directory (file_to_lose_on
))
672 free (file_to_lose_on
);
673 file_to_lose_on
= (char *)NULL
;
676 return (file_to_lose_on
);
679 /* External interface to find a command given a $PATH. Separate from
680 find_user_command_in_path to allow future customization. */
682 find_in_path (name
, path_list
, flags
)
687 return (find_user_command_in_path (name
, path_list
, flags
));