Initial import.
[profile/ivi/apr.git] / file_io / win32 / filepath.c
1 /* Licensed to the Apache Software Foundation (ASF) under one or more
2  * contributor license agreements.  See the NOTICE file distributed with
3  * this work for additional information regarding copyright ownership.
4  * The ASF licenses this file to You under the Apache License, Version 2.0
5  * (the "License"); you may not use this file except in compliance with
6  * the License.  You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 #include "apr.h"
18 #include "apr_private.h"
19 #include "apr_arch_file_io.h"
20 #include "apr_strings.h"
21 #include "apr_lib.h"
22 #include <string.h>
23 #include <ctype.h>
24
25 #ifdef NETWARE
26 #include <unistd.h>
27 #include <fsio.h>
28 #endif
29
30  /* WinNT accepts several odd forms of a 'root' path.  Under Unicode
31  * calls (ApiFunctionW) the //?/C:/foo or //?/UNC/mach/share/foo forms
32  * are accepted.  Ansi and Unicode functions both accept the //./C:/foo 
33  * form under WinNT/2K.  Since these forms are handled in the utf-8 to 
34  * unicode translation phase, we don't want the user confused by them, so 
35  * we will accept them but always return the canonical C:/ or //mach/share/
36  *
37  * OS2 appears immune from the nonsense :)
38  */
39
40 APR_DECLARE(apr_status_t) apr_filepath_root(const char **rootpath, 
41                                             const char **inpath, 
42                                             apr_int32_t flags,
43                                             apr_pool_t *p)
44 {
45     const char *testpath = *inpath;
46     char *newpath;
47 #ifdef NETWARE
48     char seperator[2] = { 0, 0};
49     char server[APR_PATH_MAX+1];
50     char volume[APR_PATH_MAX+1];
51     char file[APR_PATH_MAX+1];
52     char *volsep = NULL;
53     int elements;
54
55     if (inpath && *inpath)
56         volsep = strchr (*inpath, ':');
57     else
58         return APR_EBADPATH;
59
60     if (strlen(*inpath) > APR_PATH_MAX) {
61         return APR_EBADPATH;
62     }
63
64     seperator[0] = (flags & APR_FILEPATH_NATIVE) ? '\\' : '/';
65
66     /* Allocate and initialize each of the segment buffers
67     */
68     server[0] = volume[0] = file[0] = '\0';
69
70     /* If we don't have a volume separator then don't bother deconstructing
71         the path since we won't use the deconstructed information anyway.
72     */
73     if (volsep) {
74         /* Split the inpath into its separate parts. */
75         deconstruct(testpath, server, volume, NULL, file, NULL, &elements, PATH_UNDEF);
76     
77         /* If we got a volume part then continue splitting out the root.
78             Otherwise we either have an incomplete or relative path
79         */
80         if (volume && strlen(volume) > 0) {
81             newpath = apr_pcalloc(p, strlen(server)+strlen(volume)+5);
82             construct(newpath, server, volume, NULL, NULL, NULL, PATH_NETWARE);
83
84             /* NetWare doesn't add the root slash so we need to add it manually.
85             */
86             strcat(newpath, seperator);
87             *rootpath = newpath;
88
89             /* Skip the inpath pointer down to the first non-root character
90             */
91             newpath = volsep;
92             do {
93                 ++newpath;
94             } while (*newpath && ((*newpath == '/') || (*newpath == '\\')));
95             *inpath = newpath;
96
97             /* Need to handle APR_FILEPATH_TRUENAME checking here. */
98
99             return APR_SUCCESS;
100         }
101         else
102             return APR_EBADPATH;
103     }
104     else if ((**inpath == '/') || (**inpath == '\\')) {
105         /* if we have a root path without a volume then just split
106             in same manner as unix although this path will be
107             incomplete.
108         */
109         *rootpath = apr_pstrdup(p, seperator);
110         do {
111             ++(*inpath);
112         } while ((**inpath == '/') || (**inpath == '\\'));
113     }
114     else
115         return APR_ERELATIVE;
116
117     return APR_EINCOMPLETE;
118
119 #else /* ndef(NETWARE) */
120
121     char seperator[2];
122     const char *delim1;
123     const char *delim2;
124
125     seperator[0] = (flags & APR_FILEPATH_NATIVE) ? '\\' : '/';
126     seperator[1] = 0;
127
128     if (testpath[0] == '/' || testpath[0] == '\\') {
129         if (testpath[1] == '/' || testpath[1] == '\\') {
130
131 #ifdef WIN32 /* //server/share isn't the only // delimited syntax */
132             if ((testpath[2] == '?' || testpath[2] == '.')
133                     && (testpath[3] == '/' || testpath[3] == '\\')) {
134                 if (IS_FNCHAR(testpath[4]) && testpath[5] == ':') 
135                 {
136                     apr_status_t rv;
137                     testpath += 4;
138                     /* given  '//?/C: or //./C: let us try this
139                      * all over again from the drive designator
140                      */
141                     rv = apr_filepath_root(rootpath, &testpath, flags, p);
142                     if (!rv || rv == APR_EINCOMPLETE)
143                         *inpath = testpath;
144                     return rv;
145                 }
146                 else if (strncasecmp(testpath + 4, "UNC", 3) == 0
147                       && (testpath[7] == '/' || testpath[7] == '\\') 
148                       && (testpath[2] == '?')) {
149                     /* given  '//?/UNC/machine/share, a little magic 
150                      * at the end makes this all work out by using
151                      * 'C/machine' as the starting point and replacing
152                      * the UNC delimiters with \'s, including the 'C'
153                      */
154                     testpath += 6;
155                 }
156                 else
157                     /* This must not be a path to a file, but rather
158                      * a volume or device.  Die for now.
159                      */
160                     return APR_EBADPATH;
161             }
162 #endif /* WIN32 (non - //server/share syntax) */
163
164             /* Evaluate path of '//[machine/[share[/]]]' */
165             delim1 = testpath + 2;
166             do {
167                 /* Protect against //X/ where X is illegal */
168                 if (*delim1 && !IS_FNCHAR(*(delim1++)))
169                     return APR_EBADPATH;
170             } while (*delim1 && *delim1 != '/' && *delim1 != '\\');
171
172             if (*delim1) {
173                 apr_status_t rv;
174                 delim2 = delim1 + 1;
175                 while (*delim2 && *delim2 != '/' && *delim2 != '\\') {
176                     /* Protect against //machine/X/ where X is illegal */
177                     if (!IS_FNCHAR(*(delim2++)))
178                         return APR_EBADPATH;
179                 } 
180
181                 /* Copy the '//machine/[share[/]]' path, always providing 
182                  * an extra byte for the trailing slash.
183                  */
184                 newpath = apr_pstrmemdup(p, testpath, delim2 - testpath + 1);
185
186                 if (delim2 == delim1 + 1) {
187                     /* We found simply \\machine\, so give up already
188                      */
189                     *rootpath = newpath;
190                     *inpath = delim2;
191                     return APR_EINCOMPLETE;
192                 }
193
194                 if (flags & APR_FILEPATH_TRUENAME) {
195                     /* Validate the \\Machine\Share\ designation, 
196                      * Win32 will argue about slashed in UNC paths, 
197                      * so use backslashes till we finish testing,
198                      * and add the trailing backslash [required].
199                      * apr_pstrmemdup above guarentees us the new 
200                      * trailing null character.
201                      */
202                     newpath[0] = '\\';
203                     newpath[1] = '\\';
204                     newpath[delim1 - testpath] = '\\';
205                     newpath[delim2 - testpath] = '\\';
206
207                     rv = filepath_root_test(newpath, p);
208                     if (rv)
209                         return rv;
210                     rv = filepath_root_case(&newpath, newpath, p);
211                     if (rv)
212                         return rv;
213                     newpath[0] = seperator[0];
214                     newpath[1] = seperator[0];
215                     newpath[delim1 - testpath] = seperator[0];
216                     newpath[delim2 - testpath] = (*delim2 ? seperator[0] : '\0');
217                 }
218                 else {                
219                     /* Give back the caller's own choice of delimiters
220                      */
221                     newpath[0] = testpath[0];
222                     newpath[1] = testpath[1];
223                     newpath[delim1 - testpath] = *delim1;
224                     newpath[delim2 - testpath] = *delim2;
225                 }
226
227                 /* If this root included the trailing / or \ designation 
228                  * then lop off multiple trailing slashes and give back
229                  * appropriate delimiters.
230                  */
231                 if (*delim2) {
232                     *inpath = delim2 + 1;
233                     while (**inpath == '/' || **inpath == '\\')
234                         ++*inpath;
235                 }
236                 else {
237                     *inpath = delim2;
238                 }
239
240                 *rootpath = newpath;
241                 return APR_SUCCESS;
242             }
243             
244             /* Have path of '\\[machine]', if the machine is given,
245              * append same trailing slash as the leading slash
246              */
247             delim1 = strchr(testpath, '\0');
248             if (delim1 > testpath + 2) {
249                 newpath = apr_pstrndup(p, testpath, delim1 - testpath + 1);
250                 if (flags & APR_FILEPATH_TRUENAME)
251                     newpath[delim1 - testpath] = seperator[0];
252                 else
253                     newpath[delim1 - testpath] = newpath[0];
254                 newpath[delim1 - testpath + 1] = '\0';
255             }
256             else {
257                 newpath = apr_pstrndup(p, testpath, delim1 - testpath);
258             }
259             if (flags & APR_FILEPATH_TRUENAME) {
260                 newpath[0] = seperator[0];
261                 newpath[1] = seperator[0];
262             }
263             *rootpath = newpath;
264             *inpath = delim1;
265             return APR_EINCOMPLETE;
266         }
267
268         /* Left with a path of '/', what drive are we asking about? 
269          */
270         *inpath = testpath + 1;
271         newpath = apr_palloc(p, 2);
272         if (flags & APR_FILEPATH_TRUENAME)
273             newpath[0] = seperator[0];
274         else
275             newpath[0] = testpath[0];
276         newpath[1] = '\0';
277         *rootpath = newpath;
278         return APR_EINCOMPLETE;
279     }
280
281     /* Evaluate path of 'd:[/]' */
282     if (IS_FNCHAR(*testpath) && testpath[1] == ':') 
283     {
284         apr_status_t rv;
285         /* Validate that D:\ drive exists, test must be rooted
286          * Note that posix/win32 insists a drive letter is upper case,
287          * so who are we to argue with a 'feature'.
288          * It is a safe fold, since only A-Z is legal, and has no
289          * side effects of legal mis-mapped non-us-ascii codes.
290          */
291         newpath = apr_palloc(p, 4);
292         newpath[0] = testpath[0];
293         newpath[1] = testpath[1];
294         newpath[2] = seperator[0];
295         newpath[3] = '\0';
296         if (flags & APR_FILEPATH_TRUENAME) {
297             newpath[0] = apr_toupper(newpath[0]);
298             rv = filepath_root_test(newpath, p);
299             if (rv)
300                 return rv;
301         }
302         /* Just give back the root the user handed to us.
303          */
304         if (testpath[2] != '/' && testpath[2] != '\\') {
305             newpath[2] = '\0';
306             *rootpath = newpath;
307             *inpath = testpath + 2;
308             return APR_EINCOMPLETE;
309         }
310
311         /* strip off remaining slashes that designate the root,
312          * give the caller back their original choice of slash
313          * unless this is TRUENAME'ed
314          */
315         *inpath = testpath + 3;
316         while (**inpath == '/' || **inpath == '\\')
317             ++*inpath;
318         if (!(flags & APR_FILEPATH_TRUENAME))
319             newpath[2] = testpath[2];
320         *rootpath = newpath;
321         return APR_SUCCESS;
322     }
323
324     /* Nothing interesting */
325     return APR_ERELATIVE;
326
327 #endif /* ndef(NETWARE) */
328 }
329
330 #if !defined(NETWARE)
331 static int same_drive(const char *path1, const char *path2)
332 {
333     char drive1 = path1[0];
334     char drive2 = path2[0];
335
336     if (!drive1 || !drive2 || path1[1] != ':' || path2[1] != ':')
337         return FALSE;
338
339     if (drive1 == drive2)
340         return TRUE;
341
342     if (drive1 >= 'a' && drive1 <= 'z')
343         drive1 += 'A' - 'a';
344
345     if (drive2 >= 'a' && drive2 <= 'z')
346         drive2 += 'A' - 'a';
347
348     return (drive1 == drive2);
349 }
350 #endif
351
352 APR_DECLARE(apr_status_t) apr_filepath_merge(char **newpath, 
353                                              const char *basepath, 
354                                              const char *addpath, 
355                                              apr_int32_t flags,
356                                              apr_pool_t *p)
357 {
358     char path[APR_PATH_MAX]; /* isn't null term */
359     const char *baseroot = NULL;
360     const char *addroot;
361     apr_size_t rootlen; /* the length of the root portion of path, d:/ is 3 */
362     apr_size_t baselen; /* the length of basepath (excluding baseroot) */
363     apr_size_t keptlen; /* the length of the retained basepath (incl root) */
364     apr_size_t pathlen; /* the length of the result path */
365     apr_size_t segend;  /* the end of the current segment */
366     apr_size_t seglen;  /* the length of the segment (excl trailing chars) */
367     apr_status_t basetype = 0; /* from parsing the basepath's baseroot */
368     apr_status_t addtype;      /* from parsing the addpath's addroot */
369     apr_status_t rv;
370 #ifndef NETWARE
371     int fixunc = 0;  /* flag to complete an incomplete UNC basepath */
372 #endif
373     
374     /* Treat null as an empty path, otherwise split addroot from the addpath
375      */
376     if (!addpath) {
377         addpath = addroot = "";
378         addtype = APR_ERELATIVE;
379     }
380     else {
381         /* This call _should_ test the path
382          */
383         addtype = apr_filepath_root(&addroot, &addpath, 
384                                     APR_FILEPATH_TRUENAME
385                                     | (flags & APR_FILEPATH_NATIVE),
386                                     p);
387         if (addtype == APR_SUCCESS) {
388             addtype = APR_EABSOLUTE;
389         }
390         else if (addtype == APR_ERELATIVE) {
391             addroot = "";
392         }
393         else if (addtype != APR_EINCOMPLETE) {
394             /* apr_filepath_root was incomprehensible so fail already
395              */
396             return addtype;
397         }
398     }
399
400     /* If addpath is (even partially) rooted, then basepath is
401      * unused.  Ths violates any APR_FILEPATH_SECUREROOTTEST 
402      * and APR_FILEPATH_NOTABSOLUTE flags specified.
403      */
404     if (addtype == APR_EABSOLUTE || addtype == APR_EINCOMPLETE)
405     {
406         if (flags & APR_FILEPATH_SECUREROOTTEST)
407             return APR_EABOVEROOT;
408         if (flags & APR_FILEPATH_NOTABSOLUTE)
409             return addtype;
410     }
411
412     /* Optimized tests before we query the current working path
413      */
414     if (!basepath) {
415
416         /* If APR_FILEPATH_NOTABOVEROOT wasn't specified,
417          * we won't test the root again, it's ignored.
418          * Waste no CPU retrieving the working path.
419          */
420         if (addtype == APR_EABSOLUTE && !(flags & APR_FILEPATH_NOTABOVEROOT)) {
421             basepath = baseroot = "";
422             basetype = APR_ERELATIVE;
423         }
424
425         /* If APR_FILEPATH_NOTABSOLUTE is specified, the caller 
426          * requires an absolutely relative result, So do not retrieve 
427          * the working path.
428          */
429         if (addtype == APR_ERELATIVE && (flags & APR_FILEPATH_NOTABSOLUTE)) {
430             basepath = baseroot = "";
431             basetype = APR_ERELATIVE;
432         }
433     }
434
435     if (!basepath) 
436     {
437         /* Start with the current working path.  This is bass akwards,
438          * but required since the compiler (at least vc) doesn't like
439          * passing the address of a char const* for a char** arg.
440          * We must grab the current path of the designated drive 
441          * if addroot is given in drive-relative form (e.g. d:foo)
442          */
443         char *getpath;
444 #ifndef NETWARE
445         if (addtype == APR_EINCOMPLETE && addroot[1] == ':')
446             rv = filepath_drive_get(&getpath, addroot[0], flags, p);
447         else
448 #endif
449             rv = apr_filepath_get(&getpath, flags, p);
450         if (rv != APR_SUCCESS)
451             return rv;
452         basepath = getpath;
453     }
454
455     if (!baseroot) {
456         /* This call should _not_ test the path
457          */
458         basetype = apr_filepath_root(&baseroot, &basepath,
459                                      (flags & APR_FILEPATH_NATIVE), p);
460         if (basetype == APR_SUCCESS) {
461             basetype = APR_EABSOLUTE;
462         }
463         else if (basetype == APR_ERELATIVE) {
464             baseroot = "";
465         }
466         else if (basetype != APR_EINCOMPLETE) {
467             /* apr_filepath_root was incomprehensible so fail already
468              */
469             return basetype;
470         }
471     }
472     baselen = strlen(basepath);
473
474     /* If APR_FILEPATH_NOTABSOLUTE is specified, the caller 
475      * requires an absolutely relative result.  If the given 
476      * basepath is not relative then fail.
477      */
478     if ((flags & APR_FILEPATH_NOTABSOLUTE) && basetype != APR_ERELATIVE)
479         return basetype;
480
481     /* The Win32 nightmare on unc street... start combining for
482      * many possible root combinations.
483      */
484     if (addtype == APR_EABSOLUTE)
485     {
486         /* Ignore the given root path, and start with the addroot
487          */
488         if ((flags & APR_FILEPATH_NOTABOVEROOT) 
489                 && strncmp(baseroot, addroot, strlen(baseroot)))
490             return APR_EABOVEROOT;
491         keptlen = 0;
492         rootlen = pathlen = strlen(addroot);
493         memcpy(path, addroot, pathlen);
494     }
495     else if (addtype == APR_EINCOMPLETE)
496     {
497         /* There are several types of incomplete paths, 
498          *     incomplete UNC paths         (//foo/ or //),
499          *     drives without rooted paths  (d: as in d:foo), 
500          * and simple roots                 (/ as in /foo).
501          * Deal with these in significantly different manners...
502          */
503 #ifndef NETWARE
504         if ((addroot[0] == '/' || addroot[0] == '\\') &&
505             (addroot[1] == '/' || addroot[1] == '\\')) 
506         {
507             /* Ignore the given root path if the incomplete addpath is UNC,
508              * (note that the final result will be incomplete).
509              */
510             if (flags & APR_FILEPATH_NOTRELATIVE)
511                 return addtype;
512             if ((flags & APR_FILEPATH_NOTABOVEROOT) 
513                     && strncmp(baseroot, addroot, strlen(baseroot)))
514                 return APR_EABOVEROOT;
515             fixunc = 1;
516             keptlen = 0;
517             rootlen = pathlen = strlen(addroot);
518             memcpy(path, addroot, pathlen);
519         }
520         else
521 #endif            
522         if ((addroot[0] == '/' || addroot[0] == '\\') && !addroot[1]) 
523         {
524             /* Bring together the drive or UNC root from the baseroot
525              * if the addpath is a simple root and basepath is rooted,
526              * otherwise disregard the basepath entirely.
527              */
528             if (basetype != APR_EABSOLUTE && (flags & APR_FILEPATH_NOTRELATIVE))
529                 return basetype;
530             if (basetype != APR_ERELATIVE) {
531 #ifndef NETWARE
532                 if (basetype == APR_INCOMPLETE 
533                         && (baseroot[0] == '/' || baseroot[0] == '\\')
534                         && (baseroot[1] == '/' || baseroot[1] == '\\'))
535                     fixunc = 1;
536 #endif
537                 keptlen = rootlen = pathlen = strlen(baseroot);
538                 memcpy(path, baseroot, pathlen);
539             }
540             else {
541                 if (flags & APR_FILEPATH_NOTABOVEROOT)
542                     return APR_EABOVEROOT;
543                 keptlen = 0;
544                 rootlen = pathlen = strlen(addroot);
545                 memcpy(path, addroot, pathlen);
546             }
547         }
548 #ifdef NETWARE
549         else if (filepath_has_drive(addroot, DRIVE_ONLY, p)) 
550         {
551             /* If the addroot is a drive (without a volume root)
552              * use the basepath _if_ it matches this drive letter!
553              * Otherwise we must discard the basepath.
554              */
555             if (!filepath_compare_drive(addroot, baseroot, p) && 
556                 filepath_has_drive(baseroot, 0, p)) {
557 #else
558         else if (addroot[0] && addroot[1] == ':' && !addroot[2]) 
559         {
560             /* If the addroot is a drive (without a volume root)
561              * use the basepath _if_ it matches this drive letter!
562              * Otherwise we must discard the basepath.
563              */
564             if (same_drive(addroot, baseroot)) {
565 #endif
566                 /* Base the result path on the basepath
567                  */
568                 if (basetype != APR_EABSOLUTE && (flags & APR_FILEPATH_NOTRELATIVE))
569                     return basetype;
570                 rootlen = strlen(baseroot);
571                 keptlen = pathlen = rootlen + baselen;
572                 if (keptlen >= sizeof(path))
573                     return APR_ENAMETOOLONG;
574                 memcpy(path, baseroot, rootlen);
575                 memcpy(path + rootlen, basepath, baselen);
576             } 
577             else {
578                 if (flags & APR_FILEPATH_NOTRELATIVE)
579                     return addtype;
580                 if (flags & APR_FILEPATH_NOTABOVEROOT)
581                     return APR_EABOVEROOT;
582                 keptlen = 0;
583                 rootlen = pathlen = strlen(addroot);
584                 memcpy(path, addroot, pathlen);
585             }
586         }
587         else {
588             /* Now this is unexpected, we aren't aware of any other
589              * incomplete path forms!  Fail now.
590              */
591             return APR_EBADPATH;
592         }
593     }
594     else { /* addtype == APR_ERELATIVE */
595         /* If both paths are relative, fail early
596          */
597         if (basetype != APR_EABSOLUTE && (flags & APR_FILEPATH_NOTRELATIVE))
598             return basetype;
599
600 #ifndef NETWARE
601         /* An incomplete UNC path must be completed
602          */
603         if (basetype == APR_INCOMPLETE 
604                 && (baseroot[0] == '/' || baseroot[0] == '\\')
605                 && (baseroot[1] == '/' || baseroot[1] == '\\'))
606             fixunc = 1;
607 #endif
608
609         /* Base the result path on the basepath
610          */
611         rootlen = strlen(baseroot);
612         keptlen = pathlen = rootlen + baselen;
613         if (keptlen >= sizeof(path))
614             return APR_ENAMETOOLONG;
615         memcpy(path, baseroot, rootlen);
616         memcpy(path + rootlen, basepath, baselen);
617     }
618
619     /* '/' terminate the given root path unless it's already terminated
620      * or is an incomplete drive root.  Correct the trailing slash unless
621      * we have an incomplete UNC path still to fix.
622      */
623     if (pathlen && path[pathlen - 1] != ':') {
624         if (path[pathlen - 1] != '/' && path[pathlen - 1] != '\\') {
625             if (pathlen + 1 >= sizeof(path))
626                 return APR_ENAMETOOLONG;
627         
628             path[pathlen++] = ((flags & APR_FILEPATH_NATIVE) ? '\\' : '/');
629         }
630     /*  XXX: wrong, but gotta figure out what I intended;
631      *  else if (!fixunc)
632      *      path[pathlen++] = ((flags & APR_FILEPATH_NATIVE) ? '\\' : '/');
633      */
634     }
635
636     while (*addpath) 
637     {
638         /* Parse each segment, find the closing '/' 
639          */
640         seglen = 0;
641         while (addpath[seglen] && addpath[seglen] != '/'
642                                && addpath[seglen] != '\\')
643             ++seglen;
644
645         /* Truncate all trailing spaces and all but the first two dots */
646         segend = seglen;
647         while (seglen && (addpath[seglen - 1] == ' ' 
648                        || addpath[seglen - 1] == '.')) {
649             if (seglen > 2 || addpath[seglen - 1] != '.' || addpath[0] != '.')
650                 --seglen;
651             else
652                 break;
653         }
654
655         if (seglen == 0 || (seglen == 1 && addpath[0] == '.')) 
656         {
657             /* NOTE: win32 _hates_ '/ /' and '/. /' (yes, with spaces in there)
658              * so eliminate all preconceptions that it is valid.
659              */
660             if (seglen < segend)
661                 return APR_EBADPATH;
662
663 #ifndef NETWARE
664             /* This isn't legal unless the unc path is completed
665              */
666             if (fixunc)
667                 return APR_EBADPATH;
668 #endif
669
670             /* Otherwise, this is a noop segment (/ or ./) so ignore it 
671              */
672         }
673         else if (seglen == 2 && addpath[0] == '.' && addpath[1] == '.') 
674         {
675             /* NOTE: win32 _hates_ '/.. /' (yes, with a space in there)
676              * and '/..../', some functions treat it as ".", and some 
677              * fail! Eliminate all preconceptions that they are valid.
678              */
679             if (seglen < segend && (seglen != 3 || addpath[2] != '.'))
680                 return APR_EBADPATH;
681
682 #ifndef NETWARE
683             /* This isn't legal unless the unc path is completed
684              */
685             if (fixunc)
686                 return APR_EBADPATH;
687 #endif
688
689             /* backpath (../) when an absolute path is given */
690             if (rootlen && (pathlen <= rootlen)) 
691             {
692                 /* Attempt to move above root.  Always die if the 
693                  * APR_FILEPATH_SECUREROOTTEST flag is specified.
694                  */
695                 if (flags & APR_FILEPATH_SECUREROOTTEST)
696                     return APR_EABOVEROOT;
697                 
698                 /* Otherwise this is simply a noop, above root is root.
699                  */
700             }
701             else if (pathlen == 0 
702                       || (pathlen >= 3 
703                            && (pathlen == 3
704                                 || path[pathlen - 4] == ':'
705                                 || path[pathlen - 4] == '/' 
706                                 || path[pathlen - 4] == '\\')
707                            &&  path[pathlen - 3] == '.' 
708                            &&  path[pathlen - 2] == '.' 
709                            && (path[pathlen - 1] == '/' 
710                                 || path[pathlen - 1] == '\\')))
711             {
712                 /* Verified path is empty, exactly "..[/\]", or ends
713                  * in "[:/\]..[/\]" - these patterns we will not back
714                  * over since they aren't 'prior segements'.
715                  * 
716                  * If APR_FILEPATH_SECUREROOTTEST.was given, die now.
717                  */
718                 if (flags & APR_FILEPATH_SECUREROOTTEST)
719                     return APR_EABOVEROOT;
720
721                 /* Otherwise append another backpath.
722                  */
723                 if (pathlen + 3 >= sizeof(path))
724                     return APR_ENAMETOOLONG;
725                 path[pathlen++] = '.';
726                 path[pathlen++] = '.';
727                 if (addpath[segend]) {
728                     path[pathlen++] = ((flags & APR_FILEPATH_NATIVE) 
729                                     ? '\\' : ((flags & APR_FILEPATH_TRUENAME)
730                                            ? '/' : addpath[segend]));
731                 }
732                 /* The 'root' part of this path now includes the ../ path,
733                  * because that backpath will not be parsed by the truename
734                  * code below.
735                  */
736                 keptlen = pathlen;
737             }
738             else 
739             {
740                 /* otherwise crop the prior segment 
741                  */
742                 do {
743                     --pathlen;
744                 } while (pathlen && path[pathlen - 1] != '/'
745                                  && path[pathlen - 1] != '\\');
746
747                 /* Now test if we are above where we started and back up
748                  * the keptlen offset to reflect the added/altered path.
749                  */
750                 if (pathlen < keptlen) 
751                 {
752                     if (flags & APR_FILEPATH_SECUREROOTTEST)
753                         return APR_EABOVEROOT;
754                     keptlen = pathlen;
755                 }
756             }
757         }
758         else /* not empty or dots */
759         {
760 #ifndef NETWARE
761             if (fixunc) {
762                 const char *testpath = path;
763                 const char *testroot;
764                 apr_status_t testtype;
765                 apr_size_t i = (addpath[segend] != '\0');
766                 
767                 /* This isn't legal unless the unc path is complete!
768                  */
769                 if (seglen < segend)
770                     return APR_EBADPATH;
771                 if (pathlen + seglen + 1 >= sizeof(path))
772                     return APR_ENAMETOOLONG;
773                 memcpy(path + pathlen, addpath, seglen + i);
774                 
775                 /* Always add the trailing slash to a UNC segment
776                  */
777                 path[pathlen + seglen] = ((flags & APR_FILEPATH_NATIVE) 
778                                              ? '\\' : '/');
779                 pathlen += seglen + 1;
780
781                 /* Recanonicalize the UNC root with the new UNC segment,
782                  * and if we succeed, reset this test and the rootlen,
783                  * and replace our path with the canonical UNC root path
784                  */
785                 path[pathlen] = '\0';
786                 /* This call _should_ test the path
787                  */
788                 testtype = apr_filepath_root(&testroot, &testpath, 
789                                              APR_FILEPATH_TRUENAME
790                                              | (flags & APR_FILEPATH_NATIVE),
791                                              p);
792                 if (testtype == APR_SUCCESS) {
793                     rootlen = pathlen = (testpath - path);
794                     memcpy(path, testroot, pathlen);
795                     fixunc = 0;
796                 }
797                 else if (testtype != APR_EINCOMPLETE) {
798                     /* apr_filepath_root was very unexpected so fail already
799                      */
800                     return testtype;
801                 }
802             }
803             else
804 #endif
805             {
806                 /* An actual segment, append it to the destination path
807                  */
808                 apr_size_t i = (addpath[segend] != '\0');
809                 if (pathlen + seglen + i >= sizeof(path))
810                     return APR_ENAMETOOLONG;
811                 memcpy(path + pathlen, addpath, seglen + i);
812                 if (i)
813                     path[pathlen + seglen] = ((flags & APR_FILEPATH_NATIVE) 
814                                                  ? '\\' : '/');
815                 pathlen += seglen + i;
816             }
817         }
818
819         /* Skip over trailing slash to the next segment
820          */
821         if (addpath[segend])
822             ++segend;
823
824         addpath += segend;
825     }
826     
827     /* keptlen will be the baselen unless the addpath contained
828      * backpath elements.  If so, and APR_FILEPATH_NOTABOVEROOT
829      * is specified (APR_FILEPATH_SECUREROOTTEST was caught above),
830      * compare the string beyond the root to assure the result path 
831      * is still within given basepath.  Note that the root path 
832      * segment is thoroughly tested prior to path parsing.
833      */
834     if ((flags & APR_FILEPATH_NOTABOVEROOT) && baselen) {
835         if (memcmp(basepath, path + rootlen, baselen) != 0)
836             return APR_EABOVEROOT;
837  
838          /* Ahem... if we have a basepath without a trailing slash,
839           * we better be sure that /foo wasn't replaced with /foobar!
840           */
841         if (basepath[baselen - 1] != '/' && basepath[baselen - 1] != '\\'
842               && path[rootlen + baselen] && path[rootlen + baselen] != '/' 
843                                          && path[rootlen + baselen] != '\\')
844             return APR_EABOVEROOT;
845     }
846
847     if (addpath && (flags & APR_FILEPATH_TRUENAME)) {
848         /* We can always skip the root, it's already true-named. */
849         if (rootlen > keptlen)
850             keptlen = rootlen;
851         if ((path[keptlen] == '/') || (path[keptlen] == '\\')) {
852             /* By rights, keptlen may grown longer than pathlen.
853              * we wont' use it again (in that case) so we don't care.
854              */
855             ++keptlen;
856         }
857         /* Go through all the new segments */
858         while (keptlen < pathlen) {
859             apr_finfo_t finfo;
860             char saveslash = 0;
861             seglen = 0;
862             /* find any slash and set it aside for a minute. */
863             for (seglen = 0; keptlen + seglen < pathlen; ++seglen) {
864                 if ((path[keptlen + seglen] == '/')  ||
865                     (path[keptlen + seglen] == '\\')) {
866                     saveslash = path[keptlen + seglen];
867                     break;
868                 }
869             }
870             /* Null term for stat! */
871             path[keptlen + seglen] = '\0';
872             if ((rv = apr_stat(&finfo, path, 
873                                APR_FINFO_LINK | APR_FINFO_TYPE | APR_FINFO_NAME, p))
874                 == APR_SUCCESS) {
875                 apr_size_t namelen = strlen(finfo.name);
876
877 #if defined(OS2) /* only has case folding, never aliases that change the length */
878
879                 if (memcmp(finfo.name, path + keptlen, seglen) != 0) {
880                     memcpy(path + keptlen, finfo.name, namelen);
881                 }
882 #else /* WIN32 || NETWARE; here there be aliases that gire and gimble and change length */
883
884                 if ((namelen != seglen) || 
885                     (memcmp(finfo.name, path + keptlen, seglen) != 0)) 
886                 {
887                     if (namelen <= seglen) {
888                         memcpy(path + keptlen, finfo.name, namelen);
889                         if ((namelen < seglen) && saveslash) {
890                             memmove(path + keptlen + namelen + 1,
891                                    path + keptlen + seglen + 1,
892                                    pathlen - keptlen - seglen);
893                             pathlen += namelen - seglen;
894                             seglen = namelen;
895                         }
896                     }
897                     else { /* namelen > seglen */
898                         if (pathlen + namelen - seglen >= sizeof(path))
899                             return APR_ENAMETOOLONG;
900                         if (saveslash) {
901                             memmove(path + keptlen + namelen + 1,
902                                    path + keptlen + seglen + 1,
903                                    pathlen - keptlen - seglen);
904                         }
905                         memcpy(path + keptlen, finfo.name, namelen);
906                         pathlen += namelen - seglen;
907                         seglen = namelen;
908                     }
909                 }
910 #endif /* !OS2 (Whatever that alias was we're over it) */
911
912                 /* That's it, the rest is path info. 
913                  * I don't know how we aught to handle this.  Should
914                  * we define a new error to indicate 'more info'?
915                  * Should we split out the rest of the path?
916                  */
917                 if ((finfo.filetype != APR_DIR) && 
918                     (finfo.filetype != APR_LNK) && saveslash) 
919                     rv = APR_ENOTDIR;
920 #ifdef XXX_FIGURE_THIS_OUT
921                 {
922                     /* the example inserts a null between the end of 
923                      * the filename and the next segment, and increments
924                      * the path length so we would return both segments.
925                      */
926                     if (saveslash) {
927                         keptlen += seglen;
928                         path[keptlen] = saveslash;
929                         if (pathlen + 1 >= sizeof(path))
930                             return APR_ENAMETOOLONG;
931                         memmove(path + keptlen + 1,
932                                path + keptlen,
933                                pathlen - keptlen);
934                         path[keptlen] = '\0';
935                         ++pathlen;
936                         break;
937                     }
938                 }
939 #endif
940             }
941
942             /* put back the '/' */
943             if (saveslash) {
944                 path[keptlen + seglen] = saveslash;
945                 ++seglen;
946             }
947             keptlen += seglen;
948
949             if (rv != APR_SUCCESS) {
950                 if (APR_STATUS_IS_ENOENT(rv))
951                     break;
952                 if (APR_STATUS_IS_EPATHWILD(rv))
953                     /* This path included wildcards.  The path elements
954                      * that did not contain wildcards are canonicalized,
955                      * so we will return the path, although later elements
956                      * don't necessarily exist, and aren't canonical.
957                      */
958                     break;
959                 else if (APR_STATUS_IS_ENOTDIR(rv))
960                     /* This is a little more serious, we just added a name
961                      * onto a filename (think http's PATH_INFO)
962                      * If the caller is foolish enough to do this, we expect
963                      * the've already canonicalized the root) that they knew
964                      * what they are doing :(
965                      */
966                     break;
967                 else
968                     return rv;
969             }
970         }
971     }
972
973     *newpath = apr_pstrmemdup(p, path, pathlen);
974     return APR_SUCCESS;
975 }
976
977
978 APR_DECLARE(apr_status_t) apr_filepath_list_split(apr_array_header_t **pathelts,
979                                                   const char *liststr,
980                                                   apr_pool_t *p)
981 {
982     return apr_filepath_list_split_impl(pathelts, liststr, ';', p);
983 }
984
985 APR_DECLARE(apr_status_t) apr_filepath_list_merge(char **liststr,
986                                                   apr_array_header_t *pathelts,
987                                                   apr_pool_t *p)
988 {
989     return apr_filepath_list_merge_impl(liststr, pathelts, ';', p);
990 }
991
992
993 APR_DECLARE(apr_status_t) apr_filepath_encoding(int *style, apr_pool_t *p)
994 {
995 #if APR_HAS_UNICODE_FS
996     IF_WIN_OS_IS_UNICODE
997     {
998         *style = APR_FILEPATH_ENCODING_UTF8;
999         return APR_SUCCESS;
1000     }
1001 #endif
1002
1003     *style = APR_FILEPATH_ENCODING_LOCALE;
1004     return APR_SUCCESS;
1005 }