realpath.c revision 1.2
11.2Skre/*	$NetBSD: realpath.c,v 1.2 2022/07/21 09:47:31 kre Exp $	*/
21.1Skamil/*-
31.1Skamil * SPDX-License-Identifier: BSD-3-Clause
41.1Skamil *
51.1Skamil * Copyright (c) 1991, 1993, 1994
61.1Skamil *	The Regents of the University of California.  All rights reserved.
71.1Skamil *
81.1Skamil * Redistribution and use in source and binary forms, with or without
91.1Skamil * modification, are permitted provided that the following conditions
101.1Skamil * are met:
111.1Skamil * 1. Redistributions of source code must retain the above copyright
121.1Skamil *    notice, this list of conditions and the following disclaimer.
131.1Skamil * 2. Redistributions in binary form must reproduce the above copyright
141.1Skamil *    notice, this list of conditions and the following disclaimer in the
151.1Skamil *    documentation and/or other materials provided with the distribution.
161.1Skamil * 3. Neither the name of the University nor the names of its contributors
171.1Skamil *    may be used to endorse or promote products derived from this software
181.1Skamil *    without specific prior written permission.
191.1Skamil *
201.1Skamil * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
211.1Skamil * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
221.1Skamil * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
231.1Skamil * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
241.1Skamil * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
251.1Skamil * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
261.1Skamil * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
271.1Skamil * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
281.1Skamil * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
291.1Skamil * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
301.1Skamil * SUCH DAMAGE.
311.1Skamil */
321.1Skamil
331.1Skamil#include <sys/cdefs.h>
341.1Skamil#if !defined(lint)
351.1Skamil#if 0
361.1Skamil__FBSDID("$FreeBSD: head/bin/realpath/realpath.c 326025 2017-11-20 19:49:47Z pfg $");
371.1Skamil#else
381.2Skre__RCSID("$NetBSD: realpath.c,v 1.2 2022/07/21 09:47:31 kre Exp $");
391.1Skamil#endif
401.1Skamil#endif /* not lint */
411.1Skamil
421.1Skamil#include <sys/param.h>
431.2Skre#include <sys/stat.h>
441.1Skamil
451.1Skamil#include <err.h>
461.2Skre#include <errno.h>
471.1Skamil#include <stdio.h>
481.2Skre#include <stdbool.h>
491.1Skamil#include <stdlib.h>
501.2Skre#include <string.h>
511.1Skamil#include <unistd.h>
521.1Skamil
531.2Skrestatic bool process(char *path);
541.1Skamilstatic void usage(void) __dead;
551.1Skamil
561.2Skrestatic const char options[] = "Eeq";
571.2Skre
581.2Skrechar dot[] = ".";
591.2Skre
601.2Skrebool eflag = false;		/* default to -E mode */
611.2Skrebool qflag = false;
621.2Skre
631.1Skamilint
641.1Skamilmain(int argc, char *argv[])
651.1Skamil{
661.2Skre	char *path;
671.2Skre	int ch, rval;
681.1Skamil
691.1Skamil	setprogname(argv[0]);
701.1Skamil
711.2Skre	while ((ch = getopt(argc, argv, options)) != -1) {
721.1Skamil		switch (ch) {
731.2Skre		case 'e':
741.2Skre			eflag = true;
751.2Skre			break;
761.2Skre		case 'E':
771.2Skre			eflag = false;
781.2Skre			break;
791.1Skamil		case 'q':
801.2Skre			qflag = true;
811.1Skamil			break;
821.1Skamil		case '?':
831.1Skamil		default:
841.1Skamil			usage();
851.1Skamil		}
861.1Skamil	}
871.1Skamil	argc -= optind;
881.1Skamil	argv += optind;
891.2Skre
901.2Skre	path = *argv != NULL ? *argv++ : dot;
911.2Skre	rval = 0;
921.1Skamil	do {
931.2Skre		if (path[0] == '\0') {
941.2Skre			/* ignore -q for this one */
951.2Skre			warnx("Invalid path ''");
961.1Skamil			rval = 1;
971.2Skre			continue;
981.2Skre		}
991.2Skre		if (!process(path))
1001.2Skre			rval = 1;;
1011.1Skamil	} while ((path = *argv++) != NULL);
1021.1Skamil	exit(rval);
1031.1Skamil}
1041.1Skamil
1051.2Skrestatic bool
1061.2Skreprocess(char *path)
1071.2Skre{
1081.2Skre	char buf[PATH_MAX];
1091.2Skre	char buf2[sizeof buf];
1101.2Skre	char lbuf[PATH_MAX];
1111.2Skre	char *pp, *p, *q, *r, *s;
1121.2Skre	struct stat sb;
1131.2Skre	bool dir_reqd = false;
1141.2Skre
1151.2Skre	if ((p = realpath(path, buf)) != NULL) {
1161.2Skre		(void)printf("%s\n", p);
1171.2Skre		return true;
1181.2Skre	}
1191.2Skre
1201.2Skre	if (eflag || errno != ENOENT) {
1211.2Skre		if (!qflag)
1221.2Skre			warn("%s", path);
1231.2Skre		return false;
1241.2Skre	}
1251.2Skre
1261.2Skre	p = strrchr(path, '/');
1271.2Skre	while (p != NULL && p > &path[1] && p[1] == '\0') {
1281.2Skre		dir_reqd = true;
1291.2Skre		*p = '\0';
1301.2Skre		p = strrchr(path, '/');
1311.2Skre	}
1321.2Skre
1331.2Skre	if (p == NULL) {
1341.2Skre		p = realpath(".", buf);
1351.2Skre		if ((size_t)snprintf(buf2, sizeof buf2, "%s/%s", buf, path)
1361.2Skre		    >= sizeof buf2) {
1371.2Skre			if (!qflag)
1381.2Skre				warnx("%s/%s: path too long", p, path);
1391.2Skre			return false;
1401.2Skre		}
1411.2Skre		path = buf2;
1421.2Skre		p = strrchr(path, '/');
1431.2Skre		if (p == NULL)
1441.2Skre			abort();
1451.2Skre	}
1461.2Skre
1471.2Skre	*p = '\0';
1481.2Skre	pp = ++p;
1491.2Skre
1501.2Skre	q = path; r = buf; s = buf2;
1511.2Skre	while (realpath(*q ? q : "/", r) != NULL) {
1521.2Skre		ssize_t llen;
1531.2Skre
1541.2Skre		if (strcmp(r, "/") == 0 || strcmp(r, "//") == 0)
1551.2Skre			r++;
1561.2Skre		if ((size_t)snprintf(s, sizeof buf, "%s/%s", r, pp)
1571.2Skre		    >= sizeof buf)
1581.2Skre			return false;
1591.2Skre
1601.2Skre		if (lstat(s, &sb) == -1 || !S_ISLNK(sb.st_mode)) {
1611.2Skre			(void)printf("%s\n", s);
1621.2Skre			return true;
1631.2Skre		}
1641.2Skre
1651.2Skre		q = strchr(r, '\0');
1661.2Skre		if (q >= &r[sizeof buf - 3]) {
1671.2Skre			*p = '/';
1681.2Skre			if (!qflag)
1691.2Skre				warnx("Expanded path for %s too long\n", path);
1701.2Skre			return false;
1711.2Skre		}
1721.2Skre
1731.2Skre		if ((llen = readlink(s, lbuf, sizeof lbuf - 2)) == -1) {
1741.2Skre			*p = '/';
1751.2Skre			if (!qflag)
1761.2Skre				warn("%s", path);
1771.2Skre			return false;
1781.2Skre		}
1791.2Skre		lbuf[llen] = '\0';
1801.2Skre
1811.2Skre		if (lbuf[0] == '/') {
1821.2Skre			q = lbuf;
1831.2Skre			if (dir_reqd) {
1841.2Skre				lbuf[llen++] = '/';
1851.2Skre				lbuf[llen] = '\0';
1861.2Skre			}
1871.2Skre		} else {
1881.2Skre			if (q != buf2) {
1891.2Skre				q = buf2;
1901.2Skre				r = buf;
1911.2Skre			} else {
1921.2Skre				q = buf;
1931.2Skre				r = buf2;
1941.2Skre			}
1951.2Skre
1961.2Skre			if ((size_t)snprintf(q, sizeof buf, "%s/%s%s", r, lbuf,
1971.2Skre			    (dir_reqd ? "/" : "")) >= sizeof buf) {
1981.2Skre				*p = '/';
1991.2Skre				if (!qflag)
2001.2Skre					warnx("Expanded path for %s too long\n",
2011.2Skre					    path);
2021.2Skre				return false;
2031.2Skre			}
2041.2Skre		}
2051.2Skre
2061.2Skre		s = realpath(q, r);
2071.2Skre		if (s != NULL) {
2081.2Skre			/* this case should almost never happen (race) */
2091.2Skre			(void)printf("%s\n", s);
2101.2Skre			return true;
2111.2Skre		}
2121.2Skre		if (errno != ENOENT) {
2131.2Skre			*p = '/';
2141.2Skre			if (!qflag)
2151.2Skre				warn("%s", path);
2161.2Skre			return false;
2171.2Skre		}
2181.2Skre
2191.2Skre		pp = strrchr(q, '/');
2201.2Skre		if (pp == NULL) {
2211.2Skre			/* we just put one there, where did it go? */
2221.2Skre			abort();
2231.2Skre		}
2241.2Skre		if (dir_reqd) {
2251.2Skre			*pp = '\0';
2261.2Skre			pp = strrchr(q, '/');
2271.2Skre			if (pp == NULL)
2281.2Skre				abort();
2291.2Skre		}
2301.2Skre		*pp++ = '\0';
2311.2Skre
2321.2Skre		s = q;
2331.2Skre	}
2341.2Skre
2351.2Skre	*p = '/';
2361.2Skre
2371.2Skre	if (!qflag)
2381.2Skre		warn("%s", path);
2391.2Skre	return false;
2401.2Skre}
2411.2Skre
2421.1Skamilstatic void
2431.1Skamilusage(void)
2441.1Skamil{
2451.1Skamil
2461.2Skre	(void)fprintf(stderr, "usage: %s [-%s] [path ...]\n",
2471.2Skre	    getprogname(), options);
2481.1Skamil  	exit(1);
2491.1Skamil}
250