]> git.ipfire.org Git - thirdparty/bash.git/blame - findcmd.c
Imported from ../bash-3.2.48.tar.gz.
[thirdparty/bash.git] / findcmd.c
CommitLineData
cce855bc
JA
1/* findcmd.c -- Functions to search for commands by name. */
2
3/* Copyright (C) 1997 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 it
8 under the terms of the GNU General Public License as published by
bb70624e 9 the Free Software Foundation; either version 2, or (at your option)
cce855bc
JA
10 any later version.
11
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.
16
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. */
21
22#include "config.h"
23
24#include <stdio.h>
f73dda09 25#include "chartypes.h"
cce855bc 26#include "bashtypes.h"
b80f6443 27#if !defined (_MINIX) && defined (HAVE_SYS_FILE_H)
cce855bc
JA
28# include <sys/file.h>
29#endif
30#include "filecntl.h"
31#include "posixstat.h"
32
33#if defined (HAVE_UNISTD_H)
34# include <unistd.h>
35#endif
36
cce855bc
JA
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"
f73dda09 45#include "findcmd.h" /* matching prototypes and declarations */
cce855bc
JA
46
47extern int posixly_correct;
48
49/* Static functions defined and used in this file. */
f73dda09
JA
50static char *_find_user_command_internal __P((const char *, int));
51static char *find_user_command_internal __P((const char *, int));
52static char *find_user_command_in_path __P((const char *, char *, int));
53static char *find_in_path_element __P((const char *, char *, int, int, struct stat *));
54static char *find_absolute_program __P((const char *, int));
55
56static char *get_next_path_element __P((char *, int *));
cce855bc
JA
57
58/* The file name which we would try to execute, except that it isn't
59 possible to execute it. This is the first file that matches the
60 name that we are looking for while we are searching $PATH for a
61 suitable one to execute. If we cannot find a suitable executable
62 file, then we use this one. */
63static char *file_to_lose_on;
64
65/* Non-zero if we should stat every command found in the hash table to
66 make sure it still exists. */
67int check_hashed_filenames;
68
69/* DOT_FOUND_IN_SEARCH becomes non-zero when find_user_command ()
70 encounters a `.' as the directory pathname while scanning the
71 list of possible pathnames; i.e., if `.' comes before the directory
72 containing the file of interest. */
73int dot_found_in_search = 0;
74
cce855bc
JA
75/* Return some flags based on information about this file.
76 The EXISTS bit is non-zero if the file is found.
77 The EXECABLE bit is non-zero the file is executble.
78 Zero is returned if the file is not found. */
79int
80file_status (name)
f73dda09 81 const char *name;
cce855bc
JA
82{
83 struct stat finfo;
95732b49 84 int r;
cce855bc
JA
85
86 /* Determine whether this file exists or not. */
87 if (stat (name, &finfo) < 0)
88 return (0);
89
90 /* If the file is a directory, then it is not "executable" in the
91 sense of the shell. */
92 if (S_ISDIR (finfo.st_mode))
93 return (FS_EXISTS|FS_DIRECTORY);
94
95732b49
JA
95 r = FS_EXISTS;
96
cce855bc
JA
97#if defined (AFS)
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. */
95732b49
JA
101 if (access (name, X_OK) == 0)
102 r |= FS_EXECABLE;
103 if (access (name, R_OK) == 0)
104 r |= FS_READABLE;
105
106 return r;
cce855bc
JA
107#else /* !AFS */
108
109 /* Find out if the file is actually executable. By definition, the
110 only other criteria is that the file has an execute bit set that
95732b49 111 we can use. The same with whether or not a file is readable. */
cce855bc
JA
112
113 /* Root only requires execute permission for any of owner, group or
95732b49 114 others to be able to exec a file, and can read any file. */
cce855bc
JA
115 if (current_user.euid == (uid_t)0)
116 {
95732b49
JA
117 r |= FS_READABLE;
118 if (finfo.st_mode & S_IXUGO)
119 r |= FS_EXECABLE;
120 return r;
cce855bc
JA
121 }
122
95732b49 123 /* If we are the owner of the file, the owner bits apply. */
b80f6443 124 if (current_user.euid == finfo.st_uid)
95732b49
JA
125 {
126 if (finfo.st_mode & S_IXUSR)
127 r |= FS_EXECABLE;
128 if (finfo.st_mode & S_IRUSR)
129 r |= FS_READABLE;
130 }
cce855bc
JA
131
132 /* If we are in the owning group, the group permissions apply. */
b80f6443 133 else if (group_member (finfo.st_gid))
95732b49
JA
134 {
135 if (finfo.st_mode & S_IXGRP)
136 r |= FS_EXECABLE;
137 if (finfo.st_mode & S_IRGRP)
138 r |= FS_READABLE;
139 }
cce855bc 140
b80f6443
JA
141 /* Else we check whether `others' have permission to execute the file */
142 else
95732b49
JA
143 {
144 if (finfo.st_mode & S_IXOTH)
145 r |= FS_EXECABLE;
146 if (finfo.st_mode & S_IROTH)
147 r |= FS_READABLE;
148 }
149
150 return r;
cce855bc
JA
151#endif /* !AFS */
152}
153
154/* Return non-zero if FILE exists and is executable.
155 Note that this function is the definition of what an
156 executable file is; do not change this unless YOU know
157 what an executable file is. */
158int
159executable_file (file)
f73dda09 160 const char *file;
cce855bc
JA
161{
162 int s;
163
164 s = file_status (file);
165 return ((s & FS_EXECABLE) && ((s & FS_DIRECTORY) == 0));
166}
167
168int
169is_directory (file)
f73dda09 170 const char *file;
cce855bc
JA
171{
172 return (file_status (file) & FS_DIRECTORY);
173}
174
28ef6c31
JA
175int
176executable_or_directory (file)
f73dda09 177 const char *file;
28ef6c31
JA
178{
179 int s;
180
181 s = file_status (file);
182 return ((s & FS_EXECABLE) || (s & FS_DIRECTORY));
183}
184
cce855bc
JA
185/* Locate the executable file referenced by NAME, searching along
186 the contents of the shell PATH variable. Return a new string
187 which is the full pathname to the file, or NULL if the file
188 couldn't be found. If a file is found that isn't executable,
189 and that is the only match, then return that. */
190char *
191find_user_command (name)
f73dda09 192 const char *name;
cce855bc
JA
193{
194 return (find_user_command_internal (name, FS_EXEC_PREFERRED|FS_NODIRS));
195}
196
197/* Locate the file referenced by NAME, searching along the contents
198 of the shell PATH variable. Return a new string which is the full
199 pathname to the file, or NULL if the file couldn't be found. This
95732b49
JA
200 returns the first readable file found; designed to be used to look
201 for shell scripts or files to source. */
cce855bc
JA
202char *
203find_path_file (name)
f73dda09 204 const char *name;
cce855bc 205{
95732b49 206 return (find_user_command_internal (name, FS_READABLE));
cce855bc
JA
207}
208
209static char *
210_find_user_command_internal (name, flags)
f73dda09 211 const char *name;
cce855bc
JA
212 int flags;
213{
214 char *path_list, *cmd;
215 SHELL_VAR *var;
216
7117c2d2 217 /* Search for the value of PATH in both the temporary environments and
cce855bc
JA
218 in the regular list of variables. */
219 if (var = find_variable_internal ("PATH", 1)) /* XXX could be array? */
220 path_list = value_cell (var);
221 else
222 path_list = (char *)NULL;
223
224 if (path_list == 0 || *path_list == '\0')
225 return (savestring (name));
226
227 cmd = find_user_command_in_path (name, path_list, flags);
228
cce855bc
JA
229 return (cmd);
230}
231
232static char *
233find_user_command_internal (name, flags)
f73dda09 234 const char *name;
cce855bc
JA
235 int flags;
236{
237#ifdef __WIN32__
238 char *res, *dotexe;
239
f73dda09 240 dotexe = (char *)xmalloc (strlen (name) + 5);
cce855bc
JA
241 strcpy (dotexe, name);
242 strcat (dotexe, ".exe");
243 res = _find_user_command_internal (dotexe, flags);
244 free (dotexe);
245 if (res == 0)
246 res = _find_user_command_internal (name, flags);
247 return res;
248#else
249 return (_find_user_command_internal (name, flags));
250#endif
251}
252
253/* Return the next element from PATH_LIST, a colon separated list of
254 paths. PATH_INDEX_POINTER is the address of an index into PATH_LIST;
255 the index is modified by this function.
256 Return the next element of PATH_LIST or NULL if there are no more. */
257static char *
258get_next_path_element (path_list, path_index_pointer)
259 char *path_list;
260 int *path_index_pointer;
261{
262 char *path;
263
264 path = extract_colon_unit (path_list, path_index_pointer);
265
bb70624e 266 if (path == 0)
cce855bc
JA
267 return (path);
268
bb70624e 269 if (*path == '\0')
cce855bc
JA
270 {
271 free (path);
272 path = savestring (".");
273 }
274
275 return (path);
276}
277
278/* Look for PATHNAME in $PATH. Returns either the hashed command
279 corresponding to PATHNAME or the first instance of PATHNAME found
280 in $PATH. Returns a newly-allocated string. */
281char *
282search_for_command (pathname)
f73dda09 283 const char *pathname;
cce855bc
JA
284{
285 char *hashed_file, *command;
286 int temp_path, st;
287 SHELL_VAR *path;
288
289 hashed_file = command = (char *)NULL;
290
291 /* If PATH is in the temporary environment for this command, don't use the
292 hash table to search for the full pathname. */
7117c2d2
JA
293 path = find_variable_internal ("PATH", 1);
294 temp_path = path && tempvar_p (path);
295 if (temp_path == 0 && path)
296 path = (SHELL_VAR *)NULL;
cce855bc
JA
297
298 /* Don't waste time trying to find hashed data for a pathname
299 that is already completely specified or if we're using a command-
300 specific value for PATH. */
301 if (path == 0 && absolute_program (pathname) == 0)
7117c2d2 302 hashed_file = phash_search (pathname);
cce855bc
JA
303
304 /* If a command found in the hash table no longer exists, we need to
305 look for it in $PATH. Thank you Posix.2. This forces us to stat
306 every command found in the hash table. */
307
308 if (hashed_file && (posixly_correct || check_hashed_filenames))
309 {
310 st = file_status (hashed_file);
f1be666c 311 if ((st & (FS_EXISTS|FS_EXECABLE)) != (FS_EXISTS|FS_EXECABLE))
cce855bc 312 {
7117c2d2 313 phash_remove (pathname);
cce855bc
JA
314 free (hashed_file);
315 hashed_file = (char *)NULL;
316 }
317 }
318
319 if (hashed_file)
320 command = hashed_file;
321 else if (absolute_program (pathname))
322 /* A command containing a slash is not looked up in PATH or saved in
323 the hash table. */
324 command = savestring (pathname);
325 else
326 {
327 /* If $PATH is in the temporary environment, we've already retrieved
328 it, so don't bother trying again. */
329 if (temp_path)
28ef6c31 330 {
cce855bc
JA
331 command = find_user_command_in_path (pathname, value_cell (path),
332 FS_EXEC_PREFERRED|FS_NODIRS);
28ef6c31 333 }
cce855bc
JA
334 else
335 command = find_user_command (pathname);
336 if (command && hashing_enabled && temp_path == 0)
7117c2d2 337 phash_insert ((char *)pathname, command, dot_found_in_search, 1); /* XXX fix const later */
cce855bc
JA
338 }
339 return (command);
340}
341
342char *
343user_command_matches (name, flags, state)
f73dda09 344 const char *name;
cce855bc
JA
345 int flags, state;
346{
347 register int i;
348 int path_index, name_len;
349 char *path_list, *path_element, *match;
350 struct stat dotinfo;
351 static char **match_list = NULL;
352 static int match_list_size = 0;
353 static int match_index = 0;
354
355 if (state == 0)
356 {
357 /* Create the list of matches. */
358 if (match_list == 0)
359 {
360 match_list_size = 5;
7117c2d2 361 match_list = strvec_create (match_list_size);
cce855bc
JA
362 }
363
364 /* Clear out the old match list. */
365 for (i = 0; i < match_list_size; i++)
366 match_list[i] = 0;
367
368 /* We haven't found any files yet. */
369 match_index = 0;
370
371 if (absolute_program (name))
372 {
373 match_list[0] = find_absolute_program (name, flags);
374 match_list[1] = (char *)NULL;
375 path_list = (char *)NULL;
376 }
377 else
378 {
379 name_len = strlen (name);
380 file_to_lose_on = (char *)NULL;
381 dot_found_in_search = 0;
382 stat (".", &dotinfo);
383 path_list = get_string_value ("PATH");
384 path_index = 0;
385 }
386
387 while (path_list && path_list[path_index])
388 {
389 path_element = get_next_path_element (path_list, &path_index);
390
391 if (path_element == 0)
392 break;
393
394 match = find_in_path_element (name, path_element, flags, name_len, &dotinfo);
395
396 free (path_element);
397
398 if (match == 0)
399 continue;
400
401 if (match_index + 1 == match_list_size)
402 {
403 match_list_size += 10;
7117c2d2 404 match_list = strvec_resize (match_list, (match_list_size + 1));
cce855bc
JA
405 }
406
407 match_list[match_index++] = match;
408 match_list[match_index] = (char *)NULL;
409 FREE (file_to_lose_on);
410 file_to_lose_on = (char *)NULL;
411 }
412
413 /* We haven't returned any strings yet. */
414 match_index = 0;
415 }
416
417 match = match_list[match_index];
418
419 if (match)
420 match_index++;
421
422 return (match);
423}
424
cce855bc
JA
425static char *
426find_absolute_program (name, flags)
f73dda09 427 const char *name;
cce855bc
JA
428 int flags;
429{
430 int st;
431
432 st = file_status (name);
433
434 /* If the file doesn't exist, quit now. */
435 if ((st & FS_EXISTS) == 0)
436 return ((char *)NULL);
437
438 /* If we only care about whether the file exists or not, return
439 this filename. Otherwise, maybe we care about whether this
440 file is executable. If it is, and that is what we want, return it. */
441 if ((flags & FS_EXISTS) || ((flags & FS_EXEC_ONLY) && (st & FS_EXECABLE)))
442 return (savestring (name));
443
f73dda09 444 return (NULL);
cce855bc
JA
445}
446
447static char *
448find_in_path_element (name, path, flags, name_len, dotinfop)
f73dda09
JA
449 const char *name;
450 char *path;
cce855bc
JA
451 int flags, name_len;
452 struct stat *dotinfop;
453{
454 int status;
455 char *full_path, *xpath;
456
7117c2d2 457 xpath = (*path == '~') ? bash_tilde_expand (path, 0) : path;
cce855bc
JA
458
459 /* Remember the location of "." in the path, in all its forms
460 (as long as they begin with a `.', e.g. `./.') */
461 if (dot_found_in_search == 0 && *xpath == '.')
462 dot_found_in_search = same_file (".", xpath, dotinfop, (struct stat *)NULL);
463
bb70624e 464 full_path = sh_makepath (xpath, name, 0);
cce855bc
JA
465
466 status = file_status (full_path);
467
468 if (xpath != path)
469 free (xpath);
470
471 if ((status & FS_EXISTS) == 0)
472 {
473 free (full_path);
474 return ((char *)NULL);
475 }
476
477 /* The file exists. If the caller simply wants the first file, here it is. */
478 if (flags & FS_EXISTS)
479 return (full_path);
480
95732b49
JA
481 /* If we have a readable file, and the caller wants a readable file, this
482 is it. */
483 if ((flags & FS_READABLE) && (status & FS_READABLE))
484 return (full_path);
485
cce855bc
JA
486 /* If the file is executable, then it satisfies the cases of
487 EXEC_ONLY and EXEC_PREFERRED. Return this file unconditionally. */
95732b49 488 if ((status & FS_EXECABLE) && (flags & (FS_EXEC_ONLY|FS_EXEC_PREFERRED)) &&
cce855bc
JA
489 (((flags & FS_NODIRS) == 0) || ((status & FS_DIRECTORY) == 0)))
490 {
491 FREE (file_to_lose_on);
492 file_to_lose_on = (char *)NULL;
493 return (full_path);
494 }
495
496 /* The file is not executable, but it does exist. If we prefer
497 an executable, then remember this one if it is the first one
498 we have found. */
499 if ((flags & FS_EXEC_PREFERRED) && file_to_lose_on == 0)
500 file_to_lose_on = savestring (full_path);
501
502 /* If we want only executable files, or we don't want directories and
95732b49
JA
503 this file is a directory, or we want a readable file and this file
504 isn't readable, fail. */
505 if ((flags & (FS_EXEC_ONLY|FS_EXEC_PREFERRED)) ||
506 ((flags & FS_NODIRS) && (status & FS_DIRECTORY)) ||
507 ((flags & FS_READABLE) && (status & FS_READABLE) == 0))
cce855bc
JA
508 {
509 free (full_path);
510 return ((char *)NULL);
511 }
512 else
513 return (full_path);
514}
515
516/* This does the dirty work for find_user_command_internal () and
517 user_command_matches ().
518 NAME is the name of the file to search for.
519 PATH_LIST is a colon separated list of directories to search.
520 FLAGS contains bit fields which control the files which are eligible.
521 Some values are:
522 FS_EXEC_ONLY: The file must be an executable to be found.
523 FS_EXEC_PREFERRED: If we can't find an executable, then the
524 the first file matching NAME will do.
525 FS_EXISTS: The first file found will do.
526 FS_NODIRS: Don't find any directories.
527*/
528static char *
529find_user_command_in_path (name, path_list, flags)
f73dda09 530 const char *name;
cce855bc
JA
531 char *path_list;
532 int flags;
533{
534 char *full_path, *path;
535 int path_index, name_len;
536 struct stat dotinfo;
537
538 /* We haven't started looking, so we certainly haven't seen
539 a `.' as the directory path yet. */
540 dot_found_in_search = 0;
541
542 if (absolute_program (name))
543 {
544 full_path = find_absolute_program (name, flags);
545 return (full_path);
546 }
547
548 if (path_list == 0 || *path_list == '\0')
549 return (savestring (name)); /* XXX */
550
551 file_to_lose_on = (char *)NULL;
552 name_len = strlen (name);
553 stat (".", &dotinfo);
554 path_index = 0;
555
556 while (path_list[path_index])
557 {
558 /* Allow the user to interrupt out of a lengthy path search. */
559 QUIT;
560
561 path = get_next_path_element (path_list, &path_index);
562 if (path == 0)
563 break;
564
565 /* Side effects: sets dot_found_in_search, possibly sets
566 file_to_lose_on. */
567 full_path = find_in_path_element (name, path, flags, name_len, &dotinfo);
568 free (path);
569
570 /* This should really be in find_in_path_element, but there isn't the
571 right combination of flags. */
572 if (full_path && is_directory (full_path))
573 {
574 free (full_path);
575 continue;
576 }
577
578 if (full_path)
579 {
580 FREE (file_to_lose_on);
581 return (full_path);
582 }
583 }
584
585 /* We didn't find exactly what the user was looking for. Return
586 the contents of FILE_TO_LOSE_ON which is NULL when the search
587 required an executable, or non-NULL if a file was found and the
28ef6c31
JA
588 search would accept a non-executable as a last resort. If the
589 caller specified FS_NODIRS, and file_to_lose_on is a directory,
590 return NULL. */
591 if (file_to_lose_on && (flags & FS_NODIRS) && is_directory (file_to_lose_on))
592 {
593 free (file_to_lose_on);
594 file_to_lose_on = (char *)NULL;
595 }
596
cce855bc
JA
597 return (file_to_lose_on);
598}