Home | History | Annotate | Line # | Download | only in realpath
      1 /*	$NetBSD: realpath.c,v 1.3 2023/05/25 17:24:17 kre Exp $	*/
      2 /*-
      3  * SPDX-License-Identifier: BSD-3-Clause
      4  *
      5  * Copyright (c) 1991, 1993, 1994
      6  *	The Regents of the University of California.  All rights reserved.
      7  *
      8  * Redistribution and use in source and binary forms, with or without
      9  * modification, are permitted provided that the following conditions
     10  * are met:
     11  * 1. Redistributions of source code must retain the above copyright
     12  *    notice, this list of conditions and the following disclaimer.
     13  * 2. Redistributions in binary form must reproduce the above copyright
     14  *    notice, this list of conditions and the following disclaimer in the
     15  *    documentation and/or other materials provided with the distribution.
     16  * 3. Neither the name of the University nor the names of its contributors
     17  *    may be used to endorse or promote products derived from this software
     18  *    without specific prior written permission.
     19  *
     20  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
     21  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     23  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
     24  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     25  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     26  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     29  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     30  * SUCH DAMAGE.
     31  */
     32 
     33 #include <sys/cdefs.h>
     34 #if !defined(lint)
     35 #if 0
     36 __FBSDID("$FreeBSD: head/bin/realpath/realpath.c 326025 2017-11-20 19:49:47Z pfg $");
     37 #else
     38 __RCSID("$NetBSD: realpath.c,v 1.3 2023/05/25 17:24:17 kre Exp $");
     39 #endif
     40 #endif /* not lint */
     41 
     42 #include <sys/param.h>
     43 #include <sys/stat.h>
     44 
     45 #include <err.h>
     46 #include <errno.h>
     47 #include <stdio.h>
     48 #include <stdbool.h>
     49 #include <stdlib.h>
     50 #include <string.h>
     51 #include <unistd.h>
     52 
     53 static bool process(char *path);
     54 static void usage(void) __dead;
     55 
     56 static const char options[] = "Eeq";
     57 
     58 char dot[] = ".";
     59 
     60 bool eflag = false;		/* default to -E mode */
     61 bool qflag = false;
     62 
     63 int
     64 main(int argc, char *argv[])
     65 {
     66 	char *path;
     67 	int ch, rval;
     68 
     69 	setprogname(argv[0]);
     70 
     71 	while ((ch = getopt(argc, argv, options)) != -1) {
     72 		switch (ch) {
     73 		case 'e':
     74 			eflag = true;
     75 			break;
     76 		case 'E':
     77 			eflag = false;
     78 			break;
     79 		case 'q':
     80 			qflag = true;
     81 			break;
     82 		case '?':
     83 		default:
     84 			usage();
     85 		}
     86 	}
     87 	argc -= optind;
     88 	argv += optind;
     89 
     90 	path = *argv != NULL ? *argv++ : dot;
     91 	rval = 0;
     92 	do {
     93 		if (path[0] == '\0') {
     94 			/* ignore -q for this one */
     95 			warnx("Invalid path ''");
     96 			rval = 1;
     97 			continue;
     98 		}
     99 		if (!process(path))
    100 			rval = 1;;
    101 	} while ((path = *argv++) != NULL);
    102 	exit(rval);
    103 }
    104 
    105 static bool
    106 process(char *path)
    107 {
    108 	char buf[PATH_MAX];
    109 	char buf2[sizeof buf];
    110 	char lbuf[PATH_MAX];
    111 	char *pp, *p, *q, *r, *s;
    112 	struct stat sb;
    113 	bool dir_reqd = false;
    114 
    115 	if ((p = realpath(path, buf)) != NULL) {
    116 		(void)printf("%s\n", p);
    117 		return true;
    118 	}
    119 
    120 	if (eflag || errno != ENOENT) {
    121 		if (!qflag)
    122 			warn("%s", path);
    123 		return false;
    124 	}
    125 
    126 	p = strrchr(path, '/');
    127 	while (p != NULL && p > &path[1] && p[1] == '\0') {
    128 		dir_reqd = true;
    129 		*p = '\0';
    130 		p = strrchr(path, '/');
    131 	}
    132 
    133 	if (p == NULL) {
    134 		p = realpath(".", buf);
    135 		if (p == NULL) {
    136 			warnx("relative path; current location unknown");
    137 			return false;
    138 		}
    139 		if ((size_t)snprintf(buf2, sizeof buf2, "%s/%s", buf, path)
    140 		    >= sizeof buf2) {
    141 			if (!qflag)
    142 				warnx("%s/%s: path too long", p, path);
    143 			return false;
    144 		}
    145 		path = buf2;
    146 		p = strrchr(path, '/');
    147 		if (p == NULL)
    148 			abort();
    149 	}
    150 
    151 	*p = '\0';
    152 	pp = ++p;
    153 
    154 	q = path; r = buf; s = buf2;
    155 	while (realpath(*q ? q : "/", r) != NULL) {
    156 		ssize_t llen;
    157 
    158 		if (strcmp(r, "/") == 0 || strcmp(r, "//") == 0)
    159 			r++;
    160 		if ((size_t)snprintf(s, sizeof buf, "%s/%s", r, pp)
    161 		    >= sizeof buf)
    162 			return false;
    163 
    164 		if (lstat(s, &sb) == -1 || !S_ISLNK(sb.st_mode)) {
    165 			(void)printf("%s\n", s);
    166 			return true;
    167 		}
    168 
    169 		q = strchr(r, '\0');
    170 		if (q >= &r[sizeof buf - 3]) {
    171 			*p = '/';
    172 			if (!qflag)
    173 				warnx("Expanded path for %s too long\n", path);
    174 			return false;
    175 		}
    176 
    177 		if ((llen = readlink(s, lbuf, sizeof lbuf - 2)) == -1) {
    178 			*p = '/';
    179 			if (!qflag)
    180 				warn("%s", path);
    181 			return false;
    182 		}
    183 		lbuf[llen] = '\0';
    184 
    185 		if (lbuf[0] == '/') {
    186 			q = lbuf;
    187 			if (dir_reqd) {
    188 				lbuf[llen++] = '/';
    189 				lbuf[llen] = '\0';
    190 			}
    191 		} else {
    192 			if (q != buf2) {
    193 				q = buf2;
    194 				r = buf;
    195 			} else {
    196 				q = buf;
    197 				r = buf2;
    198 			}
    199 
    200 			if ((size_t)snprintf(q, sizeof buf, "%s/%s%s", r, lbuf,
    201 			    (dir_reqd ? "/" : "")) >= sizeof buf) {
    202 				*p = '/';
    203 				if (!qflag)
    204 					warnx("Expanded path for %s too long\n",
    205 					    path);
    206 				return false;
    207 			}
    208 		}
    209 
    210 		s = realpath(q, r);
    211 		if (s != NULL) {
    212 			/* this case should almost never happen (race) */
    213 			(void)printf("%s\n", s);
    214 			return true;
    215 		}
    216 		if (errno != ENOENT) {
    217 			*p = '/';
    218 			if (!qflag)
    219 				warn("%s", path);
    220 			return false;
    221 		}
    222 
    223 		pp = strrchr(q, '/');
    224 		if (pp == NULL) {
    225 			/* we just put one there, where did it go? */
    226 			abort();
    227 		}
    228 		if (dir_reqd) {
    229 			*pp = '\0';
    230 			pp = strrchr(q, '/');
    231 			if (pp == NULL)
    232 				abort();
    233 		}
    234 		*pp++ = '\0';
    235 
    236 		s = q;
    237 	}
    238 
    239 	*p = '/';
    240 
    241 	if (!qflag)
    242 		warn("%s", path);
    243 	return false;
    244 }
    245 
    246 static void
    247 usage(void)
    248 {
    249 
    250 	(void)fprintf(stderr, "usage: %s [-%s] [path ...]\n",
    251 	    getprogname(), options);
    252   	exit(1);
    253 }
    254