master xplshn/aruu / cmd / posix / pathchk.c
  1/* See LICENSE file for copyright and license details. */
  2
  3#include <sys/stat.h>
  4
  5#include <errno.h>
  6#include <limits.h>
  7#include <stdint.h>
  8#include <stdio.h>
  9#include <stdlib.h>
 10#include <string.h>
 11
 12#include "util.h"
 13
 14#define PORTABLE_CHARACTER_SET "0123456789._-qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM"
 15/* If your system supports more other characters, but not all non-NUL
 16 * characters, define SYSTEM_CHARACTER_SET. */
 17
 18static int most  = 0;
 19static int extra = 0;
 20
 21static int
 22pathchk(char *filename)
 23{
 24  char       *invalid, *invalid_end, *p, *q;
 25  const char *character_set;
 26  size_t      len, maxlen;
 27  struct stat st;
 28
 29  /* Empty? */
 30  if (extra && !*filename)
 31    eprintf("empty filename\n");
 32
 33  /* Leading hyphen? */
 34  if (extra && ((*filename == '-') || strstr(filename, "/-")))
 35    eprintf("%s: leading '-' in component of filename\n", filename);
 36
 37  /* Nonportable character? */
 38#ifdef SYSTEM_CHARACTER_SET
 39  character_set = "/" SYSTEM_CHARACTER_SET;
 40#else
 41  character_set = 0;
 42#endif
 43  if (most)
 44    character_set = "/" PORTABLE_CHARACTER_SET;
 45  if (character_set && *(invalid = filename + strspn(filename, character_set))) {
 46    for (invalid_end = invalid + 1; *invalid_end & 0x80; invalid_end++)
 47      ;
 48    p            = estrdup(filename);
 49    *invalid_end = 0;
 50    eprintf("%s: nonportable character '%s'\n", p, invalid);
 51  }
 52
 53  /* Symlink error? Non-searchable directory? */
 54  if (lstat(filename, &st) && errno != ENOENT) {
 55    /* lstat rather than stat, so that if filename is a bad symlink,
 56     * but all parents are OK, no error will be detected. */
 57    eprintf("%s:", filename);
 58  }
 59
 60  /* Too long pathname? */
 61  maxlen = most ? _POSIX_PATH_MAX : PATH_MAX;
 62  if (strlen(filename) >= maxlen)
 63    eprintf("%s: is longer than %zu bytes\n", filename, maxlen);
 64
 65  /* Too long component? */
 66  maxlen = most ? _POSIX_NAME_MAX : NAME_MAX;
 67  for (p = filename; p; p = q) {
 68    q   = strchr(p, '/');
 69    len = q ? (size_t)(q++ - p) : strlen(p);
 70    if (len > maxlen)
 71      eprintf(
 72          "%s: includes component longer than %zu "
 73          "bytes\n",
 74          filename,
 75          maxlen
 76      );
 77  }
 78
 79  return 0;
 80}
 81
 82static void
 83usage(void)
 84{
 85  eprintf("usage: %s [-pP] filename...\n", argv0);
 86}
 87
 88// ?man pathchk: check pathnames
 89// ?man arguments: filename...
 90// ?man verify that pathnames are valid and portable
 91int
 92main(int argc, char *argv[])
 93{
 94  int ret = 0;
 95
 96  ARGBEGIN
 97  {
 98    // ?man -p: preserve file attributes
 99    case 'p':
100      most = 1;
101      break;
102    // ?man -P: specify option flag
103    case 'P':
104      extra = 1;
105      break;
106    default:
107      usage();
108  }
109  ARGEND
110
111  if (!argc)
112    usage();
113
114  for (; argc--; argv++)
115    ret |= pathchk(*argv);
116
117  return ret;
118}