/* pathchk -- check whether file names are valid or portable This is the pathchk utility
Copyright (C) 1991-2018 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>. */ The GNUv3 license
#include <config.h> Provides system specific information
#include <stdio.h> Provides standard I/O capability
#include <getopt.h> ...!includes auto-comment...
#include <sys/types.h> Provides system data types
#include <wchar.h> ...!includes auto-comment...
#include "system.h" ...!includes auto-comment...
#include "error.h" ...!includes auto-comment...
#include "quote.h" ...!includes auto-comment...
/* The official name of this program (e.g., no 'g' prefix). */
#define PROGRAM_NAME "pathchk" Line 28
#define AUTHORS \ Line 30
proper_name ("Paul Eggert"), \ Line 31
proper_name ("David MacKenzie"), \ Line 32
proper_name ("Jim Meyering") Line 33
#ifndef _POSIX_PATH_MAX Line 35
# define _POSIX_PATH_MAX 256 Line 36
#endif Line 37
#ifndef _POSIX_NAME_MAX Line 38
# define _POSIX_NAME_MAX 14 Line 39
#endif Line 40
#ifdef _XOPEN_NAME_MAX Line 42
# define NAME_MAX_MINIMUM _XOPEN_NAME_MAX Line 43
#else Line 44
# define NAME_MAX_MINIMUM _POSIX_NAME_MAX Line 45
#endif Line 46
#ifdef _XOPEN_PATH_MAX Line 47
# define PATH_MAX_MINIMUM _XOPEN_PATH_MAX Line 48
#else Line 49
# define PATH_MAX_MINIMUM _POSIX_PATH_MAX Line 50
#endif Line 51
#if ! (HAVE_PATHCONF && defined _PC_NAME_MAX && defined _PC_PATH_MAX) Line 53
# ifndef _PC_NAME_MAX Line 54
# define _PC_NAME_MAX 0 Line 55
# define _PC_PATH_MAX 1 Line 56
# endif Line 57
# ifndef pathconf Line 58
# define pathconf(file, flag) \ Line 59
(flag == _PC_NAME_MAX ? NAME_MAX_MINIMUM : PATH_MAX_MINIMUM) Line 60
# endif Line 61
#endif Line 62
static bool validate_file_name (char *, bool, bool); Line 64
/* For long options that have no equivalent short option, use a
non-character as a pseudo short option, starting with CHAR_MAX + 1. */
enum Line 68
{
PORTABILITY_OPTION = CHAR_MAX + 1 Line 70
}; Block 1
static struct option const longopts[] = Line 73
{
{"portability", no_argument, NULL, PORTABILITY_OPTION}, Line 75
{GETOPT_HELP_OPTION_DECL}, Line 76
{GETOPT_VERSION_OPTION_DECL}, Line 77
{NULL, 0, NULL, 0} Line 78
}; Block 2
void Line 81
usage (int status) Line 82
{
if (status != EXIT_SUCCESS) Line 84
emit_try_help (); ...!common auto-comment...
else Line 86
{
printf (_("Usage: %s [OPTION]... NAME...\n"), program_name); Line 88
fputs (_("\ Line 89
Diagnose invalid or unportable file names.\n\ Line 90
\n\
-p check for most POSIX systems\n\ Line 92
-P check for empty names and leading \"-\"\n\ Line 93
--portability check for all POSIX systems (equivalent to -p -P)\n\ Line 94
"), stdout); Line 95
fputs (HELP_OPTION_DESCRIPTION, stdout); Line 96
fputs (VERSION_OPTION_DESCRIPTION, stdout); Line 97
emit_ancillary_info (PROGRAM_NAME); Line 98
}
exit (status); Line 100
} Block 3
int
main (int argc, char **argv) Line 104
{
bool ok = true; Line 106
bool check_basic_portability = false; Line 107
bool check_extra_portability = false; Line 108
int optc; Line 109
initialize_main (&argc, &argv); VMS-specific entry point handling wildcard expansion
set_program_name (argv[0]); Retains program name and discards path
setlocale (LC_ALL, ""); Sets up internationalization (i18n)
bindtextdomain (PACKAGE, LOCALEDIR); Assigns i18n directorySets text domain for _() [gettext()] function
textdomain (PACKAGE); Sets text domain for _() [gettext()] function
atexit (close_stdout); Close stdout on exit (see gnulib)
while ((optc = getopt_long (argc, argv, "+pP", longopts, NULL)) != -1) Line 119
{
switch (optc) Line 121
{
case PORTABILITY_OPTION: Line 123
check_basic_portability = true; Line 124
check_extra_portability = true; Line 125
break; Line 126
case 'p': Line 128
check_basic_portability = true; Line 129
break; Line 130
case 'P': Line 132
check_extra_portability = true; Line 133
break; Line 134
case_GETOPT_HELP_CHAR; Line 136
case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS); Line 138
default: Line 140
usage (EXIT_FAILURE); Line 141
}
}
if (optind == argc) Line 145
{
error (0, 0, _("missing operand")); Line 147
usage (EXIT_FAILURE); Line 148
}
for (; optind < argc; ++optind) Line 151
ok &= validate_file_name (argv[optind], Line 152
check_basic_portability, check_extra_portability);Line 153
return ok ? EXIT_SUCCESS : EXIT_FAILURE; Line 155
} Block 4
/* If FILE contains a component with a leading "-", report an error
and return false; otherwise, return true. */
static bool Line 161
no_leading_hyphen (char const *file) Line 162
{
char const *p; Line 164
for (p = file; (p = strchr (p, '-')); p++) Line 166
if (p == file || p[-1] == '/') Line 167
{
error (0, 0, _("leading '-' in a component of file name %s"), Line 169
quoteaf (file)); Line 170
return false; Line 171
}
return true; Line 174
} Block 5
/* If FILE (of length FILELEN) contains only portable characters,
return true, else report an error and return false. */
static bool Line 180
portable_chars_only (char const *file, size_t filelen) Line 181
{
size_t validlen = strspn (file, Line 183
("/" Line 184
"ABCDEFGHIJKLMNOPQRSTUVWXYZ" Line 185
"abcdefghijklmnopqrstuvwxyz" Line 186
"0123456789._-")); Line 187
char const *invalid = file + validlen; Line 188
if (*invalid) Line 190
{
mbstate_t mbstate = { 0, }; Line 192
size_t charlen = mbrlen (invalid, filelen - validlen, &mbstate); Line 193
error (0, 0, Line 194
_("nonportable character %s in file name %s"), Line 195
quotearg_n_style_mem (1, locale_quoting_style, invalid, Line 196
(charlen <= MB_LEN_MAX ? charlen : 1)), Line 197
quoteaf_n (0, file)); Line 198
return false; Line 199
}
return true; Line 202
} Block 6
/* Return the address of the start of the next file name component in F. */
static char * _GL_ATTRIBUTE_PURE Line 207
component_start (char *f) Line 208
{
while (*f == '/') Line 210
f++; Line 211
return f; Line 212
} Block 7
/* Return the size of the file name component F. F must be nonempty. */
static size_t _GL_ATTRIBUTE_PURE Line 217
component_len (char const *f) Line 218
{
size_t len; Line 220
for (len = 1; f[len] != '/' && f[len]; len++) Line 221
continue; Line 222
return len; Line 223
} Block 8
/* Make sure that
strlen (FILE) <= PATH_MAX
&& strlen (each-existing-directory-in-FILE) <= NAME_MAX
If CHECK_BASIC_PORTABILITY is true, compare against _POSIX_PATH_MAX and
_POSIX_NAME_MAX instead, and make sure that FILE contains no
characters not in the POSIX portable filename character set, which
consists of A-Z, a-z, 0-9, ., _, - (plus / for separators).
If CHECK_BASIC_PORTABILITY is false, make sure that all leading directories
along FILE that exist are searchable.
If CHECK_EXTRA_PORTABILITY is true, check that file name components do not
begin with "-".
If either CHECK_BASIC_PORTABILITY or CHECK_EXTRA_PORTABILITY is true,
check that the file name is not empty.
Return true if all of these tests are successful, false if any fail. */
static bool Line 246
validate_file_name (char *file, bool check_basic_portability, Line 247
bool check_extra_portability) Line 248
{
size_t filelen = strlen (file); Line 250
/* Start of file name component being checked. */
char *start; Line 253
/* True if component lengths need to be checked. */
bool check_component_lengths; Line 256
/* True if the file is known to exist. */
bool file_exists = false; Line 259
if (check_extra_portability && ! no_leading_hyphen (file)) Line 261
return false; Line 262
if ((check_basic_portability || check_extra_portability) Line 264
&& filelen == 0) Line 265
{
/* Fail, since empty names are not portable. As of
2005-01-06 POSIX does not address whether "pathchk -p ''"
should (or is allowed to) fail, so this is not a
conformance violation. */
error (0, 0, _("empty file name")); Line 271
return false; Line 272
}
if (check_basic_portability) Line 275
{
if (! portable_chars_only (file, filelen)) Line 277
return false; Line 278
}
else Line 280
{
/* Check whether a file name component is in a directory that
is not searchable, or has some other serious problem.
POSIX does not allow "" as a file name, but some non-POSIX
hosts do (as an alias for "."), so allow "" if lstat does. */
struct stat st; Line 287
if (lstat (file, &st) == 0) Line 288...!syscalls auto-comment...
file_exists = true; Line 289
else if (errno != ENOENT || filelen == 0) Line 290
{
error (0, errno, "%s", quotef (file)); Line 292
return false; Line 293
}
}
if (check_basic_portability Line 297
|| (! file_exists && PATH_MAX_MINIMUM <= filelen)) Line 298
{
size_t maxsize; Line 300
if (check_basic_portability) Line 302
maxsize = _POSIX_PATH_MAX; Line 303
else Line 304
{
long int size; Line 306
char const *dir = (*file == '/' ? "/" : "."); Line 307
errno = 0; Line 308
size = pathconf (dir, _PC_PATH_MAX); Line 309
if (size < 0 && errno != 0) Line 310
{
error (0, errno, Line 312
_("%s: unable to determine maximum file name length"), Line 313
dir); Line 314
return false; Line 315
}
maxsize = MIN (size, SSIZE_MAX); Line 317
}
if (maxsize <= filelen) Line 320
{
unsigned long int len = filelen; Line 322
unsigned long int maxlen = maxsize - 1; Line 323
error (0, 0, _("limit %lu exceeded by length %lu of file name %s"), Line 324
maxlen, len, quoteaf (file)); Line 325
return false; Line 326
}
}
/* Check whether pathconf (..., _PC_NAME_MAX) can be avoided, i.e.,
whether all file name components are so short that they are valid
in any file system on this platform. If CHECK_BASIC_PORTABILITY, though,
it's more convenient to check component lengths below. */
check_component_lengths = check_basic_portability; Line 335
if (! check_component_lengths && ! file_exists) Line 336
{
for (start = file; *(start = component_start (start)); ) Line 338
{
size_t length = component_len (start); Line 340
if (NAME_MAX_MINIMUM < length) Line 342
{
check_component_lengths = true; Line 344
break; Line 345
}
start += length; Line 348
}
}
if (check_component_lengths) Line 352
{
/* The limit on file name components for the current component.
This defaults to NAME_MAX_MINIMUM, for the sake of non-POSIX
systems (NFS, say?) where pathconf fails on "." or "/" with
errno == ENOENT. */
size_t name_max = NAME_MAX_MINIMUM; Line 358
/* If nonzero, the known limit on file name components. */
size_t known_name_max = (check_basic_portability ? _POSIX_NAME_MAX : 0); Line 361
for (start = file; *(start = component_start (start)); ) Line 363
{
size_t length; Line 365
if (known_name_max) Line 367
name_max = known_name_max; Line 368
else Line 369
{
long int len; Line 371
char const *dir = (start == file ? "." : file); Line 372
char c = *start; Line 373
errno = 0; Line 374
*start = '\0'; Line 375
len = pathconf (dir, _PC_NAME_MAX); Line 376
*start = c; Line 377
if (0 <= len) Line 378
name_max = MIN (len, SSIZE_MAX); Line 379
else Line 380
switch (errno) Line 381
{
case 0: Line 383
/* There is no limit. */
name_max = SIZE_MAX; Line 385
break; Line 386
case ENOENT: Line 388
/* DIR does not exist; use its parent's maximum. */
known_name_max = name_max; Line 390
break; Line 391
default: Line 393
*start = '\0'; Line 394
error (0, errno, "%s", quotef (dir)); Line 395
*start = c; Line 396
return false; Line 397
}
}
length = component_len (start); Line 401
if (name_max < length) Line 403
{
unsigned long int len = length; Line 405
unsigned long int maxlen = name_max; Line 406
char c = start[len]; Line 407
start[len] = '\0'; Line 408
error (0, 0, Line 409
_("limit %lu exceeded by length %lu " Line 410
"of file name component %s"), Line 411
maxlen, len, quote (start)); Line 412
start[len] = c; Line 413
return false; Line 414
}
start += length; Line 417
}
}
return true; Line 421
} Block 9