1 /* pathchk -- check whether pathnames are valid or portable
2 Copyright (C) 1991-2003 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 Foundation,
16 Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, 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
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> */
44 #include <sys/types.h>
53 #include "long-options.h"
56 /* The official name of this program (e.g., no `g' prefix). */
57 #define PROGRAM_NAME "pathchk"
59 #define AUTHORS N_ ("David MacKenzie and Jim Meyering")
61 #define NEED_PATHCONF_WRAPPER 0
64 # define PATH_MAX_FOR(p) pathconf_wrapper ((p), _PC_PATH_MAX)
65 # define NEED_PATHCONF_WRAPPER 1
66 # endif /* not PATH_MAX */
68 # define NAME_MAX_FOR(p) pathconf_wrapper ((p), _PC_NAME_MAX);
69 # undef NEED_PATHCONF_WRAPPER
70 # define NEED_PATHCONF_WRAPPER 1
71 # endif /* not NAME_MAX */
75 # include <sys/param.h>
78 # define PATH_MAX MAXPATHLEN
79 # else /* not MAXPATHLEN */
80 # define PATH_MAX _POSIX_PATH_MAX
81 # endif /* not MAXPATHLEN */
82 # endif /* not PATH_MAX */
86 # define NAME_MAX MAXNAMLEN
87 # else /* not MAXNAMLEN */
88 # define NAME_MAX _POSIX_NAME_MAX
89 # endif /* not MAXNAMLEN */
90 # endif /* not NAME_MAX */
94 #ifndef _POSIX_PATH_MAX
95 # define _POSIX_PATH_MAX 255
97 #ifndef _POSIX_NAME_MAX
98 # define _POSIX_NAME_MAX 14
102 # define PATH_MAX_FOR(p) PATH_MAX
105 # define NAME_MAX_FOR(p) NAME_MAX
108 static int validate_path (char *path, int portability);
110 /* The name this program was run with. */
113 static struct option const longopts[] =
115 {"portability", no_argument, NULL, 'p'},
119 #if NEED_PATHCONF_WRAPPER
120 /* Distinguish between the cases when pathconf fails and when it reports there
121 is no limit (the latter is the case for PATH_MAX on the Hurd). When there
122 is no limit, return LONG_MAX. Otherwise, return pathconf's return value. */
125 pathconf_wrapper (const char *filename, int param)
130 ret = pathconf (filename, param);
131 if (ret < 0 && errno == 0)
142 fprintf (stderr, _("Try `%s --help' for more information.\n"),
146 printf (_("Usage: %s [OPTION]... NAME...\n"), program_name);
148 Diagnose unportable constructs in NAME.\n\
150 -p, --portability check for all POSIX systems, not only this one\n\
152 fputs (HELP_OPTION_DESCRIPTION, stdout);
153 fputs (VERSION_OPTION_DESCRIPTION, stdout);
154 printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
160 main (int argc, char **argv)
163 int check_portability = 0;
166 initialize_main (&argc, &argv);
167 program_name = argv[0];
168 setlocale (LC_ALL, "");
169 bindtextdomain (PACKAGE, LOCALEDIR);
170 textdomain (PACKAGE);
172 atexit (close_stdout);
174 parse_long_options (argc, argv, PROGRAM_NAME, GNU_PACKAGE, VERSION,
177 while ((optc = getopt_long (argc, argv, "p", longopts, NULL)) != -1)
185 check_portability = 1;
189 usage (EXIT_FAILURE);
195 error (0, 0, _("too few arguments"));
196 usage (EXIT_FAILURE);
199 for (; optind < argc; ++optind)
200 exit_status |= validate_path (argv[optind], check_portability);
205 /* Each element is nonzero if the corresponding ASCII character is
206 in the POSIX portable character set, and zero if it is not.
207 In addition, the entry for `/' is nonzero to simplify checking. */
208 static char const portable_chars[256] =
210 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0-15 */
211 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 16-31 */
212 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, /* 32-47 */
213 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, /* 48-63 */
214 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 64-79 */
215 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, /* 80-95 */
216 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 96-111 */
217 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, /* 112-127 */
218 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
219 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
220 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
221 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
222 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
223 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
224 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
225 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
228 /* If PATH contains only portable characters, return 1, else 0. */
231 portable_chars_only (const char *path)
235 for (p = path; *p; ++p)
236 if (portable_chars[(unsigned char) *p] == 0)
238 error (0, 0, _("path `%s' contains nonportable character `%c'"),
245 /* Return 1 if PATH is a usable leading directory, 0 if not,
246 2 if it doesn't exist. */
249 dir_ok (const char *path)
253 if (stat (path, &stats))
256 if (!S_ISDIR (stats.st_mode))
258 error (0, 0, _("`%s' is not a directory"), path);
262 /* Use access to test for search permission because
263 testing permission bits of st_mode can lose with new
264 access control mechanisms. Of course, access loses if you're
266 if (access (path, X_OK) != 0)
269 error (0, 0, _("directory `%s' is not searchable"), path);
271 error (0, errno, "%s", path);
279 strlen (PATH) <= PATH_MAX
280 && strlen (each-existing-directory-in-PATH) <= NAME_MAX
282 If PORTABILITY is nonzero, compare against _POSIX_PATH_MAX and
283 _POSIX_NAME_MAX instead, and make sure that PATH contains no
284 characters not in the POSIX portable filename character set, which
285 consists of A-Z, a-z, 0-9, ., _, -.
287 Make sure that all leading directories along PATH that exist have
290 Return 0 if all of these tests are successful, 1 if any fail. */
293 validate_path (char *path, int portability)
296 int last_elem; /* Nonzero if checking last element of path. */
297 int exists IF_LINT (= 0); /* 2 if the path element exists. */
299 char *parent; /* Last existing leading directory so far. */
301 if (portability && !portable_chars_only (path))
307 /* Figure out the parent of the first element in PATH. */
308 parent = xstrdup (*path == '/' ? "/" : ".");
315 long int length; /* Length of partial path being checked. */
316 char *start; /* Start of path element being checked. */
318 /* Find the end of this element of the path.
319 Then chop off the rest of the path after this element. */
320 while (*slash == '/')
323 slash = strchr (slash, '/');
329 slash = strchr (start, '\0');
334 exists = dir_ok (path);
342 length = slash - start;
343 /* Since we know that `parent' is a directory, it's ok to call
344 pathconf with it as the argument. (If `parent' isn't a directory
345 or doesn't exist, the behavior of pathconf is undefined.)
346 But if `parent' is a directory and is on a remote file system,
347 it's likely that pathconf can't give us a reasonable value
348 and will return -1. (NFS and tempfs are not POSIX . . .)
349 In that case, we have no choice but to assume the pessimal
351 name_max = portability ? _POSIX_NAME_MAX : NAME_MAX_FOR (parent);
353 name_max = _POSIX_NAME_MAX;
354 if (length > name_max)
356 error (0, 0, _("name `%s' has length %ld; exceeds limit of %ld"),
357 start, length, name_max);
368 parent = xstrdup (path);
374 /* `parent' is now the last existing leading directory in the whole path,
375 so it's ok to call pathconf with it as the argument. */
376 path_max = portability ? _POSIX_PATH_MAX : PATH_MAX_FOR (parent);
378 path_max = _POSIX_PATH_MAX;
380 if (strlen (path) > (size_t) path_max)
382 error (0, 0, _("path `%s' has length %d; exceeds limit of %ld"),
383 path, strlen (path), path_max);