Imported from ../bash-2.05b.tar.gz.
[platform/upstream/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 }