blob: 330e39544d5529cbb0bba2c91621a27acf408482 [file] [log] [blame]
Jari Aaltocce855b1998-04-17 19:52:44 +00001/* findcmd.c -- Functions to search for commands by name. */
2
Jari Aalto31859422009-01-12 13:36:28 +00003/* Copyright (C) 1997-2009 Free Software Foundation, Inc.
Jari Aaltocce855b1998-04-17 19:52:44 +00004
5 This file is part of GNU Bash, the Bourne Again SHell.
6
Jari Aalto31859422009-01-12 13:36:28 +00007 Bash is free software: you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation, either version 3 of the License, or
10 (at your option) any later version.
Jari Aaltocce855b1998-04-17 19:52:44 +000011
Jari Aalto31859422009-01-12 13:36:28 +000012 Bash is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
Jari Aaltocce855b1998-04-17 19:52:44 +000016
17 You should have received a copy of the GNU General Public License
Jari Aalto31859422009-01-12 13:36:28 +000018 along with Bash. If not, see <http://www.gnu.org/licenses/>.
19*/
Jari Aaltocce855b1998-04-17 19:52:44 +000020
21#include "config.h"
22
23#include <stdio.h>
Jari Aaltof73dda02001-11-13 17:56:06 +000024#include "chartypes.h"
Jari Aaltocce855b1998-04-17 19:52:44 +000025#include "bashtypes.h"
Jari Aaltob80f6442004-07-27 13:29:18 +000026#if !defined (_MINIX) && defined (HAVE_SYS_FILE_H)
Jari Aaltocce855b1998-04-17 19:52:44 +000027# include <sys/file.h>
28#endif
29#include "filecntl.h"
30#include "posixstat.h"
31
32#if defined (HAVE_UNISTD_H)
33# include <unistd.h>
34#endif
Chet Ramey495aee42011-11-22 19:11:26 -050035#include <errno.h>
Jari Aaltocce855b1998-04-17 19:52:44 +000036
Jari Aaltocce855b1998-04-17 19:52:44 +000037#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"
Jari Aaltof73dda02001-11-13 17:56:06 +000045#include "findcmd.h" /* matching prototypes and declarations */
Jari Aaltocce855b1998-04-17 19:52:44 +000046
Chet Ramey495aee42011-11-22 19:11:26 -050047#if !defined (errno)
48extern int errno;
49#endif
50
Jari Aaltocce855b1998-04-17 19:52:44 +000051extern int posixly_correct;
52
53/* Static functions defined and used in this file. */
Jari Aaltof73dda02001-11-13 17:56:06 +000054static char *_find_user_command_internal __P((const char *, int));
55static char *find_user_command_internal __P((const char *, int));
56static char *find_user_command_in_path __P((const char *, char *, int));
57static char *find_in_path_element __P((const char *, char *, int, int, struct stat *));
58static char *find_absolute_program __P((const char *, int));
59
60static char *get_next_path_element __P((char *, int *));
Jari Aaltocce855b1998-04-17 19:52:44 +000061
62/* The file name which we would try to execute, except that it isn't
63 possible to execute it. This is the first file that matches the
64 name that we are looking for while we are searching $PATH for a
65 suitable one to execute. If we cannot find a suitable executable
66 file, then we use this one. */
67static char *file_to_lose_on;
68
69/* Non-zero if we should stat every command found in the hash table to
70 make sure it still exists. */
71int check_hashed_filenames;
72
73/* DOT_FOUND_IN_SEARCH becomes non-zero when find_user_command ()
74 encounters a `.' as the directory pathname while scanning the
75 list of possible pathnames; i.e., if `.' comes before the directory
76 containing the file of interest. */
77int dot_found_in_search = 0;
78
Jari Aaltocce855b1998-04-17 19:52:44 +000079/* Return some flags based on information about this file.
80 The EXISTS bit is non-zero if the file is found.
81 The EXECABLE bit is non-zero the file is executble.
82 Zero is returned if the file is not found. */
83int
84file_status (name)
Jari Aaltof73dda02001-11-13 17:56:06 +000085 const char *name;
Jari Aaltocce855b1998-04-17 19:52:44 +000086{
87 struct stat finfo;
Jari Aalto95732b42005-12-07 14:08:12 +000088 int r;
Jari Aaltocce855b1998-04-17 19:52:44 +000089
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
Jari Aalto95732b42005-12-07 14:08:12 +000099 r = FS_EXISTS;
100
Chet Ramey00018032011-11-21 20:51:19 -0500101#if defined (HAVE_EACCESS)
102 /* Use eaccess(2) if we have it to take things like ACLs and other
103 file access mechanisms into account. eaccess uses the effective
104 user and group IDs, not the real ones. We could use sh_eaccess,
105 but we don't want any special treatment for /dev/fd. */
106 if (eaccess (name, X_OK) == 0)
107 r |= FS_EXECABLE;
108 if (eaccess (name, R_OK) == 0)
109 r |= FS_READABLE;
110
111 return r;
112#elif defined (AFS)
Jari Aaltocce855b1998-04-17 19:52:44 +0000113 /* We have to use access(2) to determine access because AFS does not
114 support Unix file system semantics. This may produce wrong
115 answers for non-AFS files when ruid != euid. I hate AFS. */
Jari Aalto95732b42005-12-07 14:08:12 +0000116 if (access (name, X_OK) == 0)
117 r |= FS_EXECABLE;
118 if (access (name, R_OK) == 0)
119 r |= FS_READABLE;
120
121 return r;
Chet Ramey00018032011-11-21 20:51:19 -0500122#else /* !HAVE_EACCESS && !AFS */
Jari Aaltocce855b1998-04-17 19:52:44 +0000123
124 /* Find out if the file is actually executable. By definition, the
125 only other criteria is that the file has an execute bit set that
Jari Aalto95732b42005-12-07 14:08:12 +0000126 we can use. The same with whether or not a file is readable. */
Jari Aaltocce855b1998-04-17 19:52:44 +0000127
128 /* Root only requires execute permission for any of owner, group or
Jari Aalto95732b42005-12-07 14:08:12 +0000129 others to be able to exec a file, and can read any file. */
Jari Aaltocce855b1998-04-17 19:52:44 +0000130 if (current_user.euid == (uid_t)0)
131 {
Jari Aalto95732b42005-12-07 14:08:12 +0000132 r |= FS_READABLE;
133 if (finfo.st_mode & S_IXUGO)
134 r |= FS_EXECABLE;
135 return r;
Jari Aaltocce855b1998-04-17 19:52:44 +0000136 }
137
Jari Aalto95732b42005-12-07 14:08:12 +0000138 /* If we are the owner of the file, the owner bits apply. */
Jari Aaltob80f6442004-07-27 13:29:18 +0000139 if (current_user.euid == finfo.st_uid)
Jari Aalto95732b42005-12-07 14:08:12 +0000140 {
141 if (finfo.st_mode & S_IXUSR)
142 r |= FS_EXECABLE;
143 if (finfo.st_mode & S_IRUSR)
144 r |= FS_READABLE;
145 }
Jari Aaltocce855b1998-04-17 19:52:44 +0000146
147 /* If we are in the owning group, the group permissions apply. */
Jari Aaltob80f6442004-07-27 13:29:18 +0000148 else if (group_member (finfo.st_gid))
Jari Aalto95732b42005-12-07 14:08:12 +0000149 {
150 if (finfo.st_mode & S_IXGRP)
151 r |= FS_EXECABLE;
152 if (finfo.st_mode & S_IRGRP)
153 r |= FS_READABLE;
154 }
Jari Aaltocce855b1998-04-17 19:52:44 +0000155
Jari Aaltob80f6442004-07-27 13:29:18 +0000156 /* Else we check whether `others' have permission to execute the file */
157 else
Jari Aalto95732b42005-12-07 14:08:12 +0000158 {
159 if (finfo.st_mode & S_IXOTH)
160 r |= FS_EXECABLE;
161 if (finfo.st_mode & S_IROTH)
162 r |= FS_READABLE;
163 }
164
165 return r;
Jari Aaltocce855b1998-04-17 19:52:44 +0000166#endif /* !AFS */
167}
168
169/* Return non-zero if FILE exists and is executable.
170 Note that this function is the definition of what an
171 executable file is; do not change this unless YOU know
172 what an executable file is. */
173int
174executable_file (file)
Jari Aaltof73dda02001-11-13 17:56:06 +0000175 const char *file;
Jari Aaltocce855b1998-04-17 19:52:44 +0000176{
177 int s;
178
179 s = file_status (file);
Chet Ramey495aee42011-11-22 19:11:26 -0500180#if defined EISDIR
181 if (s & FS_DIRECTORY)
182 errno = EISDIR; /* let's see if we can improve error messages */
183#endif
Jari Aaltocce855b1998-04-17 19:52:44 +0000184 return ((s & FS_EXECABLE) && ((s & FS_DIRECTORY) == 0));
185}
186
187int
188is_directory (file)
Jari Aaltof73dda02001-11-13 17:56:06 +0000189 const char *file;
Jari Aaltocce855b1998-04-17 19:52:44 +0000190{
191 return (file_status (file) & FS_DIRECTORY);
192}
193
Jari Aalto28ef6c32001-04-06 19:14:31 +0000194int
195executable_or_directory (file)
Jari Aaltof73dda02001-11-13 17:56:06 +0000196 const char *file;
Jari Aalto28ef6c32001-04-06 19:14:31 +0000197{
198 int s;
199
200 s = file_status (file);
201 return ((s & FS_EXECABLE) || (s & FS_DIRECTORY));
202}
203
Jari Aaltocce855b1998-04-17 19:52:44 +0000204/* Locate the executable file referenced by NAME, searching along
205 the contents of the shell PATH variable. Return a new string
206 which is the full pathname to the file, or NULL if the file
207 couldn't be found. If a file is found that isn't executable,
208 and that is the only match, then return that. */
209char *
210find_user_command (name)
Jari Aaltof73dda02001-11-13 17:56:06 +0000211 const char *name;
Jari Aaltocce855b1998-04-17 19:52:44 +0000212{
213 return (find_user_command_internal (name, FS_EXEC_PREFERRED|FS_NODIRS));
214}
215
216/* Locate the file referenced by NAME, searching along the contents
217 of the shell PATH variable. Return a new string which is the full
218 pathname to the file, or NULL if the file couldn't be found. This
Jari Aalto95732b42005-12-07 14:08:12 +0000219 returns the first readable file found; designed to be used to look
220 for shell scripts or files to source. */
Jari Aaltocce855b1998-04-17 19:52:44 +0000221char *
222find_path_file (name)
Jari Aaltof73dda02001-11-13 17:56:06 +0000223 const char *name;
Jari Aaltocce855b1998-04-17 19:52:44 +0000224{
Jari Aalto95732b42005-12-07 14:08:12 +0000225 return (find_user_command_internal (name, FS_READABLE));
Jari Aaltocce855b1998-04-17 19:52:44 +0000226}
227
228static char *
229_find_user_command_internal (name, flags)
Jari Aaltof73dda02001-11-13 17:56:06 +0000230 const char *name;
Jari Aaltocce855b1998-04-17 19:52:44 +0000231 int flags;
232{
233 char *path_list, *cmd;
234 SHELL_VAR *var;
235
Jari Aalto7117c2d2002-07-17 14:10:11 +0000236 /* Search for the value of PATH in both the temporary environments and
Jari Aaltocce855b1998-04-17 19:52:44 +0000237 in the regular list of variables. */
238 if (var = find_variable_internal ("PATH", 1)) /* XXX could be array? */
239 path_list = value_cell (var);
240 else
241 path_list = (char *)NULL;
242
243 if (path_list == 0 || *path_list == '\0')
244 return (savestring (name));
245
246 cmd = find_user_command_in_path (name, path_list, flags);
247
Jari Aaltocce855b1998-04-17 19:52:44 +0000248 return (cmd);
249}
250
251static char *
252find_user_command_internal (name, flags)
Jari Aaltof73dda02001-11-13 17:56:06 +0000253 const char *name;
Jari Aaltocce855b1998-04-17 19:52:44 +0000254 int flags;
255{
256#ifdef __WIN32__
257 char *res, *dotexe;
258
Jari Aaltof73dda02001-11-13 17:56:06 +0000259 dotexe = (char *)xmalloc (strlen (name) + 5);
Jari Aaltocce855b1998-04-17 19:52:44 +0000260 strcpy (dotexe, name);
261 strcat (dotexe, ".exe");
262 res = _find_user_command_internal (dotexe, flags);
263 free (dotexe);
264 if (res == 0)
265 res = _find_user_command_internal (name, flags);
266 return res;
267#else
268 return (_find_user_command_internal (name, flags));
269#endif
270}
271
272/* Return the next element from PATH_LIST, a colon separated list of
273 paths. PATH_INDEX_POINTER is the address of an index into PATH_LIST;
274 the index is modified by this function.
275 Return the next element of PATH_LIST or NULL if there are no more. */
276static char *
277get_next_path_element (path_list, path_index_pointer)
278 char *path_list;
279 int *path_index_pointer;
280{
281 char *path;
282
283 path = extract_colon_unit (path_list, path_index_pointer);
284
Jari Aaltobb706242000-03-17 21:46:59 +0000285 if (path == 0)
Jari Aaltocce855b1998-04-17 19:52:44 +0000286 return (path);
287
Jari Aaltobb706242000-03-17 21:46:59 +0000288 if (*path == '\0')
Jari Aaltocce855b1998-04-17 19:52:44 +0000289 {
290 free (path);
291 path = savestring (".");
292 }
293
294 return (path);
295}
296
297/* Look for PATHNAME in $PATH. Returns either the hashed command
298 corresponding to PATHNAME or the first instance of PATHNAME found
299 in $PATH. Returns a newly-allocated string. */
300char *
301search_for_command (pathname)
Jari Aaltof73dda02001-11-13 17:56:06 +0000302 const char *pathname;
Jari Aaltocce855b1998-04-17 19:52:44 +0000303{
304 char *hashed_file, *command;
305 int temp_path, st;
306 SHELL_VAR *path;
307
308 hashed_file = command = (char *)NULL;
309
310 /* If PATH is in the temporary environment for this command, don't use the
311 hash table to search for the full pathname. */
Jari Aalto7117c2d2002-07-17 14:10:11 +0000312 path = find_variable_internal ("PATH", 1);
313 temp_path = path && tempvar_p (path);
314 if (temp_path == 0 && path)
315 path = (SHELL_VAR *)NULL;
Jari Aaltocce855b1998-04-17 19:52:44 +0000316
317 /* Don't waste time trying to find hashed data for a pathname
318 that is already completely specified or if we're using a command-
319 specific value for PATH. */
320 if (path == 0 && absolute_program (pathname) == 0)
Jari Aalto7117c2d2002-07-17 14:10:11 +0000321 hashed_file = phash_search (pathname);
Jari Aaltocce855b1998-04-17 19:52:44 +0000322
323 /* If a command found in the hash table no longer exists, we need to
324 look for it in $PATH. Thank you Posix.2. This forces us to stat
325 every command found in the hash table. */
326
327 if (hashed_file && (posixly_correct || check_hashed_filenames))
328 {
329 st = file_status (hashed_file);
Jari Aaltof1be6662008-11-18 13:15:12 +0000330 if ((st & (FS_EXISTS|FS_EXECABLE)) != (FS_EXISTS|FS_EXECABLE))
Jari Aaltocce855b1998-04-17 19:52:44 +0000331 {
Jari Aalto7117c2d2002-07-17 14:10:11 +0000332 phash_remove (pathname);
Jari Aaltocce855b1998-04-17 19:52:44 +0000333 free (hashed_file);
334 hashed_file = (char *)NULL;
335 }
336 }
337
338 if (hashed_file)
339 command = hashed_file;
340 else if (absolute_program (pathname))
341 /* A command containing a slash is not looked up in PATH or saved in
342 the hash table. */
343 command = savestring (pathname);
344 else
345 {
346 /* If $PATH is in the temporary environment, we've already retrieved
347 it, so don't bother trying again. */
348 if (temp_path)
Jari Aalto28ef6c32001-04-06 19:14:31 +0000349 {
Jari Aaltocce855b1998-04-17 19:52:44 +0000350 command = find_user_command_in_path (pathname, value_cell (path),
351 FS_EXEC_PREFERRED|FS_NODIRS);
Jari Aalto28ef6c32001-04-06 19:14:31 +0000352 }
Jari Aaltocce855b1998-04-17 19:52:44 +0000353 else
354 command = find_user_command (pathname);
355 if (command && hashing_enabled && temp_path == 0)
Jari Aalto7117c2d2002-07-17 14:10:11 +0000356 phash_insert ((char *)pathname, command, dot_found_in_search, 1); /* XXX fix const later */
Jari Aaltocce855b1998-04-17 19:52:44 +0000357 }
358 return (command);
359}
360
361char *
362user_command_matches (name, flags, state)
Jari Aaltof73dda02001-11-13 17:56:06 +0000363 const char *name;
Jari Aaltocce855b1998-04-17 19:52:44 +0000364 int flags, state;
365{
366 register int i;
367 int path_index, name_len;
368 char *path_list, *path_element, *match;
369 struct stat dotinfo;
370 static char **match_list = NULL;
371 static int match_list_size = 0;
372 static int match_index = 0;
373
374 if (state == 0)
375 {
376 /* Create the list of matches. */
377 if (match_list == 0)
378 {
379 match_list_size = 5;
Jari Aalto7117c2d2002-07-17 14:10:11 +0000380 match_list = strvec_create (match_list_size);
Jari Aaltocce855b1998-04-17 19:52:44 +0000381 }
382
383 /* Clear out the old match list. */
384 for (i = 0; i < match_list_size; i++)
385 match_list[i] = 0;
386
387 /* We haven't found any files yet. */
388 match_index = 0;
389
390 if (absolute_program (name))
391 {
392 match_list[0] = find_absolute_program (name, flags);
393 match_list[1] = (char *)NULL;
394 path_list = (char *)NULL;
395 }
396 else
397 {
398 name_len = strlen (name);
399 file_to_lose_on = (char *)NULL;
400 dot_found_in_search = 0;
401 stat (".", &dotinfo);
402 path_list = get_string_value ("PATH");
403 path_index = 0;
404 }
405
406 while (path_list && path_list[path_index])
407 {
408 path_element = get_next_path_element (path_list, &path_index);
409
410 if (path_element == 0)
411 break;
412
413 match = find_in_path_element (name, path_element, flags, name_len, &dotinfo);
414
415 free (path_element);
416
417 if (match == 0)
418 continue;
419
420 if (match_index + 1 == match_list_size)
421 {
422 match_list_size += 10;
Jari Aalto7117c2d2002-07-17 14:10:11 +0000423 match_list = strvec_resize (match_list, (match_list_size + 1));
Jari Aaltocce855b1998-04-17 19:52:44 +0000424 }
425
426 match_list[match_index++] = match;
427 match_list[match_index] = (char *)NULL;
428 FREE (file_to_lose_on);
429 file_to_lose_on = (char *)NULL;
430 }
431
432 /* We haven't returned any strings yet. */
433 match_index = 0;
434 }
435
436 match = match_list[match_index];
437
438 if (match)
439 match_index++;
440
441 return (match);
442}
443
Jari Aaltocce855b1998-04-17 19:52:44 +0000444static char *
445find_absolute_program (name, flags)
Jari Aaltof73dda02001-11-13 17:56:06 +0000446 const char *name;
Jari Aaltocce855b1998-04-17 19:52:44 +0000447 int flags;
448{
449 int st;
450
451 st = file_status (name);
452
453 /* If the file doesn't exist, quit now. */
454 if ((st & FS_EXISTS) == 0)
455 return ((char *)NULL);
456
457 /* If we only care about whether the file exists or not, return
458 this filename. Otherwise, maybe we care about whether this
459 file is executable. If it is, and that is what we want, return it. */
460 if ((flags & FS_EXISTS) || ((flags & FS_EXEC_ONLY) && (st & FS_EXECABLE)))
461 return (savestring (name));
462
Jari Aaltof73dda02001-11-13 17:56:06 +0000463 return (NULL);
Jari Aaltocce855b1998-04-17 19:52:44 +0000464}
465
466static char *
467find_in_path_element (name, path, flags, name_len, dotinfop)
Jari Aaltof73dda02001-11-13 17:56:06 +0000468 const char *name;
469 char *path;
Jari Aaltocce855b1998-04-17 19:52:44 +0000470 int flags, name_len;
471 struct stat *dotinfop;
472{
473 int status;
474 char *full_path, *xpath;
475
Jari Aalto7117c2d2002-07-17 14:10:11 +0000476 xpath = (*path == '~') ? bash_tilde_expand (path, 0) : path;
Jari Aaltocce855b1998-04-17 19:52:44 +0000477
478 /* Remember the location of "." in the path, in all its forms
479 (as long as they begin with a `.', e.g. `./.') */
480 if (dot_found_in_search == 0 && *xpath == '.')
481 dot_found_in_search = same_file (".", xpath, dotinfop, (struct stat *)NULL);
482
Jari Aaltobb706242000-03-17 21:46:59 +0000483 full_path = sh_makepath (xpath, name, 0);
Jari Aaltocce855b1998-04-17 19:52:44 +0000484
485 status = file_status (full_path);
486
487 if (xpath != path)
488 free (xpath);
489
490 if ((status & FS_EXISTS) == 0)
491 {
492 free (full_path);
493 return ((char *)NULL);
494 }
495
496 /* The file exists. If the caller simply wants the first file, here it is. */
497 if (flags & FS_EXISTS)
498 return (full_path);
499
Jari Aalto95732b42005-12-07 14:08:12 +0000500 /* If we have a readable file, and the caller wants a readable file, this
501 is it. */
502 if ((flags & FS_READABLE) && (status & FS_READABLE))
503 return (full_path);
504
Jari Aaltocce855b1998-04-17 19:52:44 +0000505 /* If the file is executable, then it satisfies the cases of
506 EXEC_ONLY and EXEC_PREFERRED. Return this file unconditionally. */
Jari Aalto95732b42005-12-07 14:08:12 +0000507 if ((status & FS_EXECABLE) && (flags & (FS_EXEC_ONLY|FS_EXEC_PREFERRED)) &&
Jari Aaltocce855b1998-04-17 19:52:44 +0000508 (((flags & FS_NODIRS) == 0) || ((status & FS_DIRECTORY) == 0)))
509 {
510 FREE (file_to_lose_on);
511 file_to_lose_on = (char *)NULL;
512 return (full_path);
513 }
514
515 /* The file is not executable, but it does exist. If we prefer
516 an executable, then remember this one if it is the first one
517 we have found. */
518 if ((flags & FS_EXEC_PREFERRED) && file_to_lose_on == 0)
519 file_to_lose_on = savestring (full_path);
520
521 /* If we want only executable files, or we don't want directories and
Jari Aalto95732b42005-12-07 14:08:12 +0000522 this file is a directory, or we want a readable file and this file
523 isn't readable, fail. */
524 if ((flags & (FS_EXEC_ONLY|FS_EXEC_PREFERRED)) ||
525 ((flags & FS_NODIRS) && (status & FS_DIRECTORY)) ||
526 ((flags & FS_READABLE) && (status & FS_READABLE) == 0))
Jari Aaltocce855b1998-04-17 19:52:44 +0000527 {
528 free (full_path);
529 return ((char *)NULL);
530 }
531 else
532 return (full_path);
533}
534
535/* This does the dirty work for find_user_command_internal () and
536 user_command_matches ().
537 NAME is the name of the file to search for.
538 PATH_LIST is a colon separated list of directories to search.
539 FLAGS contains bit fields which control the files which are eligible.
540 Some values are:
541 FS_EXEC_ONLY: The file must be an executable to be found.
542 FS_EXEC_PREFERRED: If we can't find an executable, then the
543 the first file matching NAME will do.
544 FS_EXISTS: The first file found will do.
545 FS_NODIRS: Don't find any directories.
546*/
547static char *
548find_user_command_in_path (name, path_list, flags)
Jari Aaltof73dda02001-11-13 17:56:06 +0000549 const char *name;
Jari Aaltocce855b1998-04-17 19:52:44 +0000550 char *path_list;
551 int flags;
552{
553 char *full_path, *path;
554 int path_index, name_len;
555 struct stat dotinfo;
556
557 /* We haven't started looking, so we certainly haven't seen
558 a `.' as the directory path yet. */
559 dot_found_in_search = 0;
560
561 if (absolute_program (name))
562 {
563 full_path = find_absolute_program (name, flags);
564 return (full_path);
565 }
566
567 if (path_list == 0 || *path_list == '\0')
568 return (savestring (name)); /* XXX */
569
570 file_to_lose_on = (char *)NULL;
571 name_len = strlen (name);
572 stat (".", &dotinfo);
573 path_index = 0;
574
575 while (path_list[path_index])
576 {
577 /* Allow the user to interrupt out of a lengthy path search. */
578 QUIT;
579
580 path = get_next_path_element (path_list, &path_index);
581 if (path == 0)
582 break;
583
584 /* Side effects: sets dot_found_in_search, possibly sets
585 file_to_lose_on. */
586 full_path = find_in_path_element (name, path, flags, name_len, &dotinfo);
587 free (path);
588
589 /* This should really be in find_in_path_element, but there isn't the
590 right combination of flags. */
591 if (full_path && is_directory (full_path))
592 {
593 free (full_path);
594 continue;
595 }
596
597 if (full_path)
598 {
599 FREE (file_to_lose_on);
600 return (full_path);
601 }
602 }
603
604 /* We didn't find exactly what the user was looking for. Return
605 the contents of FILE_TO_LOSE_ON which is NULL when the search
606 required an executable, or non-NULL if a file was found and the
Jari Aalto28ef6c32001-04-06 19:14:31 +0000607 search would accept a non-executable as a last resort. If the
608 caller specified FS_NODIRS, and file_to_lose_on is a directory,
609 return NULL. */
610 if (file_to_lose_on && (flags & FS_NODIRS) && is_directory (file_to_lose_on))
611 {
612 free (file_to_lose_on);
613 file_to_lose_on = (char *)NULL;
614 }
615
Jari Aaltocce855b1998-04-17 19:52:44 +0000616 return (file_to_lose_on);
617}