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