1 /* pathchk -- check whether pathnames are valid or portable
2 Copyright (C) 91, 92, 93, 1994 Free Software Foundation, Inc.
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)
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.
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. */
18 /* Usage: pathchk [-p] [--portability] path...
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
26 0 All PATH names passed all of the tests.
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.
38 David MacKenzie <djm@gnu.ai.mit.edu>
39 and Jim Meyering <meyering@cs.utexas.edu> */
42 #if defined (CONFIG_BROKETS)
43 /* We use <config.h> instead of "config.h" so that a compilation
44 using -I. -I$srcdir will use ./config.h rather than $srcdir/config.h
45 (which it would do because it found this file in $srcdir). */
54 #include <sys/types.h>
58 #include "safe-stat.h"
63 #define PATH_MAX_FOR(p) pathconf ((p), _PC_PATH_MAX)
64 #endif /* not PATH_MAX */
66 #define NAME_MAX_FOR(p) pathconf ((p), _PC_NAME_MAX);
67 #endif /* not NAME_MAX */
69 #else /* not _POSIX_VERSION */
71 #include <sys/param.h>
74 #define PATH_MAX MAXPATHLEN
75 #else /* not MAXPATHLEN */
76 #define PATH_MAX _POSIX_PATH_MAX
77 #endif /* not MAXPATHLEN */
78 #endif /* not PATH_MAX */
82 #define NAME_MAX MAXNAMLEN
83 #else /* not MAXNAMLEN */
84 #define NAME_MAX _POSIX_NAME_MAX
85 #endif /* not MAXNAMLEN */
86 #endif /* not NAME_MAX */
88 #endif /* not _POSIX_VERSION */
90 #ifndef _POSIX_PATH_MAX
91 #define _POSIX_PATH_MAX 255
93 #ifndef _POSIX_NAME_MAX
94 #define _POSIX_NAME_MAX 14
98 #define PATH_MAX_FOR(p) PATH_MAX
101 #define NAME_MAX_FOR(p) NAME_MAX
107 static int validate_path ();
108 static void usage ();
110 /* The name this program was run with. */
113 /* If non-zero, display usage information and exit. */
114 static int show_help;
116 /* If non-zero, print the version on standard output and exit. */
117 static int show_version;
119 static struct option const longopts[] =
121 {"help", no_argument, &show_help, 1},
122 {"portability", no_argument, NULL, 'p'},
123 {"version", no_argument, &show_version, 1},
133 int check_portability = 0;
136 program_name = argv[0];
138 while ((optc = getopt_long (argc, argv, "p", longopts, (int *) 0)) != EOF)
146 check_portability = 1;
156 printf ("pathchk - %s\n", version_string);
166 for (; optind < argc; ++optind)
167 exit_status |= validate_path (argv[optind], check_portability);
172 /* Each element is nonzero if the corresponding ASCII character is
173 in the POSIX portable character set, and zero if it is not.
174 In addition, the entry for `/' is nonzero to simplify checking. */
175 static char const portable_chars[256] =
177 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0-15 */
178 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 16-31 */
179 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, /* 32-47 */
180 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, /* 48-63 */
181 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 64-79 */
182 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, /* 80-95 */
183 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 96-111 */
184 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, /* 112-127 */
185 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
186 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
187 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
188 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
189 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
190 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
191 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
192 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
195 /* If PATH contains only portable characters, return 1, else 0. */
198 portable_chars_only (path)
203 for (p = path; *p; ++p)
204 if (portable_chars[(const unsigned char) *p] == 0)
206 error (0, 0, "path `%s' contains nonportable character `%c'",
213 /* Return 1 if PATH is a usable leading directory, 0 if not,
214 2 if it doesn't exist. */
222 if (SAFE_STAT (path, &stats))
225 if (!S_ISDIR (stats.st_mode))
227 error (0, 0, "`%s' is not a directory", path);
231 /* Use access to test for search permission because
232 testing permission bits of st_mode can lose with new
233 access control mechanisms. Of course, access loses if you're
235 if (access (path, X_OK) != 0)
238 error (0, 0, "directory `%s' is not searchable", path);
240 error (0, errno, "%s", path);
248 strlen (PATH) <= PATH_MAX
249 && strlen (each-existing-directory-in-PATH) <= NAME_MAX
251 If PORTABILITY is nonzero, compare against _POSIX_PATH_MAX and
252 _POSIX_NAME_MAX instead, and make sure that PATH contains no
253 characters not in the POSIX portable filename character set, which
254 consists of A-Z, a-z, 0-9, ., _, -.
256 Make sure that all leading directories along PATH that exist have
259 Return 0 if all of these tests are successful, 1 if any fail. */
262 validate_path (path, portability)
267 int last_elem; /* Nonzero if checking last element of path. */
268 int exists; /* 2 if the path element exists. */
270 char *parent; /* Last existing leading directory so far. */
272 if (portability && !portable_chars_only (path))
279 /* Suppress `used before initialized' warning. */
283 /* Figure out the parent of the first element in PATH. */
284 parent = xstrdup (*path == '/' ? "/" : ".");
291 int length; /* Length of partial path being checked. */
292 char *start; /* Start of path element being checked. */
294 /* Find the end of this element of the path.
295 Then chop off the rest of the path after this element. */
296 while (*slash == '/')
299 slash = index (slash, '/');
305 slash = index (start, '\0');
310 exists = dir_ok (path);
318 length = slash - start;
319 /* Since we know that `parent' is a directory, it's ok to call
320 pathconf with it as the argument. (If `parent' isn't a directory
321 or doesn't exist, the behavior of pathconf is undefined.)
322 But if `parent' is a directory and is on a remote file system,
323 it's likely that pathconf can't give us a reasonable value
324 and will return -1. (NFS and tempfs are not POSIX . . .)
325 In that case, we have no choice but to assume the pessimal
327 name_max = portability ? _POSIX_NAME_MAX : NAME_MAX_FOR (parent);
329 name_max = _POSIX_NAME_MAX;
330 if (length > name_max)
332 error (0, 0, "name `%s' has length %d; exceeds limit of %d",
333 start, length, name_max);
344 parent = xstrdup (path);
350 /* `parent' is now the last existing leading directory in the whole path,
351 so it's ok to call pathconf with it as the argument. */
352 path_max = portability ? _POSIX_PATH_MAX : PATH_MAX_FOR (parent);
354 path_max = _POSIX_PATH_MAX;
356 if (strlen (path) > path_max)
358 error (0, 0, "path `%s' has length %d; exceeds limit of %d",
359 path, strlen (path), path_max);
371 fprintf (stderr, "Try `%s --help' for more information.\n",
375 printf ("Usage: %s [OPTION]... PATH...\n", program_name);
378 -p, --portability check for all POSIX systems, not only this one\n\
379 --help display this help and exit\n\
380 --version output version information and exit\n\