1 /* findcmd.c -- Functions to search for commands by name. */
3 /* Copyright (C) 1997 Free Software Foundation, Inc.
5 This file is part of GNU Bash, the Bourne Again SHell.
7 Bash is free software; you can redistribute it and/or modify it
8 under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 1, or (at your option)
12 Bash is distributed in the hope that it will be useful, but WITHOUT
13 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
14 or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
15 License for more details.
17 You should have received a copy of the GNU General Public License
18 along with Bash; see the file COPYING. If not, write to the
19 Free Software Foundation Inc.,
20 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. */
26 #include "bashtypes.h"
28 # include <sys/file.h>
31 #include "posixstat.h"
33 #if defined (HAVE_UNISTD_H)
37 #if defined (HAVE_LIMITS_H)
50 extern int posixly_correct;
52 /* Static functions defined and used in this file. */
53 static char *find_user_command_internal (), *find_user_command_in_path ();
54 static char *find_in_path_element (), *find_absolute_program ();
56 /* The file name which we would try to execute, except that it isn't
57 possible to execute it. This is the first file that matches the
58 name that we are looking for while we are searching $PATH for a
59 suitable one to execute. If we cannot find a suitable executable
60 file, then we use this one. */
61 static char *file_to_lose_on;
63 /* Non-zero if we should stat every command found in the hash table to
64 make sure it still exists. */
65 int check_hashed_filenames;
67 /* DOT_FOUND_IN_SEARCH becomes non-zero when find_user_command ()
68 encounters a `.' as the directory pathname while scanning the
69 list of possible pathnames; i.e., if `.' comes before the directory
70 containing the file of interest. */
71 int dot_found_in_search = 0;
73 #define u_mode_bits(x) (((x) & 0000700) >> 6)
74 #define g_mode_bits(x) (((x) & 0000070) >> 3)
75 #define o_mode_bits(x) (((x) & 0000007) >> 0)
76 #define X_BIT(x) ((x) & 1)
78 /* Return some flags based on information about this file.
79 The EXISTS bit is non-zero if the file is found.
80 The EXECABLE bit is non-zero the file is executble.
81 Zero is returned if the file is not found. */
88 /* Determine whether this file exists or not. */
89 if (stat (name, &finfo) < 0)
92 /* If the file is a directory, then it is not "executable" in the
93 sense of the shell. */
94 if (S_ISDIR (finfo.st_mode))
95 return (FS_EXISTS|FS_DIRECTORY);
98 /* We have to use access(2) to determine access because AFS does not
99 support Unix file system semantics. This may produce wrong
100 answers for non-AFS files when ruid != euid. I hate AFS. */
101 if (access (name, X_OK) == 0)
102 return (FS_EXISTS | FS_EXECABLE);
107 /* Find out if the file is actually executable. By definition, the
108 only other criteria is that the file has an execute bit set that
111 /* Root only requires execute permission for any of owner, group or
112 others to be able to exec a file. */
113 if (current_user.euid == (uid_t)0)
117 bits = (u_mode_bits (finfo.st_mode) |
118 g_mode_bits (finfo.st_mode) |
119 o_mode_bits (finfo.st_mode));
122 return (FS_EXISTS | FS_EXECABLE);
125 /* If we are the owner of the file, the owner execute bit applies. */
126 if (current_user.euid == finfo.st_uid && X_BIT (u_mode_bits (finfo.st_mode)))
127 return (FS_EXISTS | FS_EXECABLE);
129 /* If we are in the owning group, the group permissions apply. */
130 if (group_member (finfo.st_gid) && X_BIT (g_mode_bits (finfo.st_mode)))
131 return (FS_EXISTS | FS_EXECABLE);
133 /* If `others' have execute permission to the file, then so do we,
134 since we are also `others'. */
135 if (X_BIT (o_mode_bits (finfo.st_mode)))
136 return (FS_EXISTS | FS_EXECABLE);
142 /* Return non-zero if FILE exists and is executable.
143 Note that this function is the definition of what an
144 executable file is; do not change this unless YOU know
145 what an executable file is. */
147 executable_file (file)
152 s = file_status (file);
153 return ((s & FS_EXECABLE) && ((s & FS_DIRECTORY) == 0));
160 return (file_status (file) & FS_DIRECTORY);
163 /* Locate the executable file referenced by NAME, searching along
164 the contents of the shell PATH variable. Return a new string
165 which is the full pathname to the file, or NULL if the file
166 couldn't be found. If a file is found that isn't executable,
167 and that is the only match, then return that. */
169 find_user_command (name)
172 return (find_user_command_internal (name, FS_EXEC_PREFERRED|FS_NODIRS));
175 /* Locate the file referenced by NAME, searching along the contents
176 of the shell PATH variable. Return a new string which is the full
177 pathname to the file, or NULL if the file couldn't be found. This
178 returns the first file found. */
180 find_path_file (name)
183 return (find_user_command_internal (name, FS_EXISTS));
187 _find_user_command_internal (name, flags)
191 char *path_list, *cmd;
194 /* Search for the value of PATH in both the temporary environment, and
195 in the regular list of variables. */
196 if (var = find_variable_internal ("PATH", 1)) /* XXX could be array? */
197 path_list = value_cell (var);
199 path_list = (char *)NULL;
201 if (path_list == 0 || *path_list == '\0')
202 return (savestring (name));
204 cmd = find_user_command_in_path (name, path_list, flags);
206 if (var && tempvar_p (var))
207 dispose_variable (var);
213 find_user_command_internal (name, flags)
220 dotexe = xmalloc (strlen (name) + 5);
221 strcpy (dotexe, name);
222 strcat (dotexe, ".exe");
223 res = _find_user_command_internal (dotexe, flags);
226 res = _find_user_command_internal (name, flags);
229 return (_find_user_command_internal (name, flags));
233 /* Return the next element from PATH_LIST, a colon separated list of
234 paths. PATH_INDEX_POINTER is the address of an index into PATH_LIST;
235 the index is modified by this function.
236 Return the next element of PATH_LIST or NULL if there are no more. */
238 get_next_path_element (path_list, path_index_pointer)
240 int *path_index_pointer;
244 path = extract_colon_unit (path_list, path_index_pointer);
252 path = savestring (".");
258 /* Look for PATHNAME in $PATH. Returns either the hashed command
259 corresponding to PATHNAME or the first instance of PATHNAME found
260 in $PATH. Returns a newly-allocated string. */
262 search_for_command (pathname)
265 char *hashed_file, *command;
269 hashed_file = command = (char *)NULL;
271 /* If PATH is in the temporary environment for this command, don't use the
272 hash table to search for the full pathname. */
273 path = find_tempenv_variable ("PATH");
274 temp_path = path != 0;
276 /* Don't waste time trying to find hashed data for a pathname
277 that is already completely specified or if we're using a command-
278 specific value for PATH. */
279 if (path == 0 && absolute_program (pathname) == 0)
280 hashed_file = find_hashed_filename (pathname);
282 /* If a command found in the hash table no longer exists, we need to
283 look for it in $PATH. Thank you Posix.2. This forces us to stat
284 every command found in the hash table. */
286 if (hashed_file && (posixly_correct || check_hashed_filenames))
288 st = file_status (hashed_file);
289 if ((st ^ (FS_EXISTS | FS_EXECABLE)) != 0)
291 remove_hashed_filename (pathname);
293 hashed_file = (char *)NULL;
298 command = hashed_file;
299 else if (absolute_program (pathname))
300 /* A command containing a slash is not looked up in PATH or saved in
302 command = savestring (pathname);
305 /* If $PATH is in the temporary environment, we've already retrieved
306 it, so don't bother trying again. */
309 command = find_user_command_in_path (pathname, value_cell (path),
310 FS_EXEC_PREFERRED|FS_NODIRS);
311 if (tempvar_p (path))
312 dispose_variable (path);
315 command = find_user_command (pathname);
316 if (command && hashing_enabled && temp_path == 0)
317 remember_filename (pathname, command, dot_found_in_search, 1);
323 user_command_matches (name, flags, state)
328 int path_index, name_len;
329 char *path_list, *path_element, *match;
331 static char **match_list = NULL;
332 static int match_list_size = 0;
333 static int match_index = 0;
337 /* Create the list of matches. */
341 match_list = (char **)xmalloc (match_list_size * sizeof(char *));
344 /* Clear out the old match list. */
345 for (i = 0; i < match_list_size; i++)
348 /* We haven't found any files yet. */
351 if (absolute_program (name))
353 match_list[0] = find_absolute_program (name, flags);
354 match_list[1] = (char *)NULL;
355 path_list = (char *)NULL;
359 name_len = strlen (name);
360 file_to_lose_on = (char *)NULL;
361 dot_found_in_search = 0;
362 stat (".", &dotinfo);
363 path_list = get_string_value ("PATH");
367 while (path_list && path_list[path_index])
369 path_element = get_next_path_element (path_list, &path_index);
371 if (path_element == 0)
374 match = find_in_path_element (name, path_element, flags, name_len, &dotinfo);
381 if (match_index + 1 == match_list_size)
383 match_list_size += 10;
384 match_list = (char **)xrealloc (match_list, (match_list_size + 1) * sizeof (char *));
387 match_list[match_index++] = match;
388 match_list[match_index] = (char *)NULL;
389 FREE (file_to_lose_on);
390 file_to_lose_on = (char *)NULL;
393 /* We haven't returned any strings yet. */
397 match = match_list[match_index];
405 /* Turn PATH, a directory, and NAME, a filename, into a full pathname.
406 This allocates new memory and returns it. */
408 make_full_pathname (path, name, name_len)
415 path_len = strlen (path);
416 full_path = xmalloc (2 + path_len + name_len);
417 strcpy (full_path, path);
418 full_path[path_len] = '/';
419 strcpy (full_path + path_len + 1, name);
424 find_absolute_program (name, flags)
430 st = file_status (name);
432 /* If the file doesn't exist, quit now. */
433 if ((st & FS_EXISTS) == 0)
434 return ((char *)NULL);
436 /* If we only care about whether the file exists or not, return
437 this filename. Otherwise, maybe we care about whether this
438 file is executable. If it is, and that is what we want, return it. */
439 if ((flags & FS_EXISTS) || ((flags & FS_EXEC_ONLY) && (st & FS_EXECABLE)))
440 return (savestring (name));
442 return ((char *)NULL);
446 find_in_path_element (name, path, flags, name_len, dotinfop)
449 struct stat *dotinfop;
452 char *full_path, *xpath;
454 xpath = (*path == '~') ? bash_tilde_expand (path) : path;
456 /* Remember the location of "." in the path, in all its forms
457 (as long as they begin with a `.', e.g. `./.') */
458 if (dot_found_in_search == 0 && *xpath == '.')
459 dot_found_in_search = same_file (".", xpath, dotinfop, (struct stat *)NULL);
461 full_path = make_full_pathname (xpath, name, name_len);
463 status = file_status (full_path);
468 if ((status & FS_EXISTS) == 0)
471 return ((char *)NULL);
474 /* The file exists. If the caller simply wants the first file, here it is. */
475 if (flags & FS_EXISTS)
478 /* If the file is executable, then it satisfies the cases of
479 EXEC_ONLY and EXEC_PREFERRED. Return this file unconditionally. */
480 if ((status & FS_EXECABLE) &&
481 (((flags & FS_NODIRS) == 0) || ((status & FS_DIRECTORY) == 0)))
483 FREE (file_to_lose_on);
484 file_to_lose_on = (char *)NULL;
488 /* The file is not executable, but it does exist. If we prefer
489 an executable, then remember this one if it is the first one
491 if ((flags & FS_EXEC_PREFERRED) && file_to_lose_on == 0)
492 file_to_lose_on = savestring (full_path);
494 /* If we want only executable files, or we don't want directories and
495 this file is a directory, fail. */
496 if ((flags & FS_EXEC_ONLY) || (flags & FS_EXEC_PREFERRED) ||
497 ((flags & FS_NODIRS) && (status & FS_DIRECTORY)))
500 return ((char *)NULL);
506 /* This does the dirty work for find_user_command_internal () and
507 user_command_matches ().
508 NAME is the name of the file to search for.
509 PATH_LIST is a colon separated list of directories to search.
510 FLAGS contains bit fields which control the files which are eligible.
512 FS_EXEC_ONLY: The file must be an executable to be found.
513 FS_EXEC_PREFERRED: If we can't find an executable, then the
514 the first file matching NAME will do.
515 FS_EXISTS: The first file found will do.
516 FS_NODIRS: Don't find any directories.
519 find_user_command_in_path (name, path_list, flags)
524 char *full_path, *path;
525 int path_index, name_len;
528 /* We haven't started looking, so we certainly haven't seen
529 a `.' as the directory path yet. */
530 dot_found_in_search = 0;
532 if (absolute_program (name))
534 full_path = find_absolute_program (name, flags);
538 if (path_list == 0 || *path_list == '\0')
539 return (savestring (name)); /* XXX */
541 file_to_lose_on = (char *)NULL;
542 name_len = strlen (name);
543 stat (".", &dotinfo);
546 while (path_list[path_index])
548 /* Allow the user to interrupt out of a lengthy path search. */
551 path = get_next_path_element (path_list, &path_index);
555 /* Side effects: sets dot_found_in_search, possibly sets
557 full_path = find_in_path_element (name, path, flags, name_len, &dotinfo);
560 /* This should really be in find_in_path_element, but there isn't the
561 right combination of flags. */
562 if (full_path && is_directory (full_path))
570 FREE (file_to_lose_on);
575 /* We didn't find exactly what the user was looking for. Return
576 the contents of FILE_TO_LOSE_ON which is NULL when the search
577 required an executable, or non-NULL if a file was found and the
578 search would accept a non-executable as a last resort. */
579 return (file_to_lose_on);