XBPS Library API 20240111
The X Binary Package System
util_path.c
1/*-
2 * Copyright (c) 2015-2019 Juan Romero Pardines.
3 * Copyright (c) 2020 Duncan Overbruck <mail@duncano.de>.
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 */
26/*-
27 * xbps_path_clean is based on the go filepath.Clean function:
28 * - https://github.com/golang/go/blob/cfe2ab42/src/path/filepath/path.go#L88
29 *
30 * Copyright (c) 2009 The Go Authors. All rights reserved.
31 *
32 * Redistribution and use in source and binary forms, with or without
33 * modification, are permitted provided that the following conditions are
34 * met:
35 *
36 * * Redistributions of source code must retain the above copyright
37 * notice, this list of conditions and the following disclaimer.
38 * * Redistributions in binary form must reproduce the above
39 * copyright notice, this list of conditions and the following disclaimer
40 * in the documentation and/or other materials provided with the
41 * distribution.
42 * * Neither the name of Google Inc. nor the names of its
43 * contributors may be used to endorse or promote products derived from
44 * this software without specific prior written permission.
45 *
46 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
47 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
48 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
49 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
50 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
51 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
52 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
53 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
54 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
55 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
56 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
57 */
58
59#include <errno.h>
60#include <limits.h>
61#include <stdlib.h>
62#include <string.h>
63
64#include "xbps_api_impl.h"
65
66ssize_t
68{
69 char buf[PATH_MAX];
70 const char *p = buf;
71 const char *dotdot = dst;
72 char *d = dst;
73 bool rooted = *dst == '/';
74
75 if (xbps_strlcpy(buf, dst, sizeof buf) >= sizeof buf)
76 return -1;
77
78 if (rooted) {
79 *d++ = '/';
80 p++;
81 dotdot++;
82 }
83
84 for (; *p;) {
85 switch (*p) {
86 /* empty path element */
87 case '/': p++; break;
88 case '.':
89 if (p[1] == '\0' || p[1] == '/') {
90 /* . element */
91 p++;
92 continue;
93 } else if (p[1] == '.' && (p[2] == '\0' || p[2] == '/')) {
94 p += 2;
95 /* .. element */
96 if (d > dotdot) {
97 /* can backtrack */
98 d--;
99 for (; d > dotdot && *d != '/'; d--)
100 ;
101 } else if (!rooted) {
102 /* cannot backtrack, but not rooted,
103 * append .. element. */
104 if (d > dst)
105 *d++ = '/';
106 *d++ = '.';
107 *d++ = '.';
108 dotdot = d;
109 }
110 continue;
111 }
112 /* normal path element starting with . */
113 /* FALLTHROUGH */
114 default:
115 if (d > dst+(rooted ? 1 : 0))
116 *d++ = '/';
117 for (; *p && *p != '/'; p++)
118 *d++ = *p;
119 }
120 }
121
122 /* Turn empty string into "." */
123 if (d == dst)
124 *d++ = '.';
125
126 *d = '\0';
127 return (d-dst);
128}
129
130ssize_t
131xbps_path_rel(char *dst, size_t dstlen, const char *from, const char *to)
132{
133 char frombuf[PATH_MAX], tobuf[PATH_MAX];
134 const char *fromp = frombuf, *top = tobuf, *suffix = tobuf;
135 size_t len = 0;
136 int up = -1;
137
138 *dst = '\0';
139
140 if (xbps_strlcpy(frombuf, from, sizeof frombuf) >= sizeof frombuf ||
141 xbps_strlcpy(tobuf, to, sizeof tobuf) >= sizeof tobuf)
142 return -1;
143
144 if (xbps_path_clean(frombuf) == -1 || xbps_path_clean(tobuf) == -1)
145 return -1;
146
147 for (; *fromp == *top && *to; fromp++, top++)
148 if (*top == '/')
149 suffix = top;
150
151 for (up = -1, fromp--; fromp && *fromp; fromp = strchr(fromp+1, '/'), up++)
152 ;
153
154 while (up--) {
155 for (const char *x = "../"; *x; x++) {
156 if (len+1 < dstlen)
157 dst[len] = *x;
158 len++;
159 }
160 }
161 if (*suffix != '\0') {
162 for (suffix += 1; *suffix; suffix++) {
163 if (len+1 < dstlen)
164 dst[len] = *suffix;
165 len++;
166 }
167 }
168
169 dst[len < dstlen ? len : dstlen - 1] = '\0';
170 return len;
171}
172
173static ssize_t
174xbps_path_vjoin(char *dst, size_t dstlen, va_list ap)
175{
176 size_t len = 0;
177 const char *val;
178 *dst = '\0';
179
180 if ((val = va_arg(ap, const char *)) == NULL)
181 return 0;
182
183 for (;;) {
184 size_t n;
185 if ((n = xbps_strlcat(dst+len, val, dstlen-len)) >= dstlen-len)
186 goto err;
187 len += n;
188 if ((val = va_arg(ap, const char *)) == NULL)
189 break;
190 if (len > 0 && dst[len-1] != '/') {
191 if (len+1 > dstlen)
192 goto err;
193 dst[len] = '/';
194 dst[len+1] = '\0';
195 len++;
196 }
197 if (len > 0 && *val == '/')
198 val++;
199 }
200
201 return (ssize_t)len < 0 ? -1 : (ssize_t)len;
202err:
203 errno = ENOBUFS;
204 return -1;
205}
206
207ssize_t
208xbps_path_join(char *dst, size_t dstlen, ...)
209{
210 ssize_t len;
211 va_list ap;
212 va_start(ap, dstlen);
213 len = xbps_path_vjoin(dst, dstlen, ap);
214 va_end(ap);
215 return len;
216}
217
218ssize_t
219xbps_path_append(char *dst, size_t dstlen, const char *suffix)
220{
221 size_t len = strlen(dst);
222
223 if (*suffix == '\0')
224 goto out;
225
226 if (*dst == '\0') {
227 if ((len = xbps_strlcpy(dst, suffix, dstlen)) >= dstlen)
228 goto err;
229 goto out;
230 }
231
232 if (dst[len-1] != '/' && len+1 < dstlen) {
233 dst[len] = '/';
234 dst[len+1] = '\0';
235 }
236 if (*suffix == '/')
237 suffix++;
238
239 if ((len = xbps_strlcat(dst, suffix, dstlen)) >= dstlen)
240 goto err;
241out:
242 return (ssize_t)len < 0 ? -1 : (ssize_t)len;
243err:
244 errno = ENOBUFS;
245 return -1;
246}
247
248ssize_t
249xbps_path_prepend(char *dst, size_t dstlen, const char *prefix)
250{
251 size_t len, prelen;
252 char *p = dst;
253
254 len = strlen(dst);
255
256 if (*prefix == '\0')
257 goto out;
258
259 if (*dst == '\0') {
260 if ((len = xbps_strlcpy(dst, prefix, dstlen)) >= dstlen)
261 goto err;
262 goto out;
263 }
264
265 prelen = strlen(prefix);
266 if (prefix[prelen-1] == '/')
267 prelen--;
268
269 if (*dst == '/') {
270 len--;
271 p++;
272 }
273
274 /* prefix + '/' + dst + '\0' */
275 if (len+prelen+2 > dstlen)
276 goto err;
277
278 memmove(dst+prelen+1, p, len);
279
280 len += prelen+1;
281
282 dst[prelen] = '/';
283
284 memcpy(dst, prefix, prelen);
285
286 dst[len] = '\0';
287out:
288 return (ssize_t)len < 0 ? -1 : (ssize_t)len;
289err:
290 errno = ENOBUFS;
291 return -1;
292}
ssize_t xbps_path_append(char *dst, size_t dstlen, const char *suffix)
Definition util_path.c:219
size_t xbps_strlcpy(char *dst, const char *src, size_t dstsize)
Definition util.c:575
ssize_t xbps_path_clean(char *dst)
Definition util_path.c:67
ssize_t xbps_path_prepend(char *dst, size_t dstlen, const char *prefix)
Definition util_path.c:249
ssize_t xbps_path_join(char *dst, size_t dstlen,...)
Definition util_path.c:208
size_t xbps_strlcat(char *dst, const char *src, size_t dstsize)
Definition util.c:566
ssize_t xbps_path_rel(char *dst, size_t dstlen, const char *from, const char *to)
Definition util_path.c:131