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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
18 #include "apr_private.h"
19 #include "apr_arch_file_io.h"
20 #include "apr_strings.h"
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/
37 * OS2 appears immune from the nonsense :)
40 APR_DECLARE(apr_status_t) apr_filepath_root(const char **rootpath,
45 const char *testpath = *inpath;
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];
55 if (inpath && *inpath)
56 volsep = strchr (*inpath, ':');
60 if (strlen(*inpath) > APR_PATH_MAX) {
64 seperator[0] = (flags & APR_FILEPATH_NATIVE) ? '\\' : '/';
66 /* Allocate and initialize each of the segment buffers
68 server[0] = volume[0] = file[0] = '\0';
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.
74 /* Split the inpath into its separate parts. */
75 deconstruct(testpath, server, volume, NULL, file, NULL, &elements, PATH_UNDEF);
77 /* If we got a volume part then continue splitting out the root.
78 Otherwise we either have an incomplete or relative path
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);
84 /* NetWare doesn't add the root slash so we need to add it manually.
86 strcat(newpath, seperator);
89 /* Skip the inpath pointer down to the first non-root character
94 } while (*newpath && ((*newpath == '/') || (*newpath == '\\')));
97 /* Need to handle APR_FILEPATH_TRUENAME checking here. */
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
109 *rootpath = apr_pstrdup(p, seperator);
112 } while ((**inpath == '/') || (**inpath == '\\'));
115 return APR_ERELATIVE;
117 return APR_EINCOMPLETE;
119 #else /* ndef(NETWARE) */
125 seperator[0] = (flags & APR_FILEPATH_NATIVE) ? '\\' : '/';
128 if (testpath[0] == '/' || testpath[0] == '\\') {
129 if (testpath[1] == '/' || testpath[1] == '\\') {
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] == ':')
138 /* given '//?/C: or //./C: let us try this
139 * all over again from the drive designator
141 rv = apr_filepath_root(rootpath, &testpath, flags, p);
142 if (!rv || rv == APR_EINCOMPLETE)
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'
157 /* This must not be a path to a file, but rather
158 * a volume or device. Die for now.
162 #endif /* WIN32 (non - //server/share syntax) */
164 /* Evaluate path of '//[machine/[share[/]]]' */
165 delim1 = testpath + 2;
167 /* Protect against //X/ where X is illegal */
168 if (*delim1 && !IS_FNCHAR(*(delim1++)))
170 } while (*delim1 && *delim1 != '/' && *delim1 != '\\');
175 while (*delim2 && *delim2 != '/' && *delim2 != '\\') {
176 /* Protect against //machine/X/ where X is illegal */
177 if (!IS_FNCHAR(*(delim2++)))
181 /* Copy the '//machine/[share[/]]' path, always providing
182 * an extra byte for the trailing slash.
184 newpath = apr_pstrmemdup(p, testpath, delim2 - testpath + 1);
186 if (delim2 == delim1 + 1) {
187 /* We found simply \\machine\, so give up already
191 return APR_EINCOMPLETE;
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.
204 newpath[delim1 - testpath] = '\\';
205 newpath[delim2 - testpath] = '\\';
207 rv = filepath_root_test(newpath, p);
210 rv = filepath_root_case(&newpath, newpath, p);
213 newpath[0] = seperator[0];
214 newpath[1] = seperator[0];
215 newpath[delim1 - testpath] = seperator[0];
216 newpath[delim2 - testpath] = (*delim2 ? seperator[0] : '\0');
219 /* Give back the caller's own choice of delimiters
221 newpath[0] = testpath[0];
222 newpath[1] = testpath[1];
223 newpath[delim1 - testpath] = *delim1;
224 newpath[delim2 - testpath] = *delim2;
227 /* If this root included the trailing / or \ designation
228 * then lop off multiple trailing slashes and give back
229 * appropriate delimiters.
232 *inpath = delim2 + 1;
233 while (**inpath == '/' || **inpath == '\\')
244 /* Have path of '\\[machine]', if the machine is given,
245 * append same trailing slash as the leading slash
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];
253 newpath[delim1 - testpath] = newpath[0];
254 newpath[delim1 - testpath + 1] = '\0';
257 newpath = apr_pstrndup(p, testpath, delim1 - testpath);
259 if (flags & APR_FILEPATH_TRUENAME) {
260 newpath[0] = seperator[0];
261 newpath[1] = seperator[0];
265 return APR_EINCOMPLETE;
268 /* Left with a path of '/', what drive are we asking about?
270 *inpath = testpath + 1;
271 newpath = apr_palloc(p, 2);
272 if (flags & APR_FILEPATH_TRUENAME)
273 newpath[0] = seperator[0];
275 newpath[0] = testpath[0];
278 return APR_EINCOMPLETE;
281 /* Evaluate path of 'd:[/]' */
282 if (IS_FNCHAR(*testpath) && testpath[1] == ':')
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.
291 newpath = apr_palloc(p, 4);
292 newpath[0] = testpath[0];
293 newpath[1] = testpath[1];
294 newpath[2] = seperator[0];
296 if (flags & APR_FILEPATH_TRUENAME) {
297 newpath[0] = apr_toupper(newpath[0]);
298 rv = filepath_root_test(newpath, p);
302 /* Just give back the root the user handed to us.
304 if (testpath[2] != '/' && testpath[2] != '\\') {
307 *inpath = testpath + 2;
308 return APR_EINCOMPLETE;
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
315 *inpath = testpath + 3;
316 while (**inpath == '/' || **inpath == '\\')
318 if (!(flags & APR_FILEPATH_TRUENAME))
319 newpath[2] = testpath[2];
324 /* Nothing interesting */
325 return APR_ERELATIVE;
327 #endif /* ndef(NETWARE) */
331 APR_DECLARE(apr_status_t) apr_filepath_merge(char **newpath,
332 const char *basepath,
337 char path[APR_PATH_MAX]; /* isn't null term */
338 const char *baseroot = NULL;
340 apr_size_t rootlen; /* the length of the root portion of path, d:/ is 3 */
341 apr_size_t baselen; /* the length of basepath (excluding baseroot) */
342 apr_size_t keptlen; /* the length of the retained basepath (incl root) */
343 apr_size_t pathlen; /* the length of the result path */
344 apr_size_t segend; /* the end of the current segment */
345 apr_size_t seglen; /* the length of the segment (excl trailing chars) */
346 apr_status_t basetype = 0; /* from parsing the basepath's baseroot */
347 apr_status_t addtype; /* from parsing the addpath's addroot */
350 int fixunc = 0; /* flag to complete an incomplete UNC basepath */
353 /* Treat null as an empty path, otherwise split addroot from the addpath
356 addpath = addroot = "";
357 addtype = APR_ERELATIVE;
360 /* This call _should_ test the path
362 addtype = apr_filepath_root(&addroot, &addpath,
363 APR_FILEPATH_TRUENAME
364 | (flags & APR_FILEPATH_NATIVE),
366 if (addtype == APR_SUCCESS) {
367 addtype = APR_EABSOLUTE;
369 else if (addtype == APR_ERELATIVE) {
372 else if (addtype != APR_EINCOMPLETE) {
373 /* apr_filepath_root was incomprehensible so fail already
379 /* If addpath is (even partially) rooted, then basepath is
380 * unused. Ths violates any APR_FILEPATH_SECUREROOTTEST
381 * and APR_FILEPATH_NOTABSOLUTE flags specified.
383 if (addtype == APR_EABSOLUTE || addtype == APR_EINCOMPLETE)
385 if (flags & APR_FILEPATH_SECUREROOTTEST)
386 return APR_EABOVEROOT;
387 if (flags & APR_FILEPATH_NOTABSOLUTE)
391 /* Optimized tests before we query the current working path
395 /* If APR_FILEPATH_NOTABOVEROOT wasn't specified,
396 * we won't test the root again, it's ignored.
397 * Waste no CPU retrieving the working path.
399 if (addtype == APR_EABSOLUTE && !(flags & APR_FILEPATH_NOTABOVEROOT)) {
400 basepath = baseroot = "";
401 basetype = APR_ERELATIVE;
404 /* If APR_FILEPATH_NOTABSOLUTE is specified, the caller
405 * requires an absolutely relative result, So do not retrieve
408 if (addtype == APR_ERELATIVE && (flags & APR_FILEPATH_NOTABSOLUTE)) {
409 basepath = baseroot = "";
410 basetype = APR_ERELATIVE;
416 /* Start with the current working path. This is bass akwards,
417 * but required since the compiler (at least vc) doesn't like
418 * passing the address of a char const* for a char** arg.
419 * We must grab the current path of the designated drive
420 * if addroot is given in drive-relative form (e.g. d:foo)
424 if (addtype == APR_EINCOMPLETE && addroot[1] == ':')
425 rv = filepath_drive_get(&getpath, addroot[0], flags, p);
428 rv = apr_filepath_get(&getpath, flags, p);
429 if (rv != APR_SUCCESS)
435 /* This call should _not_ test the path
437 basetype = apr_filepath_root(&baseroot, &basepath,
438 (flags & APR_FILEPATH_NATIVE), p);
439 if (basetype == APR_SUCCESS) {
440 basetype = APR_EABSOLUTE;
442 else if (basetype == APR_ERELATIVE) {
445 else if (basetype != APR_EINCOMPLETE) {
446 /* apr_filepath_root was incomprehensible so fail already
451 baselen = strlen(basepath);
453 /* If APR_FILEPATH_NOTABSOLUTE is specified, the caller
454 * requires an absolutely relative result. If the given
455 * basepath is not relative then fail.
457 if ((flags & APR_FILEPATH_NOTABSOLUTE) && basetype != APR_ERELATIVE)
460 /* The Win32 nightmare on unc street... start combining for
461 * many possible root combinations.
463 if (addtype == APR_EABSOLUTE)
465 /* Ignore the given root path, and start with the addroot
467 if ((flags & APR_FILEPATH_NOTABOVEROOT)
468 && strncmp(baseroot, addroot, strlen(baseroot)))
469 return APR_EABOVEROOT;
471 rootlen = pathlen = strlen(addroot);
472 memcpy(path, addroot, pathlen);
474 else if (addtype == APR_EINCOMPLETE)
476 /* There are several types of incomplete paths,
477 * incomplete UNC paths (//foo/ or //),
478 * drives without rooted paths (d: as in d:foo),
479 * and simple roots (/ as in /foo).
480 * Deal with these in significantly different manners...
483 if ((addroot[0] == '/' || addroot[0] == '\\') &&
484 (addroot[1] == '/' || addroot[1] == '\\'))
486 /* Ignore the given root path if the incomplete addpath is UNC,
487 * (note that the final result will be incomplete).
489 if (flags & APR_FILEPATH_NOTRELATIVE)
491 if ((flags & APR_FILEPATH_NOTABOVEROOT)
492 && strncmp(baseroot, addroot, strlen(baseroot)))
493 return APR_EABOVEROOT;
496 rootlen = pathlen = strlen(addroot);
497 memcpy(path, addroot, pathlen);
501 if ((addroot[0] == '/' || addroot[0] == '\\') && !addroot[1])
503 /* Bring together the drive or UNC root from the baseroot
504 * if the addpath is a simple root and basepath is rooted,
505 * otherwise disregard the basepath entirely.
507 if (basetype != APR_EABSOLUTE && (flags & APR_FILEPATH_NOTRELATIVE))
509 if (basetype != APR_ERELATIVE) {
511 if (basetype == APR_INCOMPLETE
512 && (baseroot[0] == '/' || baseroot[0] == '\\')
513 && (baseroot[1] == '/' || baseroot[1] == '\\'))
516 keptlen = rootlen = pathlen = strlen(baseroot);
517 memcpy(path, baseroot, pathlen);
520 if (flags & APR_FILEPATH_NOTABOVEROOT)
521 return APR_EABOVEROOT;
523 rootlen = pathlen = strlen(addroot);
524 memcpy(path, addroot, pathlen);
528 else if (filepath_has_drive(addroot, DRIVE_ONLY, p))
530 /* If the addroot is a drive (without a volume root)
531 * use the basepath _if_ it matches this drive letter!
532 * Otherwise we must discard the basepath.
534 if (!filepath_compare_drive(addroot, baseroot, p) &&
535 filepath_has_drive(baseroot, 0, p)) {
537 else if (addroot[0] && addroot[1] == ':' && !addroot[2])
539 /* If the addroot is a drive (without a volume root)
540 * use the basepath _if_ it matches this drive letter!
541 * Otherwise we must discard the basepath.
543 if (addroot[0] == baseroot[0] && baseroot[1] == ':') {
545 /* Base the result path on the basepath
547 if (basetype != APR_EABSOLUTE && (flags & APR_FILEPATH_NOTRELATIVE))
549 rootlen = strlen(baseroot);
550 keptlen = pathlen = rootlen + baselen;
551 if (keptlen >= sizeof(path))
552 return APR_ENAMETOOLONG;
553 memcpy(path, baseroot, rootlen);
554 memcpy(path + rootlen, basepath, baselen);
557 if (flags & APR_FILEPATH_NOTRELATIVE)
559 if (flags & APR_FILEPATH_NOTABOVEROOT)
560 return APR_EABOVEROOT;
562 rootlen = pathlen = strlen(addroot);
563 memcpy(path, addroot, pathlen);
567 /* Now this is unexpected, we aren't aware of any other
568 * incomplete path forms! Fail now.
573 else { /* addtype == APR_ERELATIVE */
574 /* If both paths are relative, fail early
576 if (basetype != APR_EABSOLUTE && (flags & APR_FILEPATH_NOTRELATIVE))
580 /* An incomplete UNC path must be completed
582 if (basetype == APR_INCOMPLETE
583 && (baseroot[0] == '/' || baseroot[0] == '\\')
584 && (baseroot[1] == '/' || baseroot[1] == '\\'))
588 /* Base the result path on the basepath
590 rootlen = strlen(baseroot);
591 keptlen = pathlen = rootlen + baselen;
592 if (keptlen >= sizeof(path))
593 return APR_ENAMETOOLONG;
594 memcpy(path, baseroot, rootlen);
595 memcpy(path + rootlen, basepath, baselen);
598 /* '/' terminate the given root path unless it's already terminated
599 * or is an incomplete drive root. Correct the trailing slash unless
600 * we have an incomplete UNC path still to fix.
602 if (pathlen && path[pathlen - 1] != ':') {
603 if (path[pathlen - 1] != '/' && path[pathlen - 1] != '\\') {
604 if (pathlen + 1 >= sizeof(path))
605 return APR_ENAMETOOLONG;
607 path[pathlen++] = ((flags & APR_FILEPATH_NATIVE) ? '\\' : '/');
609 /* XXX: wrong, but gotta figure out what I intended;
611 * path[pathlen++] = ((flags & APR_FILEPATH_NATIVE) ? '\\' : '/');
617 /* Parse each segment, find the closing '/'
620 while (addpath[seglen] && addpath[seglen] != '/'
621 && addpath[seglen] != '\\')
624 /* Truncate all trailing spaces and all but the first two dots */
626 while (seglen && (addpath[seglen - 1] == ' '
627 || addpath[seglen - 1] == '.')) {
628 if (seglen > 2 || addpath[seglen - 1] != '.' || addpath[0] != '.')
634 if (seglen == 0 || (seglen == 1 && addpath[0] == '.'))
636 /* NOTE: win32 _hates_ '/ /' and '/. /' (yes, with spaces in there)
637 * so eliminate all preconceptions that it is valid.
643 /* This isn't legal unless the unc path is completed
649 /* Otherwise, this is a noop segment (/ or ./) so ignore it
652 else if (seglen == 2 && addpath[0] == '.' && addpath[1] == '.')
654 /* NOTE: win32 _hates_ '/.. /' (yes, with a space in there)
655 * and '/..../', some functions treat it as ".", and some
656 * fail! Eliminate all preconceptions that they are valid.
658 if (seglen < segend && (seglen != 3 || addpath[2] != '.'))
662 /* This isn't legal unless the unc path is completed
668 /* backpath (../) when an absolute path is given */
669 if (rootlen && (pathlen <= rootlen))
671 /* Attempt to move above root. Always die if the
672 * APR_FILEPATH_SECUREROOTTEST flag is specified.
674 if (flags & APR_FILEPATH_SECUREROOTTEST)
675 return APR_EABOVEROOT;
677 /* Otherwise this is simply a noop, above root is root.
680 else if (pathlen == 0
683 || path[pathlen - 4] == ':'
684 || path[pathlen - 4] == '/'
685 || path[pathlen - 4] == '\\')
686 && path[pathlen - 3] == '.'
687 && path[pathlen - 2] == '.'
688 && (path[pathlen - 1] == '/'
689 || path[pathlen - 1] == '\\')))
691 /* Verified path is empty, exactly "..[/\]", or ends
692 * in "[:/\]..[/\]" - these patterns we will not back
693 * over since they aren't 'prior segements'.
695 * If APR_FILEPATH_SECUREROOTTEST.was given, die now.
697 if (flags & APR_FILEPATH_SECUREROOTTEST)
698 return APR_EABOVEROOT;
700 /* Otherwise append another backpath.
702 if (pathlen + 3 >= sizeof(path))
703 return APR_ENAMETOOLONG;
704 path[pathlen++] = '.';
705 path[pathlen++] = '.';
706 if (addpath[segend]) {
707 path[pathlen++] = ((flags & APR_FILEPATH_NATIVE)
708 ? '\\' : ((flags & APR_FILEPATH_TRUENAME)
709 ? '/' : addpath[segend]));
711 /* The 'root' part of this path now includes the ../ path,
712 * because that backpath will not be parsed by the truename
719 /* otherwise crop the prior segment
723 } while (pathlen && path[pathlen - 1] != '/'
724 && path[pathlen - 1] != '\\');
726 /* Now test if we are above where we started and back up
727 * the keptlen offset to reflect the added/altered path.
729 if (pathlen < keptlen)
731 if (flags & APR_FILEPATH_SECUREROOTTEST)
732 return APR_EABOVEROOT;
737 else /* not empty or dots */
741 const char *testpath = path;
742 const char *testroot;
743 apr_status_t testtype;
744 apr_size_t i = (addpath[segend] != '\0');
746 /* This isn't legal unless the unc path is complete!
750 if (pathlen + seglen + 1 >= sizeof(path))
751 return APR_ENAMETOOLONG;
752 memcpy(path + pathlen, addpath, seglen + i);
754 /* Always add the trailing slash to a UNC segment
756 path[pathlen + seglen] = ((flags & APR_FILEPATH_NATIVE)
758 pathlen += seglen + 1;
760 /* Recanonicalize the UNC root with the new UNC segment,
761 * and if we succeed, reset this test and the rootlen,
762 * and replace our path with the canonical UNC root path
764 path[pathlen] = '\0';
765 /* This call _should_ test the path
767 testtype = apr_filepath_root(&testroot, &testpath,
768 APR_FILEPATH_TRUENAME
769 | (flags & APR_FILEPATH_NATIVE),
771 if (testtype == APR_SUCCESS) {
772 rootlen = pathlen = (testpath - path);
773 memcpy(path, testroot, pathlen);
776 else if (testtype != APR_EINCOMPLETE) {
777 /* apr_filepath_root was very unexpected so fail already
785 /* An actual segment, append it to the destination path
787 apr_size_t i = (addpath[segend] != '\0');
788 if (pathlen + seglen + i >= sizeof(path))
789 return APR_ENAMETOOLONG;
790 memcpy(path + pathlen, addpath, seglen + i);
792 path[pathlen + seglen] = ((flags & APR_FILEPATH_NATIVE)
794 pathlen += seglen + i;
798 /* Skip over trailing slash to the next segment
806 /* keptlen will be the baselen unless the addpath contained
807 * backpath elements. If so, and APR_FILEPATH_NOTABOVEROOT
808 * is specified (APR_FILEPATH_SECUREROOTTEST was caught above),
809 * compare the string beyond the root to assure the result path
810 * is still within given basepath. Note that the root path
811 * segment is thoroughly tested prior to path parsing.
813 if ((flags & APR_FILEPATH_NOTABOVEROOT) && baselen) {
814 if (memcmp(basepath, path + rootlen, baselen) != 0)
815 return APR_EABOVEROOT;
817 /* Ahem... if we have a basepath without a trailing slash,
818 * we better be sure that /foo wasn't replaced with /foobar!
820 if (basepath[baselen - 1] != '/' && basepath[baselen - 1] != '\\'
821 && path[rootlen + baselen] && path[rootlen + baselen] != '/'
822 && path[rootlen + baselen] != '\\')
823 return APR_EABOVEROOT;
826 if (addpath && (flags & APR_FILEPATH_TRUENAME)) {
827 /* We can always skip the root, it's already true-named. */
828 if (rootlen > keptlen)
830 if ((path[keptlen] == '/') || (path[keptlen] == '\\')) {
831 /* By rights, keptlen may grown longer than pathlen.
832 * we wont' use it again (in that case) so we don't care.
836 /* Go through all the new segments */
837 while (keptlen < pathlen) {
841 /* find any slash and set it aside for a minute. */
842 for (seglen = 0; keptlen + seglen < pathlen; ++seglen) {
843 if ((path[keptlen + seglen] == '/') ||
844 (path[keptlen + seglen] == '\\')) {
845 saveslash = path[keptlen + seglen];
849 /* Null term for stat! */
850 path[keptlen + seglen] = '\0';
851 if ((rv = apr_lstat(&finfo, path,
852 APR_FINFO_TYPE | APR_FINFO_NAME, p))
854 apr_size_t namelen = strlen(finfo.name);
856 #if defined(OS2) /* only has case folding, never aliases that change the length */
858 if (memcmp(finfo.name, path + keptlen, seglen) != 0) {
859 memcpy(path + keptlen, finfo.name, namelen);
861 #else /* WIN32 || NETWARE; here there be aliases that gire and gimble and change length */
863 if ((namelen != seglen) ||
864 (memcmp(finfo.name, path + keptlen, seglen) != 0))
866 if (namelen <= seglen) {
867 memcpy(path + keptlen, finfo.name, namelen);
868 if ((namelen < seglen) && saveslash) {
869 memmove(path + keptlen + namelen + 1,
870 path + keptlen + seglen + 1,
871 pathlen - keptlen - seglen);
872 pathlen += namelen - seglen;
876 else { /* namelen > seglen */
877 if (pathlen + namelen - seglen >= sizeof(path))
878 return APR_ENAMETOOLONG;
880 memmove(path + keptlen + namelen + 1,
881 path + keptlen + seglen + 1,
882 pathlen - keptlen - seglen);
884 memcpy(path + keptlen, finfo.name, namelen);
885 pathlen += namelen - seglen;
889 #endif /* !OS2 (Whatever that alias was we're over it) */
891 /* That's it, the rest is path info.
892 * I don't know how we aught to handle this. Should
893 * we define a new error to indicate 'more info'?
894 * Should we split out the rest of the path?
896 if ((finfo.filetype != APR_DIR) &&
897 (finfo.filetype != APR_LNK) && saveslash)
899 #ifdef XXX_FIGURE_THIS_OUT
901 /* the example inserts a null between the end of
902 * the filename and the next segment, and increments
903 * the path length so we would return both segments.
907 path[keptlen] = saveslash;
908 if (pathlen + 1 >= sizeof(path))
909 return APR_ENAMETOOLONG;
910 memmove(path + keptlen + 1,
913 path[keptlen] = '\0';
921 /* put back the '/' */
923 path[keptlen + seglen] = saveslash;
928 if (rv != APR_SUCCESS) {
929 if (APR_STATUS_IS_ENOENT(rv))
931 if (APR_STATUS_IS_EPATHWILD(rv))
932 /* This path included wildcards. The path elements
933 * that did not contain wildcards are canonicalized,
934 * so we will return the path, although later elements
935 * don't necessarily exist, and aren't canonical.
938 else if (APR_STATUS_IS_ENOTDIR(rv))
939 /* This is a little more serious, we just added a name
940 * onto a filename (think http's PATH_INFO)
941 * If the caller is foolish enough to do this, we expect
942 * the've already canonicalized the root) that they knew
943 * what they are doing :(
952 *newpath = apr_pmemdup(p, path, pathlen + 1);
953 (*newpath)[pathlen] = '\0';
958 APR_DECLARE(apr_status_t) apr_filepath_list_split(apr_array_header_t **pathelts,
962 return apr_filepath_list_split_impl(pathelts, liststr, ';', p);
965 APR_DECLARE(apr_status_t) apr_filepath_list_merge(char **liststr,
966 apr_array_header_t *pathelts,
969 return apr_filepath_list_merge_impl(liststr, pathelts, ';', p);
973 APR_DECLARE(apr_status_t) apr_filepath_encoding(int *style, apr_pool_t *p)
975 #if APR_HAS_UNICODE_FS
978 *style = APR_FILEPATH_ENCODING_UTF8;
983 *style = APR_FILEPATH_ENCODING_LOCALE;