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
main(int argc,char * argv[])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
process(char * path)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
usage(void)247 usage(void)
248 {
249
250 (void)fprintf(stderr, "usage: %s [-%s] [path ...]\n",
251 getprogname(), options);
252 exit(1);
253 }
254