]> git.ipfire.org Git - thirdparty/bash.git/blob - findcmd.c
837b392dbfad8e12217c6b51abda7333d0330c5b
[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 1, 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 /* 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. */
168 char *
169 find_user_command (name)
170 char *name;
171 {
172 return (find_user_command_internal (name, FS_EXEC_PREFERRED|FS_NODIRS));
173 }
174
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. */
179 char *
180 find_path_file (name)
181 char *name;
182 {
183 return (find_user_command_internal (name, FS_EXISTS));
184 }
185
186 static char *
187 _find_user_command_internal (name, flags)
188 char *name;
189 int flags;
190 {
191 char *path_list, *cmd;
192 SHELL_VAR *var;
193
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);
198 else
199 path_list = (char *)NULL;
200
201 if (path_list == 0 || *path_list == '\0')
202 return (savestring (name));
203
204 cmd = find_user_command_in_path (name, path_list, flags);
205
206 if (var && tempvar_p (var))
207 dispose_variable (var);
208
209 return (cmd);
210 }
211
212 static char *
213 find_user_command_internal (name, flags)
214 char *name;
215 int flags;
216 {
217 #ifdef __WIN32__
218 char *res, *dotexe;
219
220 dotexe = xmalloc (strlen (name) + 5);
221 strcpy (dotexe, name);
222 strcat (dotexe, ".exe");
223 res = _find_user_command_internal (dotexe, flags);
224 free (dotexe);
225 if (res == 0)
226 res = _find_user_command_internal (name, flags);
227 return res;
228 #else
229 return (_find_user_command_internal (name, flags));
230 #endif
231 }
232
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. */
237 static char *
238 get_next_path_element (path_list, path_index_pointer)
239 char *path_list;
240 int *path_index_pointer;
241 {
242 char *path;
243
244 path = extract_colon_unit (path_list, path_index_pointer);
245
246 if (!path)
247 return (path);
248
249 if (!*path)
250 {
251 free (path);
252 path = savestring (".");
253 }
254
255 return (path);
256 }
257
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. */
261 char *
262 search_for_command (pathname)
263 char *pathname;
264 {
265 char *hashed_file, *command;
266 int temp_path, st;
267 SHELL_VAR *path;
268
269 hashed_file = command = (char *)NULL;
270
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;
275
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);
281
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. */
285
286 if (hashed_file && (posixly_correct || check_hashed_filenames))
287 {
288 st = file_status (hashed_file);
289 if ((st ^ (FS_EXISTS | FS_EXECABLE)) != 0)
290 {
291 remove_hashed_filename (pathname);
292 free (hashed_file);
293 hashed_file = (char *)NULL;
294 }
295 }
296
297 if (hashed_file)
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
301 the hash table. */
302 command = savestring (pathname);
303 else
304 {
305 /* If $PATH is in the temporary environment, we've already retrieved
306 it, so don't bother trying again. */
307 if (temp_path)
308 {
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);
313 }
314 else
315 command = find_user_command (pathname);
316 if (command && hashing_enabled && temp_path == 0)
317 remember_filename (pathname, command, dot_found_in_search, 1);
318 }
319 return (command);
320 }
321
322 char *
323 user_command_matches (name, flags, state)
324 char *name;
325 int flags, state;
326 {
327 register int i;
328 int path_index, name_len;
329 char *path_list, *path_element, *match;
330 struct stat dotinfo;
331 static char **match_list = NULL;
332 static int match_list_size = 0;
333 static int match_index = 0;
334
335 if (state == 0)
336 {
337 /* Create the list of matches. */
338 if (match_list == 0)
339 {
340 match_list_size = 5;
341 match_list = (char **)xmalloc (match_list_size * sizeof(char *));
342 }
343
344 /* Clear out the old match list. */
345 for (i = 0; i < match_list_size; i++)
346 match_list[i] = 0;
347
348 /* We haven't found any files yet. */
349 match_index = 0;
350
351 if (absolute_program (name))
352 {
353 match_list[0] = find_absolute_program (name, flags);
354 match_list[1] = (char *)NULL;
355 path_list = (char *)NULL;
356 }
357 else
358 {
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");
364 path_index = 0;
365 }
366
367 while (path_list && path_list[path_index])
368 {
369 path_element = get_next_path_element (path_list, &path_index);
370
371 if (path_element == 0)
372 break;
373
374 match = find_in_path_element (name, path_element, flags, name_len, &dotinfo);
375
376 free (path_element);
377
378 if (match == 0)
379 continue;
380
381 if (match_index + 1 == match_list_size)
382 {
383 match_list_size += 10;
384 match_list = (char **)xrealloc (match_list, (match_list_size + 1) * sizeof (char *));
385 }
386
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;
391 }
392
393 /* We haven't returned any strings yet. */
394 match_index = 0;
395 }
396
397 match = match_list[match_index];
398
399 if (match)
400 match_index++;
401
402 return (match);
403 }
404
405 /* Turn PATH, a directory, and NAME, a filename, into a full pathname.
406 This allocates new memory and returns it. */
407 static char *
408 make_full_pathname (path, name, name_len)
409 char *path, *name;
410 int name_len;
411 {
412 char *full_path;
413 int path_len;
414
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);
420 return (full_path);
421 }
422
423 static char *
424 find_absolute_program (name, flags)
425 char *name;
426 int flags;
427 {
428 int st;
429
430 st = file_status (name);
431
432 /* If the file doesn't exist, quit now. */
433 if ((st & FS_EXISTS) == 0)
434 return ((char *)NULL);
435
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));
441
442 return ((char *)NULL);
443 }
444
445 static char *
446 find_in_path_element (name, path, flags, name_len, dotinfop)
447 char *name, *path;
448 int flags, name_len;
449 struct stat *dotinfop;
450 {
451 int status;
452 char *full_path, *xpath;
453
454 xpath = (*path == '~') ? bash_tilde_expand (path) : path;
455
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);
460
461 full_path = make_full_pathname (xpath, name, name_len);
462
463 status = file_status (full_path);
464
465 if (xpath != path)
466 free (xpath);
467
468 if ((status & FS_EXISTS) == 0)
469 {
470 free (full_path);
471 return ((char *)NULL);
472 }
473
474 /* The file exists. If the caller simply wants the first file, here it is. */
475 if (flags & FS_EXISTS)
476 return (full_path);
477
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)))
482 {
483 FREE (file_to_lose_on);
484 file_to_lose_on = (char *)NULL;
485 return (full_path);
486 }
487
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
490 we have found. */
491 if ((flags & FS_EXEC_PREFERRED) && file_to_lose_on == 0)
492 file_to_lose_on = savestring (full_path);
493
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)))
498 {
499 free (full_path);
500 return ((char *)NULL);
501 }
502 else
503 return (full_path);
504 }
505
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.
511 Some values are:
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.
517 */
518 static char *
519 find_user_command_in_path (name, path_list, flags)
520 char *name;
521 char *path_list;
522 int flags;
523 {
524 char *full_path, *path;
525 int path_index, name_len;
526 struct stat dotinfo;
527
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;
531
532 if (absolute_program (name))
533 {
534 full_path = find_absolute_program (name, flags);
535 return (full_path);
536 }
537
538 if (path_list == 0 || *path_list == '\0')
539 return (savestring (name)); /* XXX */
540
541 file_to_lose_on = (char *)NULL;
542 name_len = strlen (name);
543 stat (".", &dotinfo);
544 path_index = 0;
545
546 while (path_list[path_index])
547 {
548 /* Allow the user to interrupt out of a lengthy path search. */
549 QUIT;
550
551 path = get_next_path_element (path_list, &path_index);
552 if (path == 0)
553 break;
554
555 /* Side effects: sets dot_found_in_search, possibly sets
556 file_to_lose_on. */
557 full_path = find_in_path_element (name, path, flags, name_len, &dotinfo);
558 free (path);
559
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))
563 {
564 free (full_path);
565 continue;
566 }
567
568 if (full_path)
569 {
570 FREE (file_to_lose_on);
571 return (full_path);
572 }
573 }
574
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);
580 }