1*d736f87fSkre /* $NetBSD: realpath.c,v 1.3 2023/05/25 17:24:17 kre Exp $ */
2771c4258Skamil /*-
3771c4258Skamil * SPDX-License-Identifier: BSD-3-Clause
4771c4258Skamil *
5771c4258Skamil * Copyright (c) 1991, 1993, 1994
6771c4258Skamil * The Regents of the University of California. All rights reserved.
7771c4258Skamil *
8771c4258Skamil * Redistribution and use in source and binary forms, with or without
9771c4258Skamil * modification, are permitted provided that the following conditions
10771c4258Skamil * are met:
11771c4258Skamil * 1. Redistributions of source code must retain the above copyright
12771c4258Skamil * notice, this list of conditions and the following disclaimer.
13771c4258Skamil * 2. Redistributions in binary form must reproduce the above copyright
14771c4258Skamil * notice, this list of conditions and the following disclaimer in the
15771c4258Skamil * documentation and/or other materials provided with the distribution.
16771c4258Skamil * 3. Neither the name of the University nor the names of its contributors
17771c4258Skamil * may be used to endorse or promote products derived from this software
18771c4258Skamil * without specific prior written permission.
19771c4258Skamil *
20771c4258Skamil * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21771c4258Skamil * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22771c4258Skamil * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23771c4258Skamil * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24771c4258Skamil * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25771c4258Skamil * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26771c4258Skamil * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27771c4258Skamil * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28771c4258Skamil * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29771c4258Skamil * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30771c4258Skamil * SUCH DAMAGE.
31771c4258Skamil */
32771c4258Skamil
33771c4258Skamil #include <sys/cdefs.h>
34771c4258Skamil #if !defined(lint)
35771c4258Skamil #if 0
36771c4258Skamil __FBSDID("$FreeBSD: head/bin/realpath/realpath.c 326025 2017-11-20 19:49:47Z pfg $");
37771c4258Skamil #else
38*d736f87fSkre __RCSID("$NetBSD: realpath.c,v 1.3 2023/05/25 17:24:17 kre Exp $");
39771c4258Skamil #endif
40771c4258Skamil #endif /* not lint */
41771c4258Skamil
42771c4258Skamil #include <sys/param.h>
43245b5ea4Skre #include <sys/stat.h>
44771c4258Skamil
45771c4258Skamil #include <err.h>
46245b5ea4Skre #include <errno.h>
47771c4258Skamil #include <stdio.h>
48245b5ea4Skre #include <stdbool.h>
49771c4258Skamil #include <stdlib.h>
50245b5ea4Skre #include <string.h>
51771c4258Skamil #include <unistd.h>
52771c4258Skamil
53245b5ea4Skre static bool process(char *path);
54771c4258Skamil static void usage(void) __dead;
55771c4258Skamil
56245b5ea4Skre static const char options[] = "Eeq";
57245b5ea4Skre
58245b5ea4Skre char dot[] = ".";
59245b5ea4Skre
60245b5ea4Skre bool eflag = false; /* default to -E mode */
61245b5ea4Skre bool qflag = false;
62245b5ea4Skre
63771c4258Skamil int
main(int argc,char * argv[])64771c4258Skamil main(int argc, char *argv[])
65771c4258Skamil {
66245b5ea4Skre char *path;
67245b5ea4Skre int ch, rval;
68771c4258Skamil
69771c4258Skamil setprogname(argv[0]);
70771c4258Skamil
71245b5ea4Skre while ((ch = getopt(argc, argv, options)) != -1) {
72771c4258Skamil switch (ch) {
73245b5ea4Skre case 'e':
74245b5ea4Skre eflag = true;
75245b5ea4Skre break;
76245b5ea4Skre case 'E':
77245b5ea4Skre eflag = false;
78245b5ea4Skre break;
79771c4258Skamil case 'q':
80245b5ea4Skre qflag = true;
81771c4258Skamil break;
82771c4258Skamil case '?':
83771c4258Skamil default:
84771c4258Skamil usage();
85771c4258Skamil }
86771c4258Skamil }
87771c4258Skamil argc -= optind;
88771c4258Skamil argv += optind;
89245b5ea4Skre
90245b5ea4Skre path = *argv != NULL ? *argv++ : dot;
91771c4258Skamil rval = 0;
92771c4258Skamil do {
93245b5ea4Skre if (path[0] == '\0') {
94245b5ea4Skre /* ignore -q for this one */
95245b5ea4Skre warnx("Invalid path ''");
96771c4258Skamil rval = 1;
97245b5ea4Skre continue;
98245b5ea4Skre }
99245b5ea4Skre if (!process(path))
100245b5ea4Skre rval = 1;;
101771c4258Skamil } while ((path = *argv++) != NULL);
102771c4258Skamil exit(rval);
103771c4258Skamil }
104771c4258Skamil
105245b5ea4Skre static bool
process(char * path)106245b5ea4Skre process(char *path)
107245b5ea4Skre {
108245b5ea4Skre char buf[PATH_MAX];
109245b5ea4Skre char buf2[sizeof buf];
110245b5ea4Skre char lbuf[PATH_MAX];
111245b5ea4Skre char *pp, *p, *q, *r, *s;
112245b5ea4Skre struct stat sb;
113245b5ea4Skre bool dir_reqd = false;
114245b5ea4Skre
115245b5ea4Skre if ((p = realpath(path, buf)) != NULL) {
116245b5ea4Skre (void)printf("%s\n", p);
117245b5ea4Skre return true;
118245b5ea4Skre }
119245b5ea4Skre
120245b5ea4Skre if (eflag || errno != ENOENT) {
121245b5ea4Skre if (!qflag)
122245b5ea4Skre warn("%s", path);
123245b5ea4Skre return false;
124245b5ea4Skre }
125245b5ea4Skre
126245b5ea4Skre p = strrchr(path, '/');
127245b5ea4Skre while (p != NULL && p > &path[1] && p[1] == '\0') {
128245b5ea4Skre dir_reqd = true;
129245b5ea4Skre *p = '\0';
130245b5ea4Skre p = strrchr(path, '/');
131245b5ea4Skre }
132245b5ea4Skre
133245b5ea4Skre if (p == NULL) {
134245b5ea4Skre p = realpath(".", buf);
135*d736f87fSkre if (p == NULL) {
136*d736f87fSkre warnx("relative path; current location unknown");
137*d736f87fSkre return false;
138*d736f87fSkre }
139245b5ea4Skre if ((size_t)snprintf(buf2, sizeof buf2, "%s/%s", buf, path)
140245b5ea4Skre >= sizeof buf2) {
141245b5ea4Skre if (!qflag)
142245b5ea4Skre warnx("%s/%s: path too long", p, path);
143245b5ea4Skre return false;
144245b5ea4Skre }
145245b5ea4Skre path = buf2;
146245b5ea4Skre p = strrchr(path, '/');
147245b5ea4Skre if (p == NULL)
148245b5ea4Skre abort();
149245b5ea4Skre }
150245b5ea4Skre
151245b5ea4Skre *p = '\0';
152245b5ea4Skre pp = ++p;
153245b5ea4Skre
154245b5ea4Skre q = path; r = buf; s = buf2;
155245b5ea4Skre while (realpath(*q ? q : "/", r) != NULL) {
156245b5ea4Skre ssize_t llen;
157245b5ea4Skre
158245b5ea4Skre if (strcmp(r, "/") == 0 || strcmp(r, "//") == 0)
159245b5ea4Skre r++;
160245b5ea4Skre if ((size_t)snprintf(s, sizeof buf, "%s/%s", r, pp)
161245b5ea4Skre >= sizeof buf)
162245b5ea4Skre return false;
163245b5ea4Skre
164245b5ea4Skre if (lstat(s, &sb) == -1 || !S_ISLNK(sb.st_mode)) {
165245b5ea4Skre (void)printf("%s\n", s);
166245b5ea4Skre return true;
167245b5ea4Skre }
168245b5ea4Skre
169245b5ea4Skre q = strchr(r, '\0');
170245b5ea4Skre if (q >= &r[sizeof buf - 3]) {
171245b5ea4Skre *p = '/';
172245b5ea4Skre if (!qflag)
173245b5ea4Skre warnx("Expanded path for %s too long\n", path);
174245b5ea4Skre return false;
175245b5ea4Skre }
176245b5ea4Skre
177245b5ea4Skre if ((llen = readlink(s, lbuf, sizeof lbuf - 2)) == -1) {
178245b5ea4Skre *p = '/';
179245b5ea4Skre if (!qflag)
180245b5ea4Skre warn("%s", path);
181245b5ea4Skre return false;
182245b5ea4Skre }
183245b5ea4Skre lbuf[llen] = '\0';
184245b5ea4Skre
185245b5ea4Skre if (lbuf[0] == '/') {
186245b5ea4Skre q = lbuf;
187245b5ea4Skre if (dir_reqd) {
188245b5ea4Skre lbuf[llen++] = '/';
189245b5ea4Skre lbuf[llen] = '\0';
190245b5ea4Skre }
191245b5ea4Skre } else {
192245b5ea4Skre if (q != buf2) {
193245b5ea4Skre q = buf2;
194245b5ea4Skre r = buf;
195245b5ea4Skre } else {
196245b5ea4Skre q = buf;
197245b5ea4Skre r = buf2;
198245b5ea4Skre }
199245b5ea4Skre
200245b5ea4Skre if ((size_t)snprintf(q, sizeof buf, "%s/%s%s", r, lbuf,
201245b5ea4Skre (dir_reqd ? "/" : "")) >= sizeof buf) {
202245b5ea4Skre *p = '/';
203245b5ea4Skre if (!qflag)
204245b5ea4Skre warnx("Expanded path for %s too long\n",
205245b5ea4Skre path);
206245b5ea4Skre return false;
207245b5ea4Skre }
208245b5ea4Skre }
209245b5ea4Skre
210245b5ea4Skre s = realpath(q, r);
211245b5ea4Skre if (s != NULL) {
212245b5ea4Skre /* this case should almost never happen (race) */
213245b5ea4Skre (void)printf("%s\n", s);
214245b5ea4Skre return true;
215245b5ea4Skre }
216245b5ea4Skre if (errno != ENOENT) {
217245b5ea4Skre *p = '/';
218245b5ea4Skre if (!qflag)
219245b5ea4Skre warn("%s", path);
220245b5ea4Skre return false;
221245b5ea4Skre }
222245b5ea4Skre
223245b5ea4Skre pp = strrchr(q, '/');
224245b5ea4Skre if (pp == NULL) {
225245b5ea4Skre /* we just put one there, where did it go? */
226245b5ea4Skre abort();
227245b5ea4Skre }
228245b5ea4Skre if (dir_reqd) {
229245b5ea4Skre *pp = '\0';
230245b5ea4Skre pp = strrchr(q, '/');
231245b5ea4Skre if (pp == NULL)
232245b5ea4Skre abort();
233245b5ea4Skre }
234245b5ea4Skre *pp++ = '\0';
235245b5ea4Skre
236245b5ea4Skre s = q;
237245b5ea4Skre }
238245b5ea4Skre
239245b5ea4Skre *p = '/';
240245b5ea4Skre
241245b5ea4Skre if (!qflag)
242245b5ea4Skre warn("%s", path);
243245b5ea4Skre return false;
244245b5ea4Skre }
245245b5ea4Skre
246771c4258Skamil static void
usage(void)247771c4258Skamil usage(void)
248771c4258Skamil {
249771c4258Skamil
250245b5ea4Skre (void)fprintf(stderr, "usage: %s [-%s] [path ...]\n",
251245b5ea4Skre getprogname(), options);
252771c4258Skamil exit(1);
253771c4258Skamil }
254