pathchk.c (3102B)
- // utils-std: Collection of commonly available Unix tools
- // SPDX-FileCopyrightText: 2017 Haelwenn (lanodan) Monnier <contact+utils@hacktivis.me>
- // SPDX-License-Identifier: MPL-2.0
- #define _POSIX_C_SOURCE 200809L
- #include <assert.h>
- #include <errno.h>
- #include <limits.h> // PATH_MAX
- #include <stdbool.h>
- #include <stdio.h> // fprintf
- #include <string.h> // strerror
- #include <sys/stat.h> // lstat
- #include <unistd.h> // getopt
- // POSIX Portable Character Set
- // Returns 0 on success, or the first invalid character found
- static char
- str_pcs(char *str)
- {
- for(size_t i = 0; i < strlen(str); i++)
- {
- char c = str[i];
- if(c >= 0x07 && c <= 0x0D) continue;
- if(c >= 0x20 && c <= 0x7E) continue;
- return c;
- }
- return 0;
- }
- static void
- usage(void)
- {
- fprintf(stderr, "Usage: pathchk [-p] [-P] pathname...\n");
- }
- int
- main(int argc, char *argv[])
- {
- bool opt_P = false, opt_p = false;
- size_t path_max = PATH_MAX - 1;
- size_t name_max = NAME_MAX;
- int c = -1;
- while((c = getopt(argc, argv, ":pP")) != -1)
- {
- switch(c)
- {
- case 'P':
- opt_P = true;
- break;
- case 'p':
- opt_p = true;
- path_max = _POSIX_PATH_MAX - 1;
- name_max = _POSIX_NAME_MAX;
- break;
- case ':':
- fprintf(stderr, "pathchk: error: Missing operand for option: '-%c'\n", optopt);
- usage();
- return 1;
- case '?':
- fprintf(stderr, "pathchk: error: Unrecognised option: '-%c'\n", optopt);
- usage();
- return 1;
- }
- }
- argv += optind;
- argc -= optind;
- if(argc != 1)
- {
- usage();
- return 1;
- }
- int err = 0;
- for(int i = 0; i < argc; i++)
- {
- char *path = argv[i];
- size_t len = strlen(path);
- if(opt_P && len == 0)
- {
- fprintf(stderr, "pathchk: error: Operand number %d is empty\n", i);
- err = 1;
- }
- // PATH_MAX includes terminating NULL
- if(len > path_max)
- {
- fprintf(stderr,
- "pathchk: error: Path (%zd octets) is over the maximum size (%zd octets): %s\n",
- len,
- path_max,
- path);
- err = 1;
- }
- char *p = strtok(path, "/");
- do
- {
- if(p == NULL || p[0] == 0) break;
- if(p[0] == '-' && opt_P)
- {
- fprintf(stderr, "pathchk: error: Path component starts with an hyphen: %s\n", p);
- err = 1;
- }
- // NAME_MAX doesn't includes terminating NULL
- size_t name_len = strlen(p);
- if(name_len > name_max)
- {
- fprintf(stderr,
- "pathchk: error: Path component (%zd octets) is over the maximum size (%zd "
- "octets): %s\n",
- name_len,
- name_max,
- p);
- err = 1;
- }
- } while((p = strtok(NULL, "/")) != NULL);
- if(!opt_p)
- {
- assert(errno == 0);
- if(access(path, F_OK) < 0 && errno != ENOENT)
- {
- fprintf(stderr,
- "pathchk: error: Failed checking '%s' against filesystem: %s\n",
- path,
- strerror(errno));
- errno = 0;
- err = 1;
- }
- }
- else
- {
- char c_pcs = str_pcs(path);
- if(c_pcs != 0)
- {
- fprintf(stderr,
- "pathchk: error: Non-portable character '%c' (0x%02x) found in: %s\n",
- c_pcs,
- c_pcs,
- path);
- err = 1;
- }
- }
- }
- return err;
- }