1 /* u_pathcat.c
2 *
3 * Copyright (c) 1996-2005 Mike Gleason, NcFTP Software.
4 * All rights reserved.
5 *
6 */
7
8 #include "syshdrs.h"
9 #ifdef PRAGMA_HDRSTOP
10 # pragma hdrstop
11 #endif
12
13
14 /* This simplifies a pathname, by converting it to the
15 * equivalent of "cd $dir ; dir=`pwd`". In other words,
16 * if $PWD==/usr/spool/uucp, and you had a path like
17 * "$PWD/../tmp////./../xx/", it would be converted to
18 * "/usr/spool/xx".
19 */
20
21 int
IsValidUNCPath(const char * const src)22 IsValidUNCPath(const char *const src)
23 {
24 const char *a, *b;
25 int c;
26 int n;
27
28 /* We may have a DOS path. */
29 if ((src[0] == '\\') && (src[1] == '\\') && (isalpha((int) src[2]))) {
30 for (a = src + 3; ; ) {
31 c = *a++;
32 if (c == '\\')
33 break;
34 if ((! isalnum(c)) && (c != '_'))
35 return (0);
36 }
37 b = a;
38 c = *b++;
39 if (! (isalpha(c)))
40 return (0); /* share does not start with letter */
41 for ( ; ; ) {
42 c = *b++;
43 if ((c == '\\') || (c == '\0'))
44 break;
45 if ((! isalnum(c)) && (c != '_'))
46 return (0);
47 }
48 n = (int) (b - src);
49 return (n);
50 }
51 return (0);
52 } /* IsValidUNCPath */
53
54
55
56 void
CompressPath(char * const dst,const char * const src,const size_t dsize,int dosCompat)57 CompressPath(char *const dst, const char *const src, const size_t dsize, int dosCompat)
58 {
59 int c;
60 const char *s, *start, *dotp;
61 char *d, *lim, *dstart;
62 char *a, *b;
63 char slash = (char) '/';
64 size_t n;
65
66 #define isslash(c) ((c == '/') || (c == '\\'))
67 s = start = src;
68 d = dstart = dst;
69 lim = d + dsize - 1; /* leave room for nul byte. */
70
71 if (dsize == 0)
72 return;
73 *dst = '\0';
74
75 if ((s[0] == '\0') || (dsize < 4)) {
76 return;
77 } else if (dosCompat != 0) {
78 if (src[0] == '\\') {
79 /* We have a DOS path. */
80 slash = (char) '\\';
81 n = (size_t) IsValidUNCPath(src);
82 if (n != 0) {
83 if (dsize < n)
84 return;
85 --n;
86 memcpy(d, src, n);
87 d += n;
88 *d = '\0';
89 dstart = d;
90 s += n;
91 start = s;
92 /* unc = 1; */
93 }
94 } else if ((isalpha((int) src[0])) && (src[1] == ':')) {
95 /* We may have a DOS driveletter+path. */
96 *d++ = src[0];
97 *d++ = ':';
98 start += 2;
99 dstart += 2;
100 s += 2;
101 if (! isslash(src[2])) {
102 slash = (char) '\\';
103 *d++ = '\\';
104 } else {
105 slash = src[2];
106 /* add it below *d++ = src[2]; */
107 }
108 }
109 }
110
111 for (;;) {
112 c = *s;
113 if (c == '.') {
114 if (((s == start) || isslash(s[-1])) && (isslash(s[1]) || (s[1] == '\0'))) {
115 /* Don't copy "./" */
116 if (isslash(s[1]))
117 ++s;
118 ++s;
119 } else if ((dosCompat != 0) && (s[1] == '.')) {
120 if (d < lim)
121 *d++ = *s++;
122 if (d < lim)
123 *d++ = *s++;
124 if (*s == '.') {
125 dotp = s;
126 while (*dotp == '.')
127 dotp++;
128 if ((*dotp == '\0') || (isslash(*dotp))) {
129 /* On DOS, "..." == "..",
130 * "...." == "..", etc,
131 * so skip the extra dots.
132 */
133 s = dotp;
134 }
135 }
136 } else if (d < lim) {
137 *d++ = *s++;
138 } else {
139 ++s;
140 }
141 } else if (isslash(c)) {
142 /* Don't copy multiple slashes. */
143 if (d < lim)
144 *d++ = slash;
145 ++s;
146 for (;;) {
147 c = *s;
148 if (isslash(c)) {
149 /* Don't copy multiple slashes. */
150 ++s;
151 } else if (c == '.') {
152 c = s[1];
153 if (isslash(c)) {
154 /* Skip "./" */
155 s += 2;
156 } else if (c == '\0') {
157 /* Skip "./" */
158 s += 1;
159 } else {
160 break;
161 }
162 } else {
163 break;
164 }
165 }
166 } else if (c == '\0') {
167 /* Remove trailing slash. */
168 if (isslash(d[-1]) && (d > (dstart + 1)))
169 d[-1] = '\0';
170 *d = '\0';
171 break;
172 } else if (d < lim) {
173 *d++ = *s++;
174 } else {
175 ++s;
176 }
177 }
178 a = dstart;
179
180 /* fprintf(stderr, "<%s>\n", dst); */
181 /* Go through and remove .. in the path when we know what the
182 * parent directory is. After we get done with this, the only
183 * .. nodes in the path will be at the front.
184 */
185 while (*a != '\0') {
186 b = a;
187 for (;;) {
188 /* Get the next node in the path. */
189 if (*a == '\0')
190 return;
191 if (isslash(*a)) {
192 ++a;
193 break;
194 }
195 ++a;
196 }
197 if ((b[0] == '.') && (b[1] == '.')) {
198 if (isslash(b[2])) {
199 /* We don't know what the parent of this
200 * node would be.
201 */
202 continue;
203 }
204 }
205 if ((a[0] == '.') && (a[1] == '.')) {
206 if (isslash(a[2])) {
207 /* Remove the .. node and the one before it. */
208 if ((b == dstart) && (isslash(*dstart)))
209 (void) memmove(b + 1, a + 3, strlen(a + 3) + 1);
210 else
211 (void) memmove(b, a + 3, strlen(a + 3) + 1);
212 a = dstart; /* Start over. */
213 } else if (a[2] == '\0') {
214 /* Remove a trailing .. like: /aaa/bbb/.. */
215 if (b == dstart) {
216 dstart[0] = (char) ((isslash(start[0])) ? slash : '.');
217 dstart[1] = '\0';
218 } else if ((b <= dstart + 1) && isslash(*dstart)) {
219 dstart[1] = '\0';
220 } else {
221 b[-1] = '\0';
222 }
223 a = dstart; /* Start over. */
224 } else {
225 /* continue processing this node.
226 * It is probably some bogus path,
227 * like ".../", "..foo/", etc.
228 */
229 }
230 }
231 }
232 #undef isslash
233 } /* CompressPath */
234
235
236
237 void
PathCat(char * const dst,const size_t dsize,const char * const cwd,const char * const src,int dosCompat)238 PathCat(char *const dst, const size_t dsize, const char *const cwd, const char *const src, int dosCompat)
239 {
240 char *cp;
241 char tmp[512];
242
243 if (dosCompat != 0) {
244 if ((isalpha((int) cwd[0])) && (cwd[1] == ':')) {
245 if ((isalpha((int) src[0])) && (src[1] == ':')) {
246 /* A new fully-qualified DOS drive+path was requested. */
247 CompressPath(dst, src, dsize, dosCompat);
248 return;
249 } else if (IsValidUNCPath(src)) {
250 CompressPath(dst, src, dsize, dosCompat);
251 return;
252 } else if (src[0] == '\\') {
253 /* A new fully-qualified DOS path on the same
254 * drive letter was requested.
255 */
256 dst[0] = cwd[0];
257 dst[1] = ':';
258 CompressPath(dst + 2, src, dsize - 2, dosCompat);
259 return;
260 }
261 } else if (IsValidUNCPath(src)) {
262 /* A new fully-qualified DOS UNC path was requested.
263 * (but no drive letter was present on CWD?)
264 */
265 CompressPath(dst, src, dsize, dosCompat);
266 return;
267 } else if ((src[0] == '\\') || ((isalpha((int) src[0])) && (src[1] == ':'))) {
268 /* A new fully-qualified DOS path was requested.
269 * (but no drive letter was present on CWD?)
270 */
271 CompressPath(dst, src, dsize, dosCompat);
272 return;
273 }
274 }
275 if ((src[0] == '/') || (src[0] == '~')) {
276 /* A new fully-qualified UNIX path was requested. */
277 CompressPath(dst, src, dsize, dosCompat);
278 return;
279 }
280
281 cp = Strnpcpy(tmp, cwd, sizeof(tmp) - 1);
282 if (dosCompat) {
283 if (dst[0] == '\\')
284 *cp++ = '\\';
285 else if ((dst[1] != ':') || (dst[2] == '/'))
286 *cp++ = '/';
287 else
288 *cp++ = '\\';
289 } else {
290 *cp++ = '/';
291 }
292 *cp = '\0';
293 (void) Strnpcat(cp, src, sizeof(tmp) - (size_t) (cp - tmp));
294 CompressPath(dst, tmp, dsize, dosCompat);
295 } /* PathCat */
296
297
298
299 int
DPathCat(char ** const dst0,const char * const cwd,const char * const src,int dosCompat)300 DPathCat(char **const dst0, const char *const cwd, const char *const src, int dosCompat)
301 {
302 char *cp, *dst, *tmp;
303 size_t dsize;
304
305 dsize = strlen(cwd) +
306 /* pathdelim */ 1 +
307 strlen(src) +
308 /* NUL byte */ 1 +
309 /* spare */ 10;
310
311 dst = calloc(dsize, 1);
312 *dst0 = dst;
313 if (dst == NULL)
314 return (-1);
315
316 if (dosCompat != 0) {
317 if ((isalpha((int) cwd[0])) && (cwd[1] == ':')) {
318 if ((isalpha((int) src[0])) && (src[1] == ':')) {
319 /* A new fully-qualified DOS drive+path was requested. */
320 CompressPath(dst, src, dsize, dosCompat);
321 return (0);
322 } else if (IsValidUNCPath(src)) {
323 CompressPath(dst, src, dsize, dosCompat);
324 return (0);
325 } else if (src[0] == '\\') {
326 /* A new fully-qualified DOS path on the same
327 * drive letter was requested.
328 */
329 dst[0] = cwd[0];
330 dst[1] = ':';
331 CompressPath(dst + 2, src, dsize - 2, dosCompat);
332 return (0);
333 }
334 } else if (IsValidUNCPath(src)) {
335 /* A new fully-qualified DOS UNC path was requested.
336 * (but no drive letter was present on CWD?)
337 */
338 CompressPath(dst, src, dsize, dosCompat);
339 return (0);
340 } else if ((src[0] == '\\') || ((isalpha((int) src[0])) && (src[1] == ':'))) {
341 /* A new fully-qualified DOS path was requested.
342 * (but no drive letter was present on CWD?)
343 */
344 CompressPath(dst, src, dsize, dosCompat);
345 return (0);
346 }
347 }
348 if (src[0] == '/') {
349 /* A new fully-qualified UNIX path was requested. */
350 CompressPath(dst, src, dsize, dosCompat);
351 return (0);
352 }
353
354 tmp = calloc(dsize, 1);
355 if (tmp == NULL) {
356 free(dst);
357 *dst0 = NULL;
358 return (-1);
359 }
360
361 cp = Strnpcpy(tmp, cwd, dsize - 1);
362 if (dosCompat) {
363 if (dst[0] == '\\')
364 *cp++ = '\\';
365 else if ((dst[1] != ':') || (dst[2] == '/'))
366 *cp++ = '/';
367 else
368 *cp++ = '\\';
369 } else {
370 *cp++ = '/';
371 }
372 *cp = '\0';
373 (void) Strnpcat(cp, src, dsize - (size_t) (cp - tmp));
374 CompressPath(dst, tmp, dsize, dosCompat);
375 free(tmp);
376 return (0);
377 } /* DPathCat */
378