Imported from ../bash-3.1.tar.gz.
[platform/upstream/bash.git] / findcmd.c
index 09ad2fa..6f5ff85 100644 (file)
--- a/findcmd.c
+++ b/findcmd.c
@@ -72,11 +72,6 @@ int check_hashed_filenames;
    containing the file of interest. */
 int dot_found_in_search = 0;
 
-#define u_mode_bits(x) (((x) & 0000700) >> 6)
-#define g_mode_bits(x) (((x) & 0000070) >> 3)
-#define o_mode_bits(x) (((x) & 0000007) >> 0)
-#define X_BIT(x) ((x) & 1)
-
 /* Return some flags based on information about this file.
    The EXISTS bit is non-zero if the file is found.
    The EXECABLE bit is non-zero the file is executble.
@@ -86,6 +81,7 @@ file_status (name)
      const char *name;
 {
   struct stat finfo;
+  int r;
 
   /* Determine whether this file exists or not. */
   if (stat (name, &finfo) < 0)
@@ -96,41 +92,62 @@ file_status (name)
   if (S_ISDIR (finfo.st_mode))
     return (FS_EXISTS|FS_DIRECTORY);
 
+  r = FS_EXISTS;
+
 #if defined (AFS)
   /* We have to use access(2) to determine access because AFS does not
      support Unix file system semantics.  This may produce wrong
      answers for non-AFS files when ruid != euid.  I hate AFS. */
-  return ((access (name, X_OK) == 0) ? (FS_EXISTS|FS_EXECABLE) : FS_EXISTS);
+  if (access (name, X_OK) == 0)
+    r |= FS_EXECABLE;
+  if (access (name, R_OK) == 0)
+    r |= FS_READABLE;
+
+  return r;
 #else /* !AFS */
 
   /* Find out if the file is actually executable.  By definition, the
      only other criteria is that the file has an execute bit set that
-     we can use. */
+     we can use.  The same with whether or not a file is readable. */
 
   /* Root only requires execute permission for any of owner, group or
-     others to be able to exec a file. */
+     others to be able to exec a file, and can read any file. */
   if (current_user.euid == (uid_t)0)
     {
-      int bits;
-
-      bits = (u_mode_bits (finfo.st_mode) |
-             g_mode_bits (finfo.st_mode) |
-             o_mode_bits (finfo.st_mode));
-
-      return ((X_BIT (bits)) ? (FS_EXISTS | FS_EXECABLE) : FS_EXISTS);
+      r |= FS_READABLE;
+      if (finfo.st_mode & S_IXUGO)
+       r |= FS_EXECABLE;
+      return r;
     }
 
-  /* If we are the owner of the file, the owner execute bit applies. */
+  /* If we are the owner of the file, the owner bits apply. */
   if (current_user.euid == finfo.st_uid)
-    return ((X_BIT (u_mode_bits (finfo.st_mode))) ? (FS_EXISTS | FS_EXECABLE) : FS_EXISTS);
+    {
+      if (finfo.st_mode & S_IXUSR)
+       r |= FS_EXECABLE;
+      if (finfo.st_mode & S_IRUSR)
+       r |= FS_READABLE;
+    }
 
   /* If we are in the owning group, the group permissions apply. */
   else if (group_member (finfo.st_gid))
-    return ((X_BIT (g_mode_bits (finfo.st_mode))) ? (FS_EXISTS | FS_EXECABLE) : FS_EXISTS);
+    {
+      if (finfo.st_mode & S_IXGRP)
+       r |= FS_EXECABLE;
+      if (finfo.st_mode & S_IRGRP)
+       r |= FS_READABLE;
+    }
 
   /* Else we check whether `others' have permission to execute the file */
   else
-    return ((X_BIT (o_mode_bits (finfo.st_mode))) ? (FS_EXISTS | FS_EXECABLE) : FS_EXISTS);
+    {
+      if (finfo.st_mode & S_IXOTH)
+       r |= FS_EXECABLE;
+      if (finfo.st_mode & S_IROTH)
+       r |= FS_READABLE;
+    }
+
+  return r;
 #endif /* !AFS */
 }
 
@@ -180,12 +197,13 @@ find_user_command (name)
 /* Locate the file referenced by NAME, searching along the contents
    of the shell PATH variable.  Return a new string which is the full
    pathname to the file, or NULL if the file couldn't be found.  This
-   returns the first file found. */
+   returns the first readable file found; designed to be used to look
+   for shell scripts or files to source. */
 char *
 find_path_file (name)
      const char *name;
 {
-  return (find_user_command_internal (name, FS_EXISTS));
+  return (find_user_command_internal (name, FS_READABLE));
 }
 
 static char *
@@ -460,9 +478,14 @@ find_in_path_element (name, path, flags, name_len, dotinfop)
   if (flags & FS_EXISTS)
     return (full_path);
 
+  /* If we have a readable file, and the caller wants a readable file, this
+     is it. */
+  if ((flags & FS_READABLE) && (status & FS_READABLE))
+    return (full_path);
+
   /* If the file is executable, then it satisfies the cases of
       EXEC_ONLY and EXEC_PREFERRED.  Return this file unconditionally. */
-  if ((status & FS_EXECABLE) &&
+  if ((status & FS_EXECABLE) && (flags & (FS_EXEC_ONLY|FS_EXEC_PREFERRED)) &&
       (((flags & FS_NODIRS) == 0) || ((status & FS_DIRECTORY) == 0)))
     {
       FREE (file_to_lose_on);
@@ -477,9 +500,11 @@ find_in_path_element (name, path, flags, name_len, dotinfop)
     file_to_lose_on = savestring (full_path);
 
   /* If we want only executable files, or we don't want directories and
-     this file is a directory, fail. */
-  if ((flags & FS_EXEC_ONLY) || (flags & FS_EXEC_PREFERRED) ||
-      ((flags & FS_NODIRS) && (status & FS_DIRECTORY)))
+     this file is a directory, or we want a readable file and this file
+     isn't readable, fail. */
+  if ((flags & (FS_EXEC_ONLY|FS_EXEC_PREFERRED)) ||
+      ((flags & FS_NODIRS) && (status & FS_DIRECTORY)) ||
+      ((flags & FS_READABLE) && (status & FS_READABLE) == 0))
     {
       free (full_path);
       return ((char *)NULL);