197095Stjr/*-
297095Stjr * Copyright (c) 2002 Tim J. Robbins.
397095Stjr * All rights reserved.
497095Stjr *
597095Stjr * Redistribution and use in source and binary forms, with or without
697095Stjr * modification, are permitted provided that the following conditions
797095Stjr * are met:
897095Stjr * 1. Redistributions of source code must retain the above copyright
997095Stjr *    notice, this list of conditions and the following disclaimer.
1097095Stjr * 2. Redistributions in binary form must reproduce the above copyright
1197095Stjr *    notice, this list of conditions and the following disclaimer in the
1297095Stjr *    documentation and/or other materials provided with the distribution.
1397095Stjr *
1497095Stjr * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1597095Stjr * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1697095Stjr * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1797095Stjr * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1897095Stjr * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1997095Stjr * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2097095Stjr * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2197095Stjr * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2297095Stjr * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2397095Stjr * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2497095Stjr * SUCH DAMAGE.
2597095Stjr */
2697095Stjr
2797095Stjr/*
2897095Stjr * pathchk -- check pathnames
2997095Stjr *
3097095Stjr * Check whether files could be created with the names specified on the
3197095Stjr * command line. If -p is specified, check whether the pathname is portable
3297095Stjr * to all POSIX systems.
3397095Stjr */
3497095Stjr
3597095Stjr#include <sys/cdefs.h>
3697095Stjr__FBSDID("$FreeBSD$");
3797095Stjr
3897095Stjr#include <sys/types.h>
3997095Stjr#include <sys/stat.h>
4097095Stjr
4197095Stjr#include <err.h>
4297095Stjr#include <errno.h>
4397095Stjr#include <limits.h>
4497095Stjr#include <stdio.h>
4597095Stjr#include <stdlib.h>
4697095Stjr#include <string.h>
4797095Stjr#include <unistd.h>
4897095Stjr
4997095Stjrstatic int	 check(const char *);
5097095Stjrstatic int	 portable(const char *);
5197095Stjrstatic void	 usage(void);
5297095Stjr
5397095Stjrstatic int	 pflag;			/* Perform portability checks */
54207483Sjillesstatic int	 Pflag;			/* Check for empty paths, leading '-' */
5597095Stjr
5697095Stjrint
5797095Stjrmain(int argc, char *argv[])
5897095Stjr{
5997095Stjr	int ch, rval;
6097095Stjr	const char *arg;
6197095Stjr
62207483Sjilles	while ((ch = getopt(argc, argv, "pP")) > 0) {
6397095Stjr		switch (ch) {
6497095Stjr		case 'p':
6597095Stjr			pflag = 1;
6697095Stjr			break;
67207483Sjilles		case 'P':
68207483Sjilles			Pflag = 1;
69207483Sjilles			break;
7097095Stjr		default:
7197095Stjr			usage();
7297095Stjr			/*NOTREACHED*/
7397095Stjr		}
7497095Stjr	}
7597095Stjr	argc -= optind;
7697095Stjr	argv += optind;
7797095Stjr
7897095Stjr	if (argc == 0)
7997095Stjr		usage();
8097095Stjr
8197095Stjr	rval = 0;
8297095Stjr	while ((arg = *argv++) != NULL)
8397095Stjr		rval |= check(arg);
8497095Stjr
8597095Stjr	exit(rval);
8697095Stjr}
8797095Stjr
8897095Stjrstatic void
8997095Stjrusage(void)
9097095Stjr{
9197095Stjr
92146466Sru	fprintf(stderr, "usage: pathchk [-p] pathname ...\n");
9397095Stjr	exit(1);
9497095Stjr}
9597095Stjr
9697095Stjrstatic int
9797095Stjrcheck(const char *path)
9897095Stjr{
9997095Stjr	struct stat sb;
10097095Stjr	long complen, namemax, pathmax, svnamemax;
10197095Stjr	int badch, last;
10297095Stjr	char *end, *p, *pathd;
10397095Stjr
10497095Stjr	if ((pathd = strdup(path)) == NULL)
10597095Stjr		err(1, "strdup");
10697095Stjr
10797095Stjr	p = pathd;
10897095Stjr
109207483Sjilles	if (Pflag && *p == '\0') {
110207483Sjilles		warnx("%s: empty pathname", path);
111207483Sjilles		goto bad;
112207483Sjilles	}
113207483Sjilles	if ((Pflag || pflag) && (*p == '-' || strstr(p, "/-") != NULL)) {
114207483Sjilles		warnx("%s: contains a component starting with '-'", path);
115207483Sjilles		goto bad;
116207483Sjilles	}
117207483Sjilles
11897095Stjr	if (!pflag) {
11997095Stjr		errno = 0;
12097095Stjr		namemax = pathconf(*p == '/' ? "/" : ".", _PC_NAME_MAX);
12197095Stjr		if (namemax == -1 && errno != 0)
12297095Stjr			namemax = NAME_MAX;
12397095Stjr	} else
12497095Stjr		namemax = _POSIX_NAME_MAX;
12597095Stjr
12697095Stjr	for (;;) {
12797095Stjr		p += strspn(p, "/");
12897095Stjr		complen = (long)strcspn(p, "/");
12997095Stjr		end = p + complen;
13097095Stjr		last = *end == '\0';
13197095Stjr		*end = '\0';
13297095Stjr
13397095Stjr		if (namemax != -1 && complen > namemax) {
13497095Stjr			warnx("%s: %s: component too long (limit %ld)", path,
13597095Stjr			    p, namemax);
13697095Stjr			goto bad;
13797095Stjr		}
13897095Stjr
13997095Stjr		if (!pflag && stat(pathd, &sb) == -1 && errno != ENOENT) {
14098093Stjr			warn("%s: %.*s", path, (int)(strlen(pathd) -
14198093Stjr			    complen - 1), pathd);
14297095Stjr			goto bad;
14397095Stjr		}
14497095Stjr
14597095Stjr		if (pflag && (badch = portable(p)) >= 0) {
14697095Stjr			warnx("%s: %s: component contains non-portable "
14797095Stjr			    "character `%c'", path, p, badch);
14897095Stjr			goto bad;
14997095Stjr		}
15097095Stjr
15197095Stjr		if (last)
15297095Stjr			break;
15397095Stjr
15497095Stjr		if (!pflag) {
15597095Stjr			errno = 0;
15697095Stjr			svnamemax = namemax;
15797095Stjr			namemax = pathconf(pathd, _PC_NAME_MAX);
15897095Stjr			if (namemax == -1 && errno != 0)
15997095Stjr				namemax = svnamemax;
16097095Stjr		}
16197095Stjr
16297095Stjr		*end = '/';
16397095Stjr		p = end + 1;
16497095Stjr	}
16597095Stjr
16697095Stjr	if (!pflag) {
16797095Stjr		errno = 0;
16897095Stjr		pathmax = pathconf(path, _PC_PATH_MAX);
16997095Stjr		if (pathmax == -1 && errno != 0)
17097095Stjr			pathmax = PATH_MAX;
17197095Stjr	} else
17297095Stjr		pathmax = _POSIX_PATH_MAX;
173107888Stjr	if (pathmax != -1 && strlen(path) >= (size_t)pathmax) {
174107888Stjr		warnx("%s: path too long (limit %ld)", path, pathmax - 1);
17597095Stjr		goto bad;
17697095Stjr	}
17797095Stjr
17897095Stjr	free(pathd);
17997095Stjr	return (0);
18097095Stjr
18197095Stjrbad:	free(pathd);
18297095Stjr	return (1);
18397095Stjr}
18497095Stjr
18597095Stjr/*
18697095Stjr * Check whether a path component contains only portable characters. Return
18797095Stjr * the first non-portable character found.
18897095Stjr */
18997095Stjrstatic int
19097095Stjrportable(const char *path)
19197095Stjr{
19297095Stjr	static const char charset[] =
19397095Stjr	    "abcdefghijklmnopqrstuvwxyz"
19497095Stjr	    "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
19597095Stjr	    "0123456789._-";
19697095Stjr	long s;
19797095Stjr
19897095Stjr	s = strspn(path, charset);
19997095Stjr	if (path[s] != '\0')
20097095Stjr		return (path[s]);
20197095Stjr
20297095Stjr	return (-1);
20397095Stjr}
204