1 /* $NetBSD: realpath.c,v 1.2 2022/07/21 09:47:31 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.2 2022/07/21 09:47:31 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 ((size_t)snprintf(buf2, sizeof buf2, "%s/%s", buf, path) 136 >= sizeof buf2) { 137 if (!qflag) 138 warnx("%s/%s: path too long", p, path); 139 return false; 140 } 141 path = buf2; 142 p = strrchr(path, '/'); 143 if (p == NULL) 144 abort(); 145 } 146 147 *p = '\0'; 148 pp = ++p; 149 150 q = path; r = buf; s = buf2; 151 while (realpath(*q ? q : "/", r) != NULL) { 152 ssize_t llen; 153 154 if (strcmp(r, "/") == 0 || strcmp(r, "//") == 0) 155 r++; 156 if ((size_t)snprintf(s, sizeof buf, "%s/%s", r, pp) 157 >= sizeof buf) 158 return false; 159 160 if (lstat(s, &sb) == -1 || !S_ISLNK(sb.st_mode)) { 161 (void)printf("%s\n", s); 162 return true; 163 } 164 165 q = strchr(r, '\0'); 166 if (q >= &r[sizeof buf - 3]) { 167 *p = '/'; 168 if (!qflag) 169 warnx("Expanded path for %s too long\n", path); 170 return false; 171 } 172 173 if ((llen = readlink(s, lbuf, sizeof lbuf - 2)) == -1) { 174 *p = '/'; 175 if (!qflag) 176 warn("%s", path); 177 return false; 178 } 179 lbuf[llen] = '\0'; 180 181 if (lbuf[0] == '/') { 182 q = lbuf; 183 if (dir_reqd) { 184 lbuf[llen++] = '/'; 185 lbuf[llen] = '\0'; 186 } 187 } else { 188 if (q != buf2) { 189 q = buf2; 190 r = buf; 191 } else { 192 q = buf; 193 r = buf2; 194 } 195 196 if ((size_t)snprintf(q, sizeof buf, "%s/%s%s", r, lbuf, 197 (dir_reqd ? "/" : "")) >= sizeof buf) { 198 *p = '/'; 199 if (!qflag) 200 warnx("Expanded path for %s too long\n", 201 path); 202 return false; 203 } 204 } 205 206 s = realpath(q, r); 207 if (s != NULL) { 208 /* this case should almost never happen (race) */ 209 (void)printf("%s\n", s); 210 return true; 211 } 212 if (errno != ENOENT) { 213 *p = '/'; 214 if (!qflag) 215 warn("%s", path); 216 return false; 217 } 218 219 pp = strrchr(q, '/'); 220 if (pp == NULL) { 221 /* we just put one there, where did it go? */ 222 abort(); 223 } 224 if (dir_reqd) { 225 *pp = '\0'; 226 pp = strrchr(q, '/'); 227 if (pp == NULL) 228 abort(); 229 } 230 *pp++ = '\0'; 231 232 s = q; 233 } 234 235 *p = '/'; 236 237 if (!qflag) 238 warn("%s", path); 239 return false; 240 } 241 242 static void 243 usage(void) 244 { 245 246 (void)fprintf(stderr, "usage: %s [-%s] [path ...]\n", 247 getprogname(), options); 248 exit(1); 249 } 250