sbase

suckless unix tools
git clone git://git.suckless.org/sbase
Log | Files | Refs | README | LICENSE

pathchk.c (2341B)


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