pathchk.c revision 98093
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: head/usr.bin/pathchk/pathchk.c 98093 2002-06-10 10:03:46Z tjr $");
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 */
5497095Stjr
5597095Stjrint
5697095Stjrmain(int argc, char *argv[])
5797095Stjr{
5897095Stjr	int ch, rval;
5997095Stjr	const char *arg;
6097095Stjr
6197095Stjr	while ((ch = getopt(argc, argv, "p")) > 0) {
6297095Stjr		switch (ch) {
6397095Stjr		case 'p':
6497095Stjr			pflag = 1;
6597095Stjr			break;
6697095Stjr		default:
6797095Stjr			usage();
6897095Stjr			/*NOTREACHED*/
6997095Stjr		}
7097095Stjr	}
7197095Stjr	argc -= optind;
7297095Stjr	argv += optind;
7397095Stjr
7497095Stjr	if (argc == 0)
7597095Stjr		usage();
7697095Stjr
7797095Stjr	rval = 0;
7897095Stjr	while ((arg = *argv++) != NULL)
7997095Stjr		rval |= check(arg);
8097095Stjr
8197095Stjr	exit(rval);
8297095Stjr}
8397095Stjr
8497095Stjrstatic void
8597095Stjrusage(void)
8697095Stjr{
8797095Stjr
8897095Stjr	fprintf(stderr, "usage: pathchk [-p] pathname...\n");
8997095Stjr	exit(1);
9097095Stjr}
9197095Stjr
9297095Stjrstatic int
9397095Stjrcheck(const char *path)
9497095Stjr{
9597095Stjr	struct stat sb;
9697095Stjr	long complen, namemax, pathmax, svnamemax;
9797095Stjr	int badch, last;
9897095Stjr	char *end, *p, *pathd;
9997095Stjr
10097095Stjr	if ((pathd = strdup(path)) == NULL)
10197095Stjr		err(1, "strdup");
10297095Stjr
10397095Stjr	p = pathd;
10497095Stjr
10597095Stjr	if (!pflag) {
10697095Stjr		errno = 0;
10797095Stjr		namemax = pathconf(*p == '/' ? "/" : ".", _PC_NAME_MAX);
10897095Stjr		if (namemax == -1 && errno != 0)
10997095Stjr			namemax = NAME_MAX;
11097095Stjr	} else
11197095Stjr		namemax = _POSIX_NAME_MAX;
11297095Stjr
11397095Stjr	for (;;) {
11497095Stjr		p += strspn(p, "/");
11597095Stjr		complen = (long)strcspn(p, "/");
11697095Stjr		end = p + complen;
11797095Stjr		last = *end == '\0';
11897095Stjr		*end = '\0';
11997095Stjr
12097095Stjr		if (namemax != -1 && complen > namemax) {
12197095Stjr			warnx("%s: %s: component too long (limit %ld)", path,
12297095Stjr			    p, namemax);
12397095Stjr			goto bad;
12497095Stjr		}
12597095Stjr
12697095Stjr		if (!pflag && stat(pathd, &sb) == -1 && errno != ENOENT) {
12798093Stjr			warn("%s: %.*s", path, (int)(strlen(pathd) -
12898093Stjr			    complen - 1), pathd);
12997095Stjr			goto bad;
13097095Stjr		}
13197095Stjr
13297095Stjr		if (pflag && (badch = portable(p)) >= 0) {
13397095Stjr			warnx("%s: %s: component contains non-portable "
13497095Stjr			    "character `%c'", path, p, badch);
13597095Stjr			goto bad;
13697095Stjr		}
13797095Stjr
13897095Stjr		if (last)
13997095Stjr			break;
14097095Stjr
14197095Stjr		if (!pflag) {
14297095Stjr			errno = 0;
14397095Stjr			svnamemax = namemax;
14497095Stjr			namemax = pathconf(pathd, _PC_NAME_MAX);
14597095Stjr			if (namemax == -1 && errno != 0)
14697095Stjr				namemax = svnamemax;
14797095Stjr		}
14897095Stjr
14997095Stjr		*end = '/';
15097095Stjr		p = end + 1;
15197095Stjr	}
15297095Stjr
15397095Stjr	if (!pflag) {
15497095Stjr		errno = 0;
15597095Stjr		pathmax = pathconf(path, _PC_PATH_MAX);
15697095Stjr		if (pathmax == -1 && errno != 0)
15797095Stjr			pathmax = PATH_MAX;
15897095Stjr	} else
15997095Stjr		pathmax = _POSIX_PATH_MAX;
16097095Stjr	if (pathmax != -1 && strlen(path) > (size_t)pathmax) {
16197095Stjr		warnx("%s: path too long (limit %ld)", path, pathmax);
16297095Stjr		goto bad;
16397095Stjr	}
16497095Stjr
16597095Stjr	free(pathd);
16697095Stjr	return (0);
16797095Stjr
16897095Stjrbad:	free(pathd);
16997095Stjr	return (1);
17097095Stjr}
17197095Stjr
17297095Stjr/*
17397095Stjr * Check whether a path component contains only portable characters. Return
17497095Stjr * the first non-portable character found.
17597095Stjr */
17697095Stjrstatic int
17797095Stjrportable(const char *path)
17897095Stjr{
17997095Stjr	static const char charset[] =
18097095Stjr	    "abcdefghijklmnopqrstuvwxyz"
18197095Stjr	    "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
18297095Stjr	    "0123456789._-";
18397095Stjr	long s;
18497095Stjr
18597095Stjr	if (*path == '-')
18697095Stjr		return (*path);
18797095Stjr
18897095Stjr	s = strspn(path, charset);
18997095Stjr	if (path[s] != '\0')
19097095Stjr		return (path[s]);
19197095Stjr
19297095Stjr	return (-1);
19397095Stjr}
194