]> git.ipfire.org Git - thirdparty/bash.git/blame - findcmd.c
changes to make EOF state available to readline applications; fix for command substit...
[thirdparty/bash.git] / findcmd.c
CommitLineData
cce855bc
JA
1/* findcmd.c -- Functions to search for commands by name. */
2
b06200f7 3/* Copyright (C) 1997-2021 Free Software Foundation, Inc.
cce855bc
JA
4
5 This file is part of GNU Bash, the Bourne Again SHell.
6
2e4498b3
CR
7 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.
cce855bc 11
2e4498b3
CR
12 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.
cce855bc
JA
16
17 You should have received a copy of the GNU General Public License
2e4498b3
CR
18 along with Bash. If not, see <http://www.gnu.org/licenses/>.
19*/
cce855bc
JA
20
21#include "config.h"
22
23#include <stdio.h>
f73dda09 24#include "chartypes.h"
cce855bc 25#include "bashtypes.h"
d3a24ed2 26#if !defined (_MINIX) && defined (HAVE_SYS_FILE_H)
cce855bc
JA
27# 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
aad3dfa2 35#include <errno.h>
cce855bc 36
cce855bc
JA
37#include "bashansi.h"
38
39#include "memalloc.h"
40#include "shell.h"
ec157dfe 41#include "execute_cmd.h"
cce855bc
JA
42#include "flags.h"
43#include "hashlib.h"
44#include "pathexp.h"
45#include "hashcmd.h"
f73dda09 46#include "findcmd.h" /* matching prototypes and declarations */
cce855bc 47
ff7c092e
CR
48#include <glob/strmatch.h>
49
aad3dfa2
CR
50#if !defined (errno)
51extern int errno;
52#endif
53
cce855bc 54/* Static functions defined and used in this file. */
ab309487
CR
55static char *_find_user_command_internal PARAMS((const char *, int));
56static char *find_user_command_internal PARAMS((const char *, int));
57static char *find_user_command_in_path PARAMS((const char *, char *, int));
58static char *find_in_path_element PARAMS((const char *, char *, int, int, struct stat *));
59static char *find_absolute_program PARAMS((const char *, int));
f73dda09 60
ab309487 61static char *get_next_path_element PARAMS((char *, int *));
cce855bc
JA
62
63/* The file name which we would try to execute, except that it isn't
64 possible to execute it. This is the first file that matches the
65 name that we are looking for while we are searching $PATH for a
66 suitable one to execute. If we cannot find a suitable executable
67 file, then we use this one. */
68static char *file_to_lose_on;
69
70/* Non-zero if we should stat every command found in the hash table to
71 make sure it still exists. */
25a0eacf 72int check_hashed_filenames = CHECKHASH_DEFAULT;
cce855bc
JA
73
74/* DOT_FOUND_IN_SEARCH becomes non-zero when find_user_command ()
75 encounters a `.' as the directory pathname while scanning the
76 list of possible pathnames; i.e., if `.' comes before the directory
77 containing the file of interest. */
78int dot_found_in_search = 0;
79
ff7c092e
CR
80/* Set up EXECIGNORE; a blacklist of patterns that executable files should not
81 match. */
82static struct ignorevar execignore =
83{
84 "EXECIGNORE",
85 NULL,
86 0,
87 NULL,
88 NULL
89};
90
91void
92setup_exec_ignore (varname)
93 char *varname;
94{
95 setup_ignore_patterns (&execignore);
96}
97
98static int
99exec_name_should_ignore (name)
100 const char *name;
101{
102 struct ign *p;
103
104 for (p = execignore.ignores; p && p->val; p++)
e6f5e0c8 105 if (strmatch (p->val, (char *)name, FNMATCH_EXTFLAG|FNM_CASEFOLD) != FNM_NOMATCH)
ff7c092e
CR
106 return 1;
107 return 0;
108}
109
cce855bc
JA
110/* Return some flags based on information about this file.
111 The EXISTS bit is non-zero if the file is found.
112 The EXECABLE bit is non-zero the file is executble.
113 Zero is returned if the file is not found. */
114int
115file_status (name)
f73dda09 116 const char *name;
cce855bc
JA
117{
118 struct stat finfo;
cdb32d45 119 int r;
cce855bc
JA
120
121 /* Determine whether this file exists or not. */
122 if (stat (name, &finfo) < 0)
123 return (0);
124
125 /* If the file is a directory, then it is not "executable" in the
126 sense of the shell. */
127 if (S_ISDIR (finfo.st_mode))
128 return (FS_EXISTS|FS_DIRECTORY);
129
cdb32d45
CR
130 r = FS_EXISTS;
131
4301bca7
CR
132#if defined (HAVE_EACCESS)
133 /* Use eaccess(2) if we have it to take things like ACLs and other
134 file access mechanisms into account. eaccess uses the effective
135 user and group IDs, not the real ones. We could use sh_eaccess,
136 but we don't want any special treatment for /dev/fd. */
ff7c092e 137 if (exec_name_should_ignore (name) == 0 && eaccess (name, X_OK) == 0)
4301bca7
CR
138 r |= FS_EXECABLE;
139 if (eaccess (name, R_OK) == 0)
140 r |= FS_READABLE;
141
142 return r;
143#elif defined (AFS)
cce855bc
JA
144 /* We have to use access(2) to determine access because AFS does not
145 support Unix file system semantics. This may produce wrong
146 answers for non-AFS files when ruid != euid. I hate AFS. */
ff7c092e 147 if (exec_name_should_ignore (name) == 0 && access (name, X_OK) == 0)
cdb32d45
CR
148 r |= FS_EXECABLE;
149 if (access (name, R_OK) == 0)
150 r |= FS_READABLE;
151
152 return r;
4301bca7 153#else /* !HAVE_EACCESS && !AFS */
cce855bc
JA
154
155 /* Find out if the file is actually executable. By definition, the
156 only other criteria is that the file has an execute bit set that
cdb32d45 157 we can use. The same with whether or not a file is readable. */
cce855bc
JA
158
159 /* Root only requires execute permission for any of owner, group or
cdb32d45 160 others to be able to exec a file, and can read any file. */
cce855bc
JA
161 if (current_user.euid == (uid_t)0)
162 {
cdb32d45 163 r |= FS_READABLE;
ff7c092e 164 if (exec_name_should_ignore (name) == 0 && (finfo.st_mode & S_IXUGO))
cdb32d45
CR
165 r |= FS_EXECABLE;
166 return r;
cce855bc
JA
167 }
168
cdb32d45 169 /* If we are the owner of the file, the owner bits apply. */
d3a24ed2 170 if (current_user.euid == finfo.st_uid)
cdb32d45 171 {
ff7c092e 172 if (exec_name_should_ignore (name) == 0 && (finfo.st_mode & S_IXUSR))
cdb32d45
CR
173 r |= FS_EXECABLE;
174 if (finfo.st_mode & S_IRUSR)
175 r |= FS_READABLE;
176 }
cce855bc
JA
177
178 /* If we are in the owning group, the group permissions apply. */
d3a24ed2 179 else if (group_member (finfo.st_gid))
cdb32d45 180 {
ff7c092e 181 if (exec_name_should_ignore (name) == 0 && (finfo.st_mode & S_IXGRP))
cdb32d45
CR
182 r |= FS_EXECABLE;
183 if (finfo.st_mode & S_IRGRP)
184 r |= FS_READABLE;
185 }
cce855bc 186
d3a24ed2
CR
187 /* Else we check whether `others' have permission to execute the file */
188 else
cdb32d45 189 {
4dd820b4 190 if (exec_name_should_ignore (name) == 0 && finfo.st_mode & S_IXOTH)
cdb32d45
CR
191 r |= FS_EXECABLE;
192 if (finfo.st_mode & S_IROTH)
193 r |= FS_READABLE;
194 }
195
196 return r;
cce855bc
JA
197#endif /* !AFS */
198}
199
200/* Return non-zero if FILE exists and is executable.
201 Note that this function is the definition of what an
202 executable file is; do not change this unless YOU know
203 what an executable file is. */
204int
205executable_file (file)
f73dda09 206 const char *file;
cce855bc
JA
207{
208 int s;
209
210 s = file_status (file);
85ec0778 211#if defined (EISDIR)
aad3dfa2
CR
212 if (s & FS_DIRECTORY)
213 errno = EISDIR; /* let's see if we can improve error messages */
214#endif
cce855bc
JA
215 return ((s & FS_EXECABLE) && ((s & FS_DIRECTORY) == 0));
216}
217
218int
219is_directory (file)
f73dda09 220 const char *file;
cce855bc
JA
221{
222 return (file_status (file) & FS_DIRECTORY);
223}
224
28ef6c31
JA
225int
226executable_or_directory (file)
f73dda09 227 const char *file;
28ef6c31
JA
228{
229 int s;
230
231 s = file_status (file);
232 return ((s & FS_EXECABLE) || (s & FS_DIRECTORY));
233}
234
cce855bc
JA
235/* Locate the executable file referenced by NAME, searching along
236 the contents of the shell PATH variable. Return a new string
237 which is the full pathname to the file, or NULL if the file
238 couldn't be found. If a file is found that isn't executable,
239 and that is the only match, then return that. */
240char *
241find_user_command (name)
f73dda09 242 const char *name;
cce855bc
JA
243{
244 return (find_user_command_internal (name, FS_EXEC_PREFERRED|FS_NODIRS));
245}
246
247/* Locate the file referenced by NAME, searching along the contents
248 of the shell PATH variable. Return a new string which is the full
249 pathname to the file, or NULL if the file couldn't be found. This
cdb32d45
CR
250 returns the first readable file found; designed to be used to look
251 for shell scripts or files to source. */
cce855bc
JA
252char *
253find_path_file (name)
f73dda09 254 const char *name;
cce855bc 255{
cdb32d45 256 return (find_user_command_internal (name, FS_READABLE));
cce855bc
JA
257}
258
259static char *
260_find_user_command_internal (name, flags)
f73dda09 261 const char *name;
cce855bc
JA
262 int flags;
263{
264 char *path_list, *cmd;
265 SHELL_VAR *var;
266
7117c2d2 267 /* Search for the value of PATH in both the temporary environments and
cce855bc 268 in the regular list of variables. */
348a457e 269 if (var = find_variable_tempenv ("PATH")) /* XXX could be array? */
cce855bc
JA
270 path_list = value_cell (var);
271 else
272 path_list = (char *)NULL;
273
274 if (path_list == 0 || *path_list == '\0')
275 return (savestring (name));
276
277 cmd = find_user_command_in_path (name, path_list, flags);
278
cce855bc
JA
279 return (cmd);
280}
281
282static char *
283find_user_command_internal (name, flags)
f73dda09 284 const char *name;
cce855bc
JA
285 int flags;
286{
287#ifdef __WIN32__
288 char *res, *dotexe;
289
f73dda09 290 dotexe = (char *)xmalloc (strlen (name) + 5);
cce855bc
JA
291 strcpy (dotexe, name);
292 strcat (dotexe, ".exe");
293 res = _find_user_command_internal (dotexe, flags);
294 free (dotexe);
295 if (res == 0)
296 res = _find_user_command_internal (name, flags);
297 return res;
298#else
299 return (_find_user_command_internal (name, flags));
300#endif
301}
302
303/* Return the next element from PATH_LIST, a colon separated list of
304 paths. PATH_INDEX_POINTER is the address of an index into PATH_LIST;
305 the index is modified by this function.
306 Return the next element of PATH_LIST or NULL if there are no more. */
307static char *
308get_next_path_element (path_list, path_index_pointer)
309 char *path_list;
310 int *path_index_pointer;
311{
312 char *path;
313
314 path = extract_colon_unit (path_list, path_index_pointer);
315
bb70624e 316 if (path == 0)
cce855bc
JA
317 return (path);
318
bb70624e 319 if (*path == '\0')
cce855bc
JA
320 {
321 free (path);
322 path = savestring (".");
323 }
324
325 return (path);
326}
327
328/* Look for PATHNAME in $PATH. Returns either the hashed command
329 corresponding to PATHNAME or the first instance of PATHNAME found
1573ba78 330 in $PATH. If (FLAGS&CMDSRCH_HASH) is non-zero, insert the instance of
b06200f7
CR
331 PATHNAME found in $PATH into the command hash table.
332 If (FLAGS&CMDSRCH_STDPATH) is non-zero, we are running in a `command -p'
333 environment and should use the Posix standard path.
1573ba78 334 Returns a newly-allocated string. */
cce855bc 335char *
5a318736 336search_for_command (pathname, flags)
f73dda09 337 const char *pathname;
5a318736 338 int flags;
cce855bc 339{
124d67cd 340 char *hashed_file, *command, *path_list;
cce855bc
JA
341 int temp_path, st;
342 SHELL_VAR *path;
343
344 hashed_file = command = (char *)NULL;
345
346 /* If PATH is in the temporary environment for this command, don't use the
347 hash table to search for the full pathname. */
348a457e 348 path = find_variable_tempenv ("PATH");
7117c2d2 349 temp_path = path && tempvar_p (path);
cce855bc
JA
350
351 /* Don't waste time trying to find hashed data for a pathname
352 that is already completely specified or if we're using a command-
353 specific value for PATH. */
b06200f7 354 if (temp_path == 0 && (flags & CMDSRCH_STDPATH) == 0 && absolute_program (pathname) == 0)
7117c2d2 355 hashed_file = phash_search (pathname);
cce855bc
JA
356
357 /* If a command found in the hash table no longer exists, we need to
358 look for it in $PATH. Thank you Posix.2. This forces us to stat
359 every command found in the hash table. */
360
361 if (hashed_file && (posixly_correct || check_hashed_filenames))
362 {
363 st = file_status (hashed_file);
d3ad40de 364 if ((st & (FS_EXISTS|FS_EXECABLE)) != (FS_EXISTS|FS_EXECABLE))
cce855bc 365 {
7117c2d2 366 phash_remove (pathname);
cce855bc
JA
367 free (hashed_file);
368 hashed_file = (char *)NULL;
369 }
370 }
371
372 if (hashed_file)
373 command = hashed_file;
374 else if (absolute_program (pathname))
375 /* A command containing a slash is not looked up in PATH or saved in
376 the hash table. */
377 command = savestring (pathname);
378 else
379 {
1573ba78 380 if (flags & CMDSRCH_STDPATH)
124d67cd 381 path_list = conf_standard_path ();
1573ba78 382 else if (temp_path || path)
124d67cd 383 path_list = value_cell (path);
1573ba78 384 else
124d67cd 385 path_list = 0;
1573ba78 386
124d67cd 387 command = find_user_command_in_path (pathname, path_list, FS_EXEC_PREFERRED|FS_NODIRS);
1573ba78
CR
388
389 if (command && hashing_enabled && temp_path == 0 && (flags & CMDSRCH_HASH))
28ef6c31 390 {
1573ba78
CR
391 /* If we found the full pathname the same as the command name, the
392 command probably doesn't exist. Don't put it into the hash
b06200f7 393 table unless it's an executable file in the current directory. */
1573ba78
CR
394 if (STREQ (command, pathname))
395 {
396 st = file_status (command);
397 if (st & FS_EXECABLE)
398 phash_insert ((char *)pathname, command, dot_found_in_search, 1);
399 }
03e4274f
CR
400 /* If we're in posix mode, don't add files without the execute bit
401 to the hash table. */
a9cf0031 402 else if (posixly_correct || check_hashed_filenames)
03e4274f
CR
403 {
404 st = file_status (command);
405 if (st & FS_EXECABLE)
406 phash_insert ((char *)pathname, command, dot_found_in_search, 1);
407 }
1573ba78
CR
408 else
409 phash_insert ((char *)pathname, command, dot_found_in_search, 1);
28ef6c31 410 }
1573ba78
CR
411
412 if (flags & CMDSRCH_STDPATH)
124d67cd 413 free (path_list);
cce855bc 414 }
1573ba78 415
cce855bc
JA
416 return (command);
417}
418
419char *
420user_command_matches (name, flags, state)
f73dda09 421 const char *name;
cce855bc
JA
422 int flags, state;
423{
424 register int i;
425 int path_index, name_len;
426 char *path_list, *path_element, *match;
427 struct stat dotinfo;
428 static char **match_list = NULL;
429 static int match_list_size = 0;
430 static int match_index = 0;
431
432 if (state == 0)
433 {
434 /* Create the list of matches. */
435 if (match_list == 0)
436 {
437 match_list_size = 5;
7117c2d2 438 match_list = strvec_create (match_list_size);
cce855bc
JA
439 }
440
441 /* Clear out the old match list. */
442 for (i = 0; i < match_list_size; i++)
443 match_list[i] = 0;
444
445 /* We haven't found any files yet. */
446 match_index = 0;
447
448 if (absolute_program (name))
449 {
450 match_list[0] = find_absolute_program (name, flags);
451 match_list[1] = (char *)NULL;
452 path_list = (char *)NULL;
453 }
454 else
455 {
456 name_len = strlen (name);
457 file_to_lose_on = (char *)NULL;
458 dot_found_in_search = 0;
631b20c6
CR
459 if (stat (".", &dotinfo) < 0)
460 dotinfo.st_dev = dotinfo.st_ino = 0; /* so same_file won't match */
cce855bc
JA
461 path_list = get_string_value ("PATH");
462 path_index = 0;
463 }
464
465 while (path_list && path_list[path_index])
466 {
467 path_element = get_next_path_element (path_list, &path_index);
468
469 if (path_element == 0)
470 break;
471
472 match = find_in_path_element (name, path_element, flags, name_len, &dotinfo);
473
474 free (path_element);
475
476 if (match == 0)
477 continue;
478
479 if (match_index + 1 == match_list_size)
480 {
481 match_list_size += 10;
7117c2d2 482 match_list = strvec_resize (match_list, (match_list_size + 1));
cce855bc
JA
483 }
484
485 match_list[match_index++] = match;
486 match_list[match_index] = (char *)NULL;
487 FREE (file_to_lose_on);
488 file_to_lose_on = (char *)NULL;
489 }
490
491 /* We haven't returned any strings yet. */
492 match_index = 0;
493 }
494
495 match = match_list[match_index];
496
497 if (match)
498 match_index++;
499
500 return (match);
501}
502
cce855bc
JA
503static char *
504find_absolute_program (name, flags)
f73dda09 505 const char *name;
cce855bc
JA
506 int flags;
507{
508 int st;
509
510 st = file_status (name);
511
512 /* If the file doesn't exist, quit now. */
513 if ((st & FS_EXISTS) == 0)
514 return ((char *)NULL);
515
516 /* If we only care about whether the file exists or not, return
517 this filename. Otherwise, maybe we care about whether this
518 file is executable. If it is, and that is what we want, return it. */
519 if ((flags & FS_EXISTS) || ((flags & FS_EXEC_ONLY) && (st & FS_EXECABLE)))
520 return (savestring (name));
521
f73dda09 522 return (NULL);
cce855bc
JA
523}
524
525static char *
526find_in_path_element (name, path, flags, name_len, dotinfop)
f73dda09
JA
527 const char *name;
528 char *path;
cce855bc
JA
529 int flags, name_len;
530 struct stat *dotinfop;
531{
532 int status;
533 char *full_path, *xpath;
534
e198129d 535 xpath = (posixly_correct == 0 && *path == '~') ? bash_tilde_expand (path, 0) : path;
cce855bc
JA
536
537 /* Remember the location of "." in the path, in all its forms
538 (as long as they begin with a `.', e.g. `./.') */
b06200f7
CR
539 /* We could also do this or something similar for all relative pathnames
540 found while searching PATH. */
cce855bc
JA
541 if (dot_found_in_search == 0 && *xpath == '.')
542 dot_found_in_search = same_file (".", xpath, dotinfop, (struct stat *)NULL);
543
bb70624e 544 full_path = sh_makepath (xpath, name, 0);
cce855bc
JA
545
546 status = file_status (full_path);
547
548 if (xpath != path)
549 free (xpath);
550
551 if ((status & FS_EXISTS) == 0)
552 {
553 free (full_path);
554 return ((char *)NULL);
555 }
556
557 /* The file exists. If the caller simply wants the first file, here it is. */
558 if (flags & FS_EXISTS)
559 return (full_path);
560
cdb32d45
CR
561 /* If we have a readable file, and the caller wants a readable file, this
562 is it. */
563 if ((flags & FS_READABLE) && (status & FS_READABLE))
564 return (full_path);
565
cce855bc
JA
566 /* If the file is executable, then it satisfies the cases of
567 EXEC_ONLY and EXEC_PREFERRED. Return this file unconditionally. */
cdb32d45 568 if ((status & FS_EXECABLE) && (flags & (FS_EXEC_ONLY|FS_EXEC_PREFERRED)) &&
cce855bc
JA
569 (((flags & FS_NODIRS) == 0) || ((status & FS_DIRECTORY) == 0)))
570 {
571 FREE (file_to_lose_on);
572 file_to_lose_on = (char *)NULL;
573 return (full_path);
574 }
575
576 /* The file is not executable, but it does exist. If we prefer
577 an executable, then remember this one if it is the first one
578 we have found. */
4dd820b4 579 if ((flags & FS_EXEC_PREFERRED) && file_to_lose_on == 0 && exec_name_should_ignore (full_path) == 0)
cce855bc
JA
580 file_to_lose_on = savestring (full_path);
581
582 /* If we want only executable files, or we don't want directories and
cdb32d45
CR
583 this file is a directory, or we want a readable file and this file
584 isn't readable, fail. */
585 if ((flags & (FS_EXEC_ONLY|FS_EXEC_PREFERRED)) ||
586 ((flags & FS_NODIRS) && (status & FS_DIRECTORY)) ||
587 ((flags & FS_READABLE) && (status & FS_READABLE) == 0))
cce855bc
JA
588 {
589 free (full_path);
590 return ((char *)NULL);
591 }
592 else
593 return (full_path);
594}
595
596/* This does the dirty work for find_user_command_internal () and
597 user_command_matches ().
598 NAME is the name of the file to search for.
599 PATH_LIST is a colon separated list of directories to search.
600 FLAGS contains bit fields which control the files which are eligible.
601 Some values are:
602 FS_EXEC_ONLY: The file must be an executable to be found.
603 FS_EXEC_PREFERRED: If we can't find an executable, then the
604 the first file matching NAME will do.
605 FS_EXISTS: The first file found will do.
606 FS_NODIRS: Don't find any directories.
607*/
608static char *
609find_user_command_in_path (name, path_list, flags)
f73dda09 610 const char *name;
cce855bc
JA
611 char *path_list;
612 int flags;
613{
614 char *full_path, *path;
615 int path_index, name_len;
616 struct stat dotinfo;
617
618 /* We haven't started looking, so we certainly haven't seen
619 a `.' as the directory path yet. */
620 dot_found_in_search = 0;
621
622 if (absolute_program (name))
623 {
624 full_path = find_absolute_program (name, flags);
625 return (full_path);
626 }
627
628 if (path_list == 0 || *path_list == '\0')
629 return (savestring (name)); /* XXX */
630
631 file_to_lose_on = (char *)NULL;
632 name_len = strlen (name);
631b20c6
CR
633 if (stat (".", &dotinfo) < 0)
634 dotinfo.st_dev = dotinfo.st_ino = 0;
cce855bc
JA
635 path_index = 0;
636
637 while (path_list[path_index])
638 {
639 /* Allow the user to interrupt out of a lengthy path search. */
640 QUIT;
641
642 path = get_next_path_element (path_list, &path_index);
643 if (path == 0)
644 break;
645
646 /* Side effects: sets dot_found_in_search, possibly sets
647 file_to_lose_on. */
648 full_path = find_in_path_element (name, path, flags, name_len, &dotinfo);
649 free (path);
650
651 /* This should really be in find_in_path_element, but there isn't the
652 right combination of flags. */
653 if (full_path && is_directory (full_path))
654 {
655 free (full_path);
656 continue;
657 }
658
659 if (full_path)
660 {
661 FREE (file_to_lose_on);
662 return (full_path);
663 }
664 }
665
666 /* We didn't find exactly what the user was looking for. Return
667 the contents of FILE_TO_LOSE_ON which is NULL when the search
668 required an executable, or non-NULL if a file was found and the
28ef6c31
JA
669 search would accept a non-executable as a last resort. If the
670 caller specified FS_NODIRS, and file_to_lose_on is a directory,
671 return NULL. */
672 if (file_to_lose_on && (flags & FS_NODIRS) && is_directory (file_to_lose_on))
673 {
674 free (file_to_lose_on);
675 file_to_lose_on = (char *)NULL;
676 }
677
cce855bc
JA
678 return (file_to_lose_on);
679}
1573ba78
CR
680
681/* External interface to find a command given a $PATH. Separate from
682 find_user_command_in_path to allow future customization. */
683char *
684find_in_path (name, path_list, flags)
685 const char *name;
686 char *path_list;
687 int flags;
688{
689 return (find_user_command_in_path (name, path_list, flags));
690}