]> git.ipfire.org Git - thirdparty/bash.git/blob - findcmd.c
Imported from ../bash-2.05.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 <ctype.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 #if defined (HAVE_LIMITS_H)
38 # include <limits.h>
39 #endif
40
41 #include "bashansi.h"
42
43 #include "memalloc.h"
44 #include "shell.h"
45 #include "flags.h"
46 #include "hashlib.h"
47 #include "pathexp.h"
48 #include "hashcmd.h"
49
50 extern int posixly_correct;
51
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 ();
55
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;
62
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;
66
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;
72
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)
77
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. */
82 int
83 file_status (name)
84 char *name;
85 {
86 struct stat finfo;
87
88 /* Determine whether this file exists or not. */
89 if (stat (name, &finfo) < 0)
90 return (0);
91
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);
96
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. */
101 if (access (name, X_OK) == 0)
102 return (FS_EXISTS | FS_EXECABLE);
103 else
104 return (FS_EXISTS);
105 #else /* !AFS */
106
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
109 we can use. */
110
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)
114 {
115 int bits;
116
117 bits = (u_mode_bits (finfo.st_mode) |
118 g_mode_bits (finfo.st_mode) |
119 o_mode_bits (finfo.st_mode));
120
121 if (X_BIT (bits))
122 return (FS_EXISTS | FS_EXECABLE);
123 }
124
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);
128
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);
132
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);
137
138 return (FS_EXISTS);
139 #endif /* !AFS */
140 }
141
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. */
146 int
147 executable_file (file)
148 char *file;
149 {
150 int s;
151
152 s = file_status (file);
153 return ((s & FS_EXECABLE) && ((s & FS_DIRECTORY) == 0));
154 }
155
156 int
157 is_directory (file)
158 char *file;
159 {
160 return (file_status (file) & FS_DIRECTORY);
161 }
162
163 int
164 executable_or_directory (file)
165 char *file;
166 {
167 int s;
168
169 s = file_status (file);
170 return ((s & FS_EXECABLE) || (s & FS_DIRECTORY));
171 }
172
173 /* Locate the executable file referenced by NAME, searching along
174 the contents of the shell PATH variable. Return a new string
175 which is the full pathname to the file, or NULL if the file
176 couldn't be found. If a file is found that isn't executable,
177 and that is the only match, then return that. */
178 char *
179 find_user_command (name)
180 char *name;
181 {
182 return (find_user_command_internal (name, FS_EXEC_PREFERRED|FS_NODIRS));
183 }
184
185 /* Locate the file referenced by NAME, searching along the contents
186 of the shell PATH variable. Return a new string which is the full
187 pathname to the file, or NULL if the file couldn't be found. This
188 returns the first file found. */
189 char *
190 find_path_file (name)
191 char *name;
192 {
193 return (find_user_command_internal (name, FS_EXISTS));
194 }
195
196 static char *
197 _find_user_command_internal (name, flags)
198 char *name;
199 int flags;
200 {
201 char *path_list, *cmd;
202 SHELL_VAR *var;
203
204 /* Search for the value of PATH in both the temporary environment, and
205 in the regular list of variables. */
206 if (var = find_variable_internal ("PATH", 1)) /* XXX could be array? */
207 path_list = value_cell (var);
208 else
209 path_list = (char *)NULL;
210
211 if (path_list == 0 || *path_list == '\0')
212 return (savestring (name));
213
214 cmd = find_user_command_in_path (name, path_list, flags);
215
216 if (var && tempvar_p (var))
217 dispose_variable (var);
218
219 return (cmd);
220 }
221
222 static char *
223 find_user_command_internal (name, flags)
224 char *name;
225 int flags;
226 {
227 #ifdef __WIN32__
228 char *res, *dotexe;
229
230 dotexe = xmalloc (strlen (name) + 5);
231 strcpy (dotexe, name);
232 strcat (dotexe, ".exe");
233 res = _find_user_command_internal (dotexe, flags);
234 free (dotexe);
235 if (res == 0)
236 res = _find_user_command_internal (name, flags);
237 return res;
238 #else
239 return (_find_user_command_internal (name, flags));
240 #endif
241 }
242
243 /* Return the next element from PATH_LIST, a colon separated list of
244 paths. PATH_INDEX_POINTER is the address of an index into PATH_LIST;
245 the index is modified by this function.
246 Return the next element of PATH_LIST or NULL if there are no more. */
247 static char *
248 get_next_path_element (path_list, path_index_pointer)
249 char *path_list;
250 int *path_index_pointer;
251 {
252 char *path;
253
254 path = extract_colon_unit (path_list, path_index_pointer);
255
256 if (path == 0)
257 return (path);
258
259 if (*path == '\0')
260 {
261 free (path);
262 path = savestring (".");
263 }
264
265 return (path);
266 }
267
268 /* Look for PATHNAME in $PATH. Returns either the hashed command
269 corresponding to PATHNAME or the first instance of PATHNAME found
270 in $PATH. Returns a newly-allocated string. */
271 char *
272 search_for_command (pathname)
273 char *pathname;
274 {
275 char *hashed_file, *command;
276 int temp_path, st;
277 SHELL_VAR *path;
278
279 hashed_file = command = (char *)NULL;
280
281 /* If PATH is in the temporary environment for this command, don't use the
282 hash table to search for the full pathname. */
283 path = find_tempenv_variable ("PATH");
284 temp_path = path != 0;
285
286 /* Don't waste time trying to find hashed data for a pathname
287 that is already completely specified or if we're using a command-
288 specific value for PATH. */
289 if (path == 0 && absolute_program (pathname) == 0)
290 hashed_file = find_hashed_filename (pathname);
291
292 /* If a command found in the hash table no longer exists, we need to
293 look for it in $PATH. Thank you Posix.2. This forces us to stat
294 every command found in the hash table. */
295
296 if (hashed_file && (posixly_correct || check_hashed_filenames))
297 {
298 st = file_status (hashed_file);
299 if ((st ^ (FS_EXISTS | FS_EXECABLE)) != 0)
300 {
301 remove_hashed_filename (pathname);
302 free (hashed_file);
303 hashed_file = (char *)NULL;
304 }
305 }
306
307 if (hashed_file)
308 command = hashed_file;
309 else if (absolute_program (pathname))
310 /* A command containing a slash is not looked up in PATH or saved in
311 the hash table. */
312 command = savestring (pathname);
313 else
314 {
315 /* If $PATH is in the temporary environment, we've already retrieved
316 it, so don't bother trying again. */
317 if (temp_path)
318 {
319 command = find_user_command_in_path (pathname, value_cell (path),
320 FS_EXEC_PREFERRED|FS_NODIRS);
321 if (tempvar_p (path))
322 dispose_variable (path);
323 }
324 else
325 command = find_user_command (pathname);
326 if (command && hashing_enabled && temp_path == 0)
327 remember_filename (pathname, command, dot_found_in_search, 1);
328 }
329 return (command);
330 }
331
332 char *
333 user_command_matches (name, flags, state)
334 char *name;
335 int flags, state;
336 {
337 register int i;
338 int path_index, name_len;
339 char *path_list, *path_element, *match;
340 struct stat dotinfo;
341 static char **match_list = NULL;
342 static int match_list_size = 0;
343 static int match_index = 0;
344
345 if (state == 0)
346 {
347 /* Create the list of matches. */
348 if (match_list == 0)
349 {
350 match_list_size = 5;
351 match_list = alloc_array (match_list_size);
352 }
353
354 /* Clear out the old match list. */
355 for (i = 0; i < match_list_size; i++)
356 match_list[i] = 0;
357
358 /* We haven't found any files yet. */
359 match_index = 0;
360
361 if (absolute_program (name))
362 {
363 match_list[0] = find_absolute_program (name, flags);
364 match_list[1] = (char *)NULL;
365 path_list = (char *)NULL;
366 }
367 else
368 {
369 name_len = strlen (name);
370 file_to_lose_on = (char *)NULL;
371 dot_found_in_search = 0;
372 stat (".", &dotinfo);
373 path_list = get_string_value ("PATH");
374 path_index = 0;
375 }
376
377 while (path_list && path_list[path_index])
378 {
379 path_element = get_next_path_element (path_list, &path_index);
380
381 if (path_element == 0)
382 break;
383
384 match = find_in_path_element (name, path_element, flags, name_len, &dotinfo);
385
386 free (path_element);
387
388 if (match == 0)
389 continue;
390
391 if (match_index + 1 == match_list_size)
392 {
393 match_list_size += 10;
394 match_list = (char **)xrealloc (match_list, (match_list_size + 1) * sizeof (char *));
395 }
396
397 match_list[match_index++] = match;
398 match_list[match_index] = (char *)NULL;
399 FREE (file_to_lose_on);
400 file_to_lose_on = (char *)NULL;
401 }
402
403 /* We haven't returned any strings yet. */
404 match_index = 0;
405 }
406
407 match = match_list[match_index];
408
409 if (match)
410 match_index++;
411
412 return (match);
413 }
414
415 static char *
416 find_absolute_program (name, flags)
417 char *name;
418 int flags;
419 {
420 int st;
421
422 st = file_status (name);
423
424 /* If the file doesn't exist, quit now. */
425 if ((st & FS_EXISTS) == 0)
426 return ((char *)NULL);
427
428 /* If we only care about whether the file exists or not, return
429 this filename. Otherwise, maybe we care about whether this
430 file is executable. If it is, and that is what we want, return it. */
431 if ((flags & FS_EXISTS) || ((flags & FS_EXEC_ONLY) && (st & FS_EXECABLE)))
432 return (savestring (name));
433
434 return ((char *)NULL);
435 }
436
437 static char *
438 find_in_path_element (name, path, flags, name_len, dotinfop)
439 char *name, *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) : 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 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 }