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