master xplshn/aruu / cmd / posix / pathchk.c
  1/* See LICENSE file for copyright and license details. */
  2
  3
  4#include <sys/stat.h>
  5
  6#include <errno.h>
  7#include <limits.h>
  8#include <stdint.h>
  9#include <stdio.h>
 10#include <stdlib.h>
 11#include <string.h>
 12
 13#include "util.h"
 14
 15#define PORTABLE_CHARACTER_SET "0123456789._-qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM"
 16/* If your system supports more other characters, but not all non-NUL 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		p = estrdup(filename);
 48		*invalid_end = 0;
 49		eprintf("%s: nonportable character '%s'\n", p, invalid);
 50	}
 51
 52	/* Symlink error? Non-searchable directory? */
 53	if (lstat(filename, &st) && errno != ENOENT) {
 54		/* lstat rather than stat, so that if filename is a bad symlink, but
 55		 * all parents are OK, no error will be detected. */
 56		eprintf("%s:", filename);
 57	}
 58
 59	/* Too long pathname? */
 60	maxlen = most ? _POSIX_PATH_MAX : PATH_MAX;
 61	if (strlen(filename) >= maxlen)
 62		eprintf("%s: is longer than %zu bytes\n", filename, maxlen);
 63
 64	/* Too long component? */
 65	maxlen = most ? _POSIX_NAME_MAX : NAME_MAX;
 66	for (p = filename; p; p = q) {
 67		q = strchr(p, '/');
 68		len = q ? (size_t)(q++ - p) : strlen(p);
 69		if (len > maxlen)
 70			eprintf("%s: includes component longer than %zu bytes\n",
 71			         filename, maxlen);
 72	}
 73
 74	return 0;
 75}
 76
 77static void
 78usage(void)
 79{
 80	eprintf("usage: %s [-pP] filename...\n", argv0);
 81}
 82
 83// ?man pathchk: check pathnames
 84// ?man arguments: filename...
 85// ?man verify that pathnames are valid and portable
 86int
 87main(int argc, char *argv[])
 88{
 89	int ret = 0;
 90
 91	ARGBEGIN {
 92	// ?man -p: preserve file attributes
 93	case 'p':
 94		most = 1;
 95		break;
 96	// ?man -P: specify option flag
 97	case 'P':
 98		extra = 1;
 99		break;
100	default:
101		usage();
102	} ARGEND
103
104	if (!argc)
105		usage();
106
107	for (; argc--; argv++)
108		ret |= pathchk(*argv);
109
110	return ret;
111}