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