home *** CD-ROM | disk | FTP | other *** search
/ Geek Gadgets 1 / ADE-1.bin / ade-dist / sh-utils-1.12-src.tgz / tar.out / fsf / sh-utils / src / pathchk.c < prev    next >
C/C++ Source or Header  |  1996-09-28  |  10KB  |  377 lines

  1. /* pathchk -- check whether pathnames are valid or portable
  2.    Copyright (C) 91, 92, 93, 1994 Free Software Foundation, Inc.
  3.  
  4.    This program is free software; you can redistribute it and/or modify
  5.    it under the terms of the GNU General Public License as published by
  6.    the Free Software Foundation; either version 2, or (at your option)
  7.    any later version.
  8.  
  9.    This program is distributed in the hope that it will be useful,
  10.    but WITHOUT ANY WARRANTY; without even the implied warranty of
  11.    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12.    GNU General Public License for more details.
  13.  
  14.    You should have received a copy of the GNU General Public License
  15.    along with this program; if not, write to the Free Software
  16.    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */
  17.  
  18. /* Usage: pathchk [-p] [--portability] path...
  19.  
  20.    For each PATH, print a message if any of these conditions are false:
  21.    * all existing leading directories in PATH have search (execute) permission
  22.    * strlen (PATH) <= PATH_MAX
  23.    * strlen (each_directory_in_PATH) <= NAME_MAX
  24.  
  25.    Exit status:
  26.    0            All PATH names passed all of the tests.
  27.    1            An error occurred.
  28.  
  29.    Options:
  30.    -p, --portability    Instead of performing length checks on the
  31.             underlying filesystem, test the length of the
  32.             pathname and its components against the POSIX.1
  33.             minimum limits for portability, _POSIX_NAME_MAX
  34.             and _POSIX_PATH_MAX in 2.9.2.  Also check that
  35.             the pathname contains no character not in the
  36.             portable filename character set.
  37.  
  38.    David MacKenzie <djm@gnu.ai.mit.edu>
  39.    and Jim Meyering <meyering@cs.utexas.edu> */
  40.  
  41. #include <config.h>
  42. #include <stdio.h>
  43. #include <getopt.h>
  44. #include <sys/types.h>
  45.  
  46. #include "version.h"
  47. #include "system.h"
  48. #include "safe-stat.h"
  49.  
  50. #ifdef _POSIX_VERSION
  51. #include <limits.h>
  52. #ifndef PATH_MAX
  53. #define PATH_MAX_FOR(p) pathconf ((p), _PC_PATH_MAX)
  54. #endif /* not PATH_MAX */
  55. #ifndef NAME_MAX
  56. #define NAME_MAX_FOR(p) pathconf ((p), _PC_NAME_MAX);
  57. #endif /* not NAME_MAX */
  58.  
  59. #else /* not _POSIX_VERSION */
  60.  
  61. #include <sys/param.h>
  62. #ifndef PATH_MAX
  63. #ifdef MAXPATHLEN
  64. #define PATH_MAX MAXPATHLEN
  65. #else /* not MAXPATHLEN */
  66. #define PATH_MAX _POSIX_PATH_MAX
  67. #endif /* not MAXPATHLEN */
  68. #endif /* not PATH_MAX */
  69.  
  70. #ifndef NAME_MAX
  71. #ifdef MAXNAMLEN
  72. #define NAME_MAX MAXNAMLEN
  73. #else /* not MAXNAMLEN */
  74. #define NAME_MAX _POSIX_NAME_MAX
  75. #endif /* not MAXNAMLEN */
  76. #endif /* not NAME_MAX */
  77.  
  78. #endif /* not _POSIX_VERSION */
  79.  
  80. #ifndef _POSIX_PATH_MAX
  81. #define _POSIX_PATH_MAX 255
  82. #endif
  83. #ifndef _POSIX_NAME_MAX
  84. #define _POSIX_NAME_MAX 14
  85. #endif
  86.  
  87. #ifndef PATH_MAX_FOR
  88. #define PATH_MAX_FOR(p) PATH_MAX
  89. #endif
  90. #ifndef NAME_MAX_FOR
  91. #define NAME_MAX_FOR(p) NAME_MAX
  92. #endif
  93.  
  94. char *xstrdup ();
  95. void error ();
  96.  
  97. static int validate_path ();
  98. static void usage ();
  99.  
  100. /* The name this program was run with. */
  101. char *program_name;
  102.  
  103. /* If non-zero, display usage information and exit.  */
  104. static int show_help;
  105.  
  106. /* If non-zero, print the version on standard output and exit.  */
  107. static int show_version;
  108.  
  109. static struct option const longopts[] =
  110. {
  111.   {"help", no_argument, &show_help, 1},
  112.   {"portability", no_argument, NULL, 'p'},
  113.   {"version", no_argument, &show_version, 1},
  114.   {NULL, 0, NULL, 0}
  115. };
  116.  
  117. main (argc, argv)
  118.      int argc;
  119.      char **argv;
  120. {
  121.   int exit_status = 0;
  122.   int check_portability = 0;
  123.   int optc;
  124.  
  125.   program_name = argv[0];
  126.  
  127.   while ((optc = getopt_long (argc, argv, "p", longopts, (int *) 0)) != EOF)
  128.     {
  129.       switch (optc)
  130.     {
  131.     case 0:
  132.       break;
  133.  
  134.     case 'p':
  135.       check_portability = 1;
  136.       break;
  137.  
  138.     default:
  139.       usage (1);
  140.     }
  141.     }
  142.  
  143.   if (show_version)
  144.     {
  145.       printf ("pathchk - %s\n", version_string);
  146.       exit (0);
  147.     }
  148.  
  149.   if (show_help)
  150.     usage (0);
  151.  
  152.   if (optind == argc)
  153.     {
  154.       error (0, 0, "too few arguments");
  155.       usage (1);
  156.     }
  157.  
  158.   for (; optind < argc; ++optind)
  159.     exit_status |= validate_path (argv[optind], check_portability);
  160.  
  161.   exit (exit_status);
  162. }
  163.  
  164. /* Each element is nonzero if the corresponding ASCII character is
  165.    in the POSIX portable character set, and zero if it is not.
  166.    In addition, the entry for `/' is nonzero to simplify checking. */
  167. static char const portable_chars[256] =
  168. {
  169.   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0-15 */
  170.   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 16-31 */
  171.   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, /* 32-47 */
  172.   1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, /* 48-63 */
  173.   0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 64-79 */
  174.   1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, /* 80-95 */
  175.   0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 96-111 */
  176.   1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, /* 112-127 */
  177.   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
  178.   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
  179.   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
  180.   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
  181.   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
  182.   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
  183.   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
  184.   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
  185. };
  186.  
  187. /* If PATH contains only portable characters, return 1, else 0.  */
  188.  
  189. static int
  190. portable_chars_only (path)
  191.      const char *path;
  192. {
  193.   const char *p;
  194.  
  195.   for (p = path; *p; ++p)
  196.     if (portable_chars[(const unsigned char) *p] == 0)
  197.       {
  198.     error (0, 0, "path `%s' contains nonportable character `%c'",
  199.            path, *p);
  200.     return 0;
  201.       }
  202.   return 1;
  203. }
  204.  
  205. /* Return 1 if PATH is a usable leading directory, 0 if not,
  206.    2 if it doesn't exist.  */
  207.  
  208. static int
  209. dir_ok (path)
  210.      const char *path;
  211. {
  212.   struct stat stats;
  213.  
  214.   if (SAFE_STAT (path, &stats))
  215.     return 2;
  216.  
  217.   if (!S_ISDIR (stats.st_mode))
  218.     {
  219.       error (0, 0, "`%s' is not a directory", path);
  220.       return 0;
  221.     }
  222.  
  223.   /* Use access to test for search permission because
  224.      testing permission bits of st_mode can lose with new
  225.      access control mechanisms.  Of course, access loses if you're
  226.      running setuid. */
  227.   if (access (path, X_OK) != 0)
  228.     {
  229.       if (errno == EACCES)
  230.     error (0, 0, "directory `%s' is not searchable", path);
  231.       else
  232.     error (0, errno, "%s", path);
  233.       return 0;
  234.     }
  235.  
  236.   return 1;
  237. }
  238.  
  239. /* Make sure that
  240.    strlen (PATH) <= PATH_MAX
  241.    && strlen (each-existing-directory-in-PATH) <= NAME_MAX
  242.  
  243.    If PORTABILITY is nonzero, compare against _POSIX_PATH_MAX and
  244.    _POSIX_NAME_MAX instead, and make sure that PATH contains no
  245.    characters not in the POSIX portable filename character set, which
  246.    consists of A-Z, a-z, 0-9, ., _, -.
  247.  
  248.    Make sure that all leading directories along PATH that exist have
  249.    `x' permission.
  250.  
  251.    Return 0 if all of these tests are successful, 1 if any fail. */
  252.  
  253. static int
  254. validate_path (path, portability)
  255.      char *path;
  256.      int portability;
  257. {
  258.   int path_max;
  259.   int last_elem;        /* Nonzero if checking last element of path. */
  260.   int exists;            /* 2 if the path element exists.  */
  261.   char *slash;
  262.   char *parent;            /* Last existing leading directory so far.  */
  263.  
  264.   if (portability && !portable_chars_only (path))
  265.     return 1;
  266.  
  267.   if (*path == '\0')
  268.     return 0;
  269.  
  270. #ifdef lint
  271.   /* Suppress `used before initialized' warning.  */
  272.   exists = 0;
  273. #endif
  274.  
  275.   /* Figure out the parent of the first element in PATH.  */
  276.   parent = xstrdup (*path == '/' ? "/" : ".");
  277.  
  278.   slash = path;
  279.   last_elem = 0;
  280.   while (1)
  281.     {
  282.       int name_max;
  283.       int length;        /* Length of partial path being checked. */
  284.       char *start;        /* Start of path element being checked. */
  285.  
  286.       /* Find the end of this element of the path.
  287.      Then chop off the rest of the path after this element. */
  288.       while (*slash == '/')
  289.     slash++;
  290.       start = slash;
  291.       slash = index (slash, '/');
  292.       if (slash != NULL)
  293.     *slash = '\0';
  294.       else
  295.     {
  296.       last_elem = 1;
  297.       slash = index (start, '\0');
  298.     }
  299.  
  300.       if (!last_elem)
  301.     {
  302.       exists = dir_ok (path);
  303.       if (dir_ok == 0)
  304.         {
  305.           free (parent);
  306.           return 1;
  307.         }
  308.     }
  309.  
  310.       length = slash - start;
  311.       /* Since we know that `parent' is a directory, it's ok to call
  312.      pathconf with it as the argument.  (If `parent' isn't a directory
  313.      or doesn't exist, the behavior of pathconf is undefined.)
  314.      But if `parent' is a directory and is on a remote file system,
  315.      it's likely that pathconf can't give us a reasonable value
  316.      and will return -1.  (NFS and tempfs are not POSIX . . .)
  317.      In that case, we have no choice but to assume the pessimal
  318.      POSIX minimums.  */
  319.       name_max = portability ? _POSIX_NAME_MAX : NAME_MAX_FOR (parent);
  320.       if (name_max < 0)
  321.     name_max = _POSIX_NAME_MAX;
  322.       if (length > name_max)
  323.     {
  324.       error (0, 0, "name `%s' has length %d; exceeds limit of %d",
  325.          start, length, name_max);
  326.       free (parent);
  327.       return 1;
  328.     }
  329.  
  330.       if (last_elem)
  331.     break;
  332.  
  333.       if (exists == 1)
  334.     {
  335.       free (parent);
  336.       parent = xstrdup (path);
  337.     }
  338.  
  339.       *slash++ = '/';
  340.     }
  341.  
  342.   /* `parent' is now the last existing leading directory in the whole path,
  343.      so it's ok to call pathconf with it as the argument.  */
  344.   path_max = portability ? _POSIX_PATH_MAX : PATH_MAX_FOR (parent);
  345.   if (path_max < 0)
  346.     path_max = _POSIX_PATH_MAX;
  347.   free (parent);
  348.   if (strlen (path) > path_max)
  349.     {
  350.       error (0, 0, "path `%s' has length %d; exceeds limit of %d",
  351.          path, strlen (path), path_max);
  352.       return 1;
  353.     }
  354.  
  355.   return 0;
  356. }
  357.  
  358. static void
  359. usage (status)
  360.      int status;
  361. {
  362.   if (status != 0)
  363.     fprintf (stderr, "Try `%s --help' for more information.\n",
  364.          program_name);
  365.   else
  366.     {
  367.       printf ("Usage: %s [OPTION]... PATH...\n", program_name);
  368.       printf ("\
  369. \n\
  370.   -p, --portability   check for all POSIX systems, not only this one\n\
  371.       --help          display this help and exit\n\
  372.       --version       output version information and exit\n\
  373. ");
  374.     }
  375.   exit (status);
  376. }
  377.