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