11.3Skre/* $NetBSD: realpath.c,v 1.3 2023/05/25 17:24:17 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.3Skre__RCSID("$NetBSD: realpath.c,v 1.3 2023/05/25 17:24:17 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.3Skre if (p == NULL) { 1361.3Skre warnx("relative path; current location unknown"); 1371.3Skre return false; 1381.3Skre } 1391.2Skre if ((size_t)snprintf(buf2, sizeof buf2, "%s/%s", buf, path) 1401.2Skre >= sizeof buf2) { 1411.2Skre if (!qflag) 1421.2Skre warnx("%s/%s: path too long", p, path); 1431.2Skre return false; 1441.2Skre } 1451.2Skre path = buf2; 1461.2Skre p = strrchr(path, '/'); 1471.2Skre if (p == NULL) 1481.2Skre abort(); 1491.2Skre } 1501.2Skre 1511.2Skre *p = '\0'; 1521.2Skre pp = ++p; 1531.2Skre 1541.2Skre q = path; r = buf; s = buf2; 1551.2Skre while (realpath(*q ? q : "/", r) != NULL) { 1561.2Skre ssize_t llen; 1571.2Skre 1581.2Skre if (strcmp(r, "/") == 0 || strcmp(r, "//") == 0) 1591.2Skre r++; 1601.2Skre if ((size_t)snprintf(s, sizeof buf, "%s/%s", r, pp) 1611.2Skre >= sizeof buf) 1621.2Skre return false; 1631.2Skre 1641.2Skre if (lstat(s, &sb) == -1 || !S_ISLNK(sb.st_mode)) { 1651.2Skre (void)printf("%s\n", s); 1661.2Skre return true; 1671.2Skre } 1681.2Skre 1691.2Skre q = strchr(r, '\0'); 1701.2Skre if (q >= &r[sizeof buf - 3]) { 1711.2Skre *p = '/'; 1721.2Skre if (!qflag) 1731.2Skre warnx("Expanded path for %s too long\n", path); 1741.2Skre return false; 1751.2Skre } 1761.2Skre 1771.2Skre if ((llen = readlink(s, lbuf, sizeof lbuf - 2)) == -1) { 1781.2Skre *p = '/'; 1791.2Skre if (!qflag) 1801.2Skre warn("%s", path); 1811.2Skre return false; 1821.2Skre } 1831.2Skre lbuf[llen] = '\0'; 1841.2Skre 1851.2Skre if (lbuf[0] == '/') { 1861.2Skre q = lbuf; 1871.2Skre if (dir_reqd) { 1881.2Skre lbuf[llen++] = '/'; 1891.2Skre lbuf[llen] = '\0'; 1901.2Skre } 1911.2Skre } else { 1921.2Skre if (q != buf2) { 1931.2Skre q = buf2; 1941.2Skre r = buf; 1951.2Skre } else { 1961.2Skre q = buf; 1971.2Skre r = buf2; 1981.2Skre } 1991.2Skre 2001.2Skre if ((size_t)snprintf(q, sizeof buf, "%s/%s%s", r, lbuf, 2011.2Skre (dir_reqd ? "/" : "")) >= sizeof buf) { 2021.2Skre *p = '/'; 2031.2Skre if (!qflag) 2041.2Skre warnx("Expanded path for %s too long\n", 2051.2Skre path); 2061.2Skre return false; 2071.2Skre } 2081.2Skre } 2091.2Skre 2101.2Skre s = realpath(q, r); 2111.2Skre if (s != NULL) { 2121.2Skre /* this case should almost never happen (race) */ 2131.2Skre (void)printf("%s\n", s); 2141.2Skre return true; 2151.2Skre } 2161.2Skre if (errno != ENOENT) { 2171.2Skre *p = '/'; 2181.2Skre if (!qflag) 2191.2Skre warn("%s", path); 2201.2Skre return false; 2211.2Skre } 2221.2Skre 2231.2Skre pp = strrchr(q, '/'); 2241.2Skre if (pp == NULL) { 2251.2Skre /* we just put one there, where did it go? */ 2261.2Skre abort(); 2271.2Skre } 2281.2Skre if (dir_reqd) { 2291.2Skre *pp = '\0'; 2301.2Skre pp = strrchr(q, '/'); 2311.2Skre if (pp == NULL) 2321.2Skre abort(); 2331.2Skre } 2341.2Skre *pp++ = '\0'; 2351.2Skre 2361.2Skre s = q; 2371.2Skre } 2381.2Skre 2391.2Skre *p = '/'; 2401.2Skre 2411.2Skre if (!qflag) 2421.2Skre warn("%s", path); 2431.2Skre return false; 2441.2Skre} 2451.2Skre 2461.1Skamilstatic void 2471.1Skamilusage(void) 2481.1Skamil{ 2491.1Skamil 2501.2Skre (void)fprintf(stderr, "usage: %s [-%s] [path ...]\n", 2511.2Skre getprogname(), options); 2521.1Skamil exit(1); 2531.1Skamil} 254