summaryrefslogtreecommitdiffstats
path: root/file_io/win32/filepath.c
diff options
context:
space:
mode:
Diffstat (limited to 'file_io/win32/filepath.c')
-rw-r--r--file_io/win32/filepath.c1005
1 files changed, 1005 insertions, 0 deletions
diff --git a/file_io/win32/filepath.c b/file_io/win32/filepath.c
new file mode 100644
index 0000000..5dbe5ae
--- /dev/null
+++ b/file_io/win32/filepath.c
@@ -0,0 +1,1005 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "apr.h"
+#include "apr_private.h"
+#include "apr_arch_file_io.h"
+#include "apr_strings.h"
+#include "apr_lib.h"
+#include <string.h>
+#include <ctype.h>
+
+#ifdef NETWARE
+#include <unistd.h>
+#include <fsio.h>
+#endif
+
+ /* WinNT accepts several odd forms of a 'root' path. Under Unicode
+ * calls (ApiFunctionW) the //?/C:/foo or //?/UNC/mach/share/foo forms
+ * are accepted. Ansi and Unicode functions both accept the //./C:/foo
+ * form under WinNT/2K. Since these forms are handled in the utf-8 to
+ * unicode translation phase, we don't want the user confused by them, so
+ * we will accept them but always return the canonical C:/ or //mach/share/
+ *
+ * OS2 appears immune from the nonsense :)
+ */
+
+APR_DECLARE(apr_status_t) apr_filepath_root(const char **rootpath,
+ const char **inpath,
+ apr_int32_t flags,
+ apr_pool_t *p)
+{
+ const char *testpath = *inpath;
+ char *newpath;
+#ifdef NETWARE
+ char seperator[2] = { 0, 0};
+ char server[APR_PATH_MAX+1];
+ char volume[APR_PATH_MAX+1];
+ char file[APR_PATH_MAX+1];
+ char *volsep = NULL;
+ int elements;
+
+ if (inpath && *inpath)
+ volsep = strchr (*inpath, ':');
+ else
+ return APR_EBADPATH;
+
+ if (strlen(*inpath) > APR_PATH_MAX) {
+ return APR_EBADPATH;
+ }
+
+ seperator[0] = (flags & APR_FILEPATH_NATIVE) ? '\\' : '/';
+
+ /* Allocate and initialize each of the segment buffers
+ */
+ server[0] = volume[0] = file[0] = '\0';
+
+ /* If we don't have a volume separator then don't bother deconstructing
+ the path since we won't use the deconstructed information anyway.
+ */
+ if (volsep) {
+ /* Split the inpath into its separate parts. */
+ deconstruct(testpath, server, volume, NULL, file, NULL, &elements, PATH_UNDEF);
+
+ /* If we got a volume part then continue splitting out the root.
+ Otherwise we either have an incomplete or relative path
+ */
+ if (volume && strlen(volume) > 0) {
+ newpath = apr_pcalloc(p, strlen(server)+strlen(volume)+5);
+ construct(newpath, server, volume, NULL, NULL, NULL, PATH_NETWARE);
+
+ /* NetWare doesn't add the root slash so we need to add it manually.
+ */
+ strcat(newpath, seperator);
+ *rootpath = newpath;
+
+ /* Skip the inpath pointer down to the first non-root character
+ */
+ newpath = volsep;
+ do {
+ ++newpath;
+ } while (*newpath && ((*newpath == '/') || (*newpath == '\\')));
+ *inpath = newpath;
+
+ /* Need to handle APR_FILEPATH_TRUENAME checking here. */
+
+ return APR_SUCCESS;
+ }
+ else
+ return APR_EBADPATH;
+ }
+ else if ((**inpath == '/') || (**inpath == '\\')) {
+ /* if we have a root path without a volume then just split
+ in same manner as unix although this path will be
+ incomplete.
+ */
+ *rootpath = apr_pstrdup(p, seperator);
+ do {
+ ++(*inpath);
+ } while ((**inpath == '/') || (**inpath == '\\'));
+ }
+ else
+ return APR_ERELATIVE;
+
+ return APR_EINCOMPLETE;
+
+#else /* ndef(NETWARE) */
+
+ char seperator[2];
+ const char *delim1;
+ const char *delim2;
+
+ seperator[0] = (flags & APR_FILEPATH_NATIVE) ? '\\' : '/';
+ seperator[1] = 0;
+
+ if (testpath[0] == '/' || testpath[0] == '\\') {
+ if (testpath[1] == '/' || testpath[1] == '\\') {
+
+#ifdef WIN32 /* //server/share isn't the only // delimited syntax */
+ if ((testpath[2] == '?' || testpath[2] == '.')
+ && (testpath[3] == '/' || testpath[3] == '\\')) {
+ if (IS_FNCHAR(testpath[4]) && testpath[5] == ':')
+ {
+ apr_status_t rv;
+ testpath += 4;
+ /* given '//?/C: or //./C: let us try this
+ * all over again from the drive designator
+ */
+ rv = apr_filepath_root(rootpath, &testpath, flags, p);
+ if (!rv || rv == APR_EINCOMPLETE)
+ *inpath = testpath;
+ return rv;
+ }
+ else if (strncasecmp(testpath + 4, "UNC", 3) == 0
+ && (testpath[7] == '/' || testpath[7] == '\\')
+ && (testpath[2] == '?')) {
+ /* given '//?/UNC/machine/share, a little magic
+ * at the end makes this all work out by using
+ * 'C/machine' as the starting point and replacing
+ * the UNC delimiters with \'s, including the 'C'
+ */
+ testpath += 6;
+ }
+ else
+ /* This must not be a path to a file, but rather
+ * a volume or device. Die for now.
+ */
+ return APR_EBADPATH;
+ }
+#endif /* WIN32 (non - //server/share syntax) */
+
+ /* Evaluate path of '//[machine/[share[/]]]' */
+ delim1 = testpath + 2;
+ do {
+ /* Protect against //X/ where X is illegal */
+ if (*delim1 && !IS_FNCHAR(*(delim1++)))
+ return APR_EBADPATH;
+ } while (*delim1 && *delim1 != '/' && *delim1 != '\\');
+
+ if (*delim1) {
+ apr_status_t rv;
+ delim2 = delim1 + 1;
+ while (*delim2 && *delim2 != '/' && *delim2 != '\\') {
+ /* Protect against //machine/X/ where X is illegal */
+ if (!IS_FNCHAR(*(delim2++)))
+ return APR_EBADPATH;
+ }
+
+ /* Copy the '//machine/[share[/]]' path, always providing
+ * an extra byte for the trailing slash.
+ */
+ newpath = apr_pstrmemdup(p, testpath, delim2 - testpath + 1);
+
+ if (delim2 == delim1 + 1) {
+ /* We found simply \\machine\, so give up already
+ */
+ *rootpath = newpath;
+ *inpath = delim2;
+ return APR_EINCOMPLETE;
+ }
+
+ if (flags & APR_FILEPATH_TRUENAME) {
+ /* Validate the \\Machine\Share\ designation,
+ * Win32 will argue about slashed in UNC paths,
+ * so use backslashes till we finish testing,
+ * and add the trailing backslash [required].
+ * apr_pstrmemdup above guarentees us the new
+ * trailing null character.
+ */
+ newpath[0] = '\\';
+ newpath[1] = '\\';
+ newpath[delim1 - testpath] = '\\';
+ newpath[delim2 - testpath] = '\\';
+
+ rv = filepath_root_test(newpath, p);
+ if (rv)
+ return rv;
+ rv = filepath_root_case(&newpath, newpath, p);
+ if (rv)
+ return rv;
+ newpath[0] = seperator[0];
+ newpath[1] = seperator[0];
+ newpath[delim1 - testpath] = seperator[0];
+ newpath[delim2 - testpath] = (*delim2 ? seperator[0] : '\0');
+ }
+ else {
+ /* Give back the caller's own choice of delimiters
+ */
+ newpath[0] = testpath[0];
+ newpath[1] = testpath[1];
+ newpath[delim1 - testpath] = *delim1;
+ newpath[delim2 - testpath] = *delim2;
+ }
+
+ /* If this root included the trailing / or \ designation
+ * then lop off multiple trailing slashes and give back
+ * appropriate delimiters.
+ */
+ if (*delim2) {
+ *inpath = delim2 + 1;
+ while (**inpath == '/' || **inpath == '\\')
+ ++*inpath;
+ }
+ else {
+ *inpath = delim2;
+ }
+
+ *rootpath = newpath;
+ return APR_SUCCESS;
+ }
+
+ /* Have path of '\\[machine]', if the machine is given,
+ * append same trailing slash as the leading slash
+ */
+ delim1 = strchr(testpath, '\0');
+ if (delim1 > testpath + 2) {
+ newpath = apr_pstrndup(p, testpath, delim1 - testpath + 1);
+ if (flags & APR_FILEPATH_TRUENAME)
+ newpath[delim1 - testpath] = seperator[0];
+ else
+ newpath[delim1 - testpath] = newpath[0];
+ newpath[delim1 - testpath + 1] = '\0';
+ }
+ else {
+ newpath = apr_pstrndup(p, testpath, delim1 - testpath);
+ }
+ if (flags & APR_FILEPATH_TRUENAME) {
+ newpath[0] = seperator[0];
+ newpath[1] = seperator[0];
+ }
+ *rootpath = newpath;
+ *inpath = delim1;
+ return APR_EINCOMPLETE;
+ }
+
+ /* Left with a path of '/', what drive are we asking about?
+ */
+ *inpath = testpath + 1;
+ newpath = apr_palloc(p, 2);
+ if (flags & APR_FILEPATH_TRUENAME)
+ newpath[0] = seperator[0];
+ else
+ newpath[0] = testpath[0];
+ newpath[1] = '\0';
+ *rootpath = newpath;
+ return APR_EINCOMPLETE;
+ }
+
+ /* Evaluate path of 'd:[/]' */
+ if (IS_FNCHAR(*testpath) && testpath[1] == ':')
+ {
+ apr_status_t rv;
+ /* Validate that D:\ drive exists, test must be rooted
+ * Note that posix/win32 insists a drive letter is upper case,
+ * so who are we to argue with a 'feature'.
+ * It is a safe fold, since only A-Z is legal, and has no
+ * side effects of legal mis-mapped non-us-ascii codes.
+ */
+ newpath = apr_palloc(p, 4);
+ newpath[0] = testpath[0];
+ newpath[1] = testpath[1];
+ newpath[2] = seperator[0];
+ newpath[3] = '\0';
+ if (flags & APR_FILEPATH_TRUENAME) {
+ newpath[0] = apr_toupper(newpath[0]);
+ rv = filepath_root_test(newpath, p);
+ if (rv)
+ return rv;
+ }
+ /* Just give back the root the user handed to us.
+ */
+ if (testpath[2] != '/' && testpath[2] != '\\') {
+ newpath[2] = '\0';
+ *rootpath = newpath;
+ *inpath = testpath + 2;
+ return APR_EINCOMPLETE;
+ }
+
+ /* strip off remaining slashes that designate the root,
+ * give the caller back their original choice of slash
+ * unless this is TRUENAME'ed
+ */
+ *inpath = testpath + 3;
+ while (**inpath == '/' || **inpath == '\\')
+ ++*inpath;
+ if (!(flags & APR_FILEPATH_TRUENAME))
+ newpath[2] = testpath[2];
+ *rootpath = newpath;
+ return APR_SUCCESS;
+ }
+
+ /* Nothing interesting */
+ return APR_ERELATIVE;
+
+#endif /* ndef(NETWARE) */
+}
+
+#if !defined(NETWARE)
+static int same_drive(const char *path1, const char *path2)
+{
+ char drive1 = path1[0];
+ char drive2 = path2[0];
+
+ if (!drive1 || !drive2 || path1[1] != ':' || path2[1] != ':')
+ return FALSE;
+
+ if (drive1 == drive2)
+ return TRUE;
+
+ if (drive1 >= 'a' && drive1 <= 'z')
+ drive1 += 'A' - 'a';
+
+ if (drive2 >= 'a' && drive2 <= 'z')
+ drive2 += 'A' - 'a';
+
+ return (drive1 == drive2);
+}
+#endif
+
+APR_DECLARE(apr_status_t) apr_filepath_merge(char **newpath,
+ const char *basepath,
+ const char *addpath,
+ apr_int32_t flags,
+ apr_pool_t *p)
+{
+ char path[APR_PATH_MAX]; /* isn't null term */
+ const char *baseroot = NULL;
+ const char *addroot;
+ apr_size_t rootlen; /* the length of the root portion of path, d:/ is 3 */
+ apr_size_t baselen; /* the length of basepath (excluding baseroot) */
+ apr_size_t keptlen; /* the length of the retained basepath (incl root) */
+ apr_size_t pathlen; /* the length of the result path */
+ apr_size_t segend; /* the end of the current segment */
+ apr_size_t seglen; /* the length of the segment (excl trailing chars) */
+ apr_status_t basetype = 0; /* from parsing the basepath's baseroot */
+ apr_status_t addtype; /* from parsing the addpath's addroot */
+ apr_status_t rv;
+#ifndef NETWARE
+ int fixunc = 0; /* flag to complete an incomplete UNC basepath */
+#endif
+
+ /* Treat null as an empty path, otherwise split addroot from the addpath
+ */
+ if (!addpath) {
+ addpath = addroot = "";
+ addtype = APR_ERELATIVE;
+ }
+ else {
+ /* This call _should_ test the path
+ */
+ addtype = apr_filepath_root(&addroot, &addpath,
+ APR_FILEPATH_TRUENAME
+ | (flags & APR_FILEPATH_NATIVE),
+ p);
+ if (addtype == APR_SUCCESS) {
+ addtype = APR_EABSOLUTE;
+ }
+ else if (addtype == APR_ERELATIVE) {
+ addroot = "";
+ }
+ else if (addtype != APR_EINCOMPLETE) {
+ /* apr_filepath_root was incomprehensible so fail already
+ */
+ return addtype;
+ }
+ }
+
+ /* If addpath is (even partially) rooted, then basepath is
+ * unused. Ths violates any APR_FILEPATH_SECUREROOTTEST
+ * and APR_FILEPATH_NOTABSOLUTE flags specified.
+ */
+ if (addtype == APR_EABSOLUTE || addtype == APR_EINCOMPLETE)
+ {
+ if (flags & APR_FILEPATH_SECUREROOTTEST)
+ return APR_EABOVEROOT;
+ if (flags & APR_FILEPATH_NOTABSOLUTE)
+ return addtype;
+ }
+
+ /* Optimized tests before we query the current working path
+ */
+ if (!basepath) {
+
+ /* If APR_FILEPATH_NOTABOVEROOT wasn't specified,
+ * we won't test the root again, it's ignored.
+ * Waste no CPU retrieving the working path.
+ */
+ if (addtype == APR_EABSOLUTE && !(flags & APR_FILEPATH_NOTABOVEROOT)) {
+ basepath = baseroot = "";
+ basetype = APR_ERELATIVE;
+ }
+
+ /* If APR_FILEPATH_NOTABSOLUTE is specified, the caller
+ * requires an absolutely relative result, So do not retrieve
+ * the working path.
+ */
+ if (addtype == APR_ERELATIVE && (flags & APR_FILEPATH_NOTABSOLUTE)) {
+ basepath = baseroot = "";
+ basetype = APR_ERELATIVE;
+ }
+ }
+
+ if (!basepath)
+ {
+ /* Start with the current working path. This is bass akwards,
+ * but required since the compiler (at least vc) doesn't like
+ * passing the address of a char const* for a char** arg.
+ * We must grab the current path of the designated drive
+ * if addroot is given in drive-relative form (e.g. d:foo)
+ */
+ char *getpath;
+#ifndef NETWARE
+ if (addtype == APR_EINCOMPLETE && addroot[1] == ':')
+ rv = filepath_drive_get(&getpath, addroot[0], flags, p);
+ else
+#endif
+ rv = apr_filepath_get(&getpath, flags, p);
+ if (rv != APR_SUCCESS)
+ return rv;
+ basepath = getpath;
+ }
+
+ if (!baseroot) {
+ /* This call should _not_ test the path
+ */
+ basetype = apr_filepath_root(&baseroot, &basepath,
+ (flags & APR_FILEPATH_NATIVE), p);
+ if (basetype == APR_SUCCESS) {
+ basetype = APR_EABSOLUTE;
+ }
+ else if (basetype == APR_ERELATIVE) {
+ baseroot = "";
+ }
+ else if (basetype != APR_EINCOMPLETE) {
+ /* apr_filepath_root was incomprehensible so fail already
+ */
+ return basetype;
+ }
+ }
+ baselen = strlen(basepath);
+
+ /* If APR_FILEPATH_NOTABSOLUTE is specified, the caller
+ * requires an absolutely relative result. If the given
+ * basepath is not relative then fail.
+ */
+ if ((flags & APR_FILEPATH_NOTABSOLUTE) && basetype != APR_ERELATIVE)
+ return basetype;
+
+ /* The Win32 nightmare on unc street... start combining for
+ * many possible root combinations.
+ */
+ if (addtype == APR_EABSOLUTE)
+ {
+ /* Ignore the given root path, and start with the addroot
+ */
+ if ((flags & APR_FILEPATH_NOTABOVEROOT)
+ && strncmp(baseroot, addroot, strlen(baseroot)))
+ return APR_EABOVEROOT;
+ keptlen = 0;
+ rootlen = pathlen = strlen(addroot);
+ memcpy(path, addroot, pathlen);
+ }
+ else if (addtype == APR_EINCOMPLETE)
+ {
+ /* There are several types of incomplete paths,
+ * incomplete UNC paths (//foo/ or //),
+ * drives without rooted paths (d: as in d:foo),
+ * and simple roots (/ as in /foo).
+ * Deal with these in significantly different manners...
+ */
+#ifndef NETWARE
+ if ((addroot[0] == '/' || addroot[0] == '\\') &&
+ (addroot[1] == '/' || addroot[1] == '\\'))
+ {
+ /* Ignore the given root path if the incomplete addpath is UNC,
+ * (note that the final result will be incomplete).
+ */
+ if (flags & APR_FILEPATH_NOTRELATIVE)
+ return addtype;
+ if ((flags & APR_FILEPATH_NOTABOVEROOT)
+ && strncmp(baseroot, addroot, strlen(baseroot)))
+ return APR_EABOVEROOT;
+ fixunc = 1;
+ keptlen = 0;
+ rootlen = pathlen = strlen(addroot);
+ memcpy(path, addroot, pathlen);
+ }
+ else
+#endif
+ if ((addroot[0] == '/' || addroot[0] == '\\') && !addroot[1])
+ {
+ /* Bring together the drive or UNC root from the baseroot
+ * if the addpath is a simple root and basepath is rooted,
+ * otherwise disregard the basepath entirely.
+ */
+ if (basetype != APR_EABSOLUTE && (flags & APR_FILEPATH_NOTRELATIVE))
+ return basetype;
+ if (basetype != APR_ERELATIVE) {
+#ifndef NETWARE
+ if (basetype == APR_INCOMPLETE
+ && (baseroot[0] == '/' || baseroot[0] == '\\')
+ && (baseroot[1] == '/' || baseroot[1] == '\\'))
+ fixunc = 1;
+#endif
+ keptlen = rootlen = pathlen = strlen(baseroot);
+ memcpy(path, baseroot, pathlen);
+ }
+ else {
+ if (flags & APR_FILEPATH_NOTABOVEROOT)
+ return APR_EABOVEROOT;
+ keptlen = 0;
+ rootlen = pathlen = strlen(addroot);
+ memcpy(path, addroot, pathlen);
+ }
+ }
+#ifdef NETWARE
+ else if (filepath_has_drive(addroot, DRIVE_ONLY, p))
+ {
+ /* If the addroot is a drive (without a volume root)
+ * use the basepath _if_ it matches this drive letter!
+ * Otherwise we must discard the basepath.
+ */
+ if (!filepath_compare_drive(addroot, baseroot, p) &&
+ filepath_has_drive(baseroot, 0, p)) {
+#else
+ else if (addroot[0] && addroot[1] == ':' && !addroot[2])
+ {
+ /* If the addroot is a drive (without a volume root)
+ * use the basepath _if_ it matches this drive letter!
+ * Otherwise we must discard the basepath.
+ */
+ if (same_drive(addroot, baseroot)) {
+#endif
+ /* Base the result path on the basepath
+ */
+ if (basetype != APR_EABSOLUTE && (flags & APR_FILEPATH_NOTRELATIVE))
+ return basetype;
+ rootlen = strlen(baseroot);
+ keptlen = pathlen = rootlen + baselen;
+ if (keptlen >= sizeof(path))
+ return APR_ENAMETOOLONG;
+ memcpy(path, baseroot, rootlen);
+ memcpy(path + rootlen, basepath, baselen);
+ }
+ else {
+ if (flags & APR_FILEPATH_NOTRELATIVE)
+ return addtype;
+ if (flags & APR_FILEPATH_NOTABOVEROOT)
+ return APR_EABOVEROOT;
+ keptlen = 0;
+ rootlen = pathlen = strlen(addroot);
+ memcpy(path, addroot, pathlen);
+ }
+ }
+ else {
+ /* Now this is unexpected, we aren't aware of any other
+ * incomplete path forms! Fail now.
+ */
+ return APR_EBADPATH;
+ }
+ }
+ else { /* addtype == APR_ERELATIVE */
+ /* If both paths are relative, fail early
+ */
+ if (basetype != APR_EABSOLUTE && (flags & APR_FILEPATH_NOTRELATIVE))
+ return basetype;
+
+#ifndef NETWARE
+ /* An incomplete UNC path must be completed
+ */
+ if (basetype == APR_INCOMPLETE
+ && (baseroot[0] == '/' || baseroot[0] == '\\')
+ && (baseroot[1] == '/' || baseroot[1] == '\\'))
+ fixunc = 1;
+#endif
+
+ /* Base the result path on the basepath
+ */
+ rootlen = strlen(baseroot);
+ keptlen = pathlen = rootlen + baselen;
+ if (keptlen >= sizeof(path))
+ return APR_ENAMETOOLONG;
+ memcpy(path, baseroot, rootlen);
+ memcpy(path + rootlen, basepath, baselen);
+ }
+
+ /* '/' terminate the given root path unless it's already terminated
+ * or is an incomplete drive root. Correct the trailing slash unless
+ * we have an incomplete UNC path still to fix.
+ */
+ if (pathlen && path[pathlen - 1] != ':') {
+ if (path[pathlen - 1] != '/' && path[pathlen - 1] != '\\') {
+ if (pathlen + 1 >= sizeof(path))
+ return APR_ENAMETOOLONG;
+
+ path[pathlen++] = ((flags & APR_FILEPATH_NATIVE) ? '\\' : '/');
+ }
+ /* XXX: wrong, but gotta figure out what I intended;
+ * else if (!fixunc)
+ * path[pathlen++] = ((flags & APR_FILEPATH_NATIVE) ? '\\' : '/');
+ */
+ }
+
+ while (*addpath)
+ {
+ /* Parse each segment, find the closing '/'
+ */
+ seglen = 0;
+ while (addpath[seglen] && addpath[seglen] != '/'
+ && addpath[seglen] != '\\')
+ ++seglen;
+
+ /* Truncate all trailing spaces and all but the first two dots */
+ segend = seglen;
+ while (seglen && (addpath[seglen - 1] == ' '
+ || addpath[seglen - 1] == '.')) {
+ if (seglen > 2 || addpath[seglen - 1] != '.' || addpath[0] != '.')
+ --seglen;
+ else
+ break;
+ }
+
+ if (seglen == 0 || (seglen == 1 && addpath[0] == '.'))
+ {
+ /* NOTE: win32 _hates_ '/ /' and '/. /' (yes, with spaces in there)
+ * so eliminate all preconceptions that it is valid.
+ */
+ if (seglen < segend)
+ return APR_EBADPATH;
+
+#ifndef NETWARE
+ /* This isn't legal unless the unc path is completed
+ */
+ if (fixunc)
+ return APR_EBADPATH;
+#endif
+
+ /* Otherwise, this is a noop segment (/ or ./) so ignore it
+ */
+ }
+ else if (seglen == 2 && addpath[0] == '.' && addpath[1] == '.')
+ {
+ /* NOTE: win32 _hates_ '/.. /' (yes, with a space in there)
+ * and '/..../', some functions treat it as ".", and some
+ * fail! Eliminate all preconceptions that they are valid.
+ */
+ if (seglen < segend && (seglen != 3 || addpath[2] != '.'))
+ return APR_EBADPATH;
+
+#ifndef NETWARE
+ /* This isn't legal unless the unc path is completed
+ */
+ if (fixunc)
+ return APR_EBADPATH;
+#endif
+
+ /* backpath (../) when an absolute path is given */
+ if (rootlen && (pathlen <= rootlen))
+ {
+ /* Attempt to move above root. Always die if the
+ * APR_FILEPATH_SECUREROOTTEST flag is specified.
+ */
+ if (flags & APR_FILEPATH_SECUREROOTTEST)
+ return APR_EABOVEROOT;
+
+ /* Otherwise this is simply a noop, above root is root.
+ */
+ }
+ else if (pathlen == 0
+ || (pathlen >= 3
+ && (pathlen == 3
+ || path[pathlen - 4] == ':'
+ || path[pathlen - 4] == '/'
+ || path[pathlen - 4] == '\\')
+ && path[pathlen - 3] == '.'
+ && path[pathlen - 2] == '.'
+ && (path[pathlen - 1] == '/'
+ || path[pathlen - 1] == '\\')))
+ {
+ /* Verified path is empty, exactly "..[/\]", or ends
+ * in "[:/\]..[/\]" - these patterns we will not back
+ * over since they aren't 'prior segements'.
+ *
+ * If APR_FILEPATH_SECUREROOTTEST.was given, die now.
+ */
+ if (flags & APR_FILEPATH_SECUREROOTTEST)
+ return APR_EABOVEROOT;
+
+ /* Otherwise append another backpath.
+ */
+ if (pathlen + 3 >= sizeof(path))
+ return APR_ENAMETOOLONG;
+ path[pathlen++] = '.';
+ path[pathlen++] = '.';
+ if (addpath[segend]) {
+ path[pathlen++] = ((flags & APR_FILEPATH_NATIVE)
+ ? '\\' : ((flags & APR_FILEPATH_TRUENAME)
+ ? '/' : addpath[segend]));
+ }
+ /* The 'root' part of this path now includes the ../ path,
+ * because that backpath will not be parsed by the truename
+ * code below.
+ */
+ keptlen = pathlen;
+ }
+ else
+ {
+ /* otherwise crop the prior segment
+ */
+ do {
+ --pathlen;
+ } while (pathlen && path[pathlen - 1] != '/'
+ && path[pathlen - 1] != '\\');
+
+ /* Now test if we are above where we started and back up
+ * the keptlen offset to reflect the added/altered path.
+ */
+ if (pathlen < keptlen)
+ {
+ if (flags & APR_FILEPATH_SECUREROOTTEST)
+ return APR_EABOVEROOT;
+ keptlen = pathlen;
+ }
+ }
+ }
+ else /* not empty or dots */
+ {
+#ifndef NETWARE
+ if (fixunc) {
+ const char *testpath = path;
+ const char *testroot;
+ apr_status_t testtype;
+ apr_size_t i = (addpath[segend] != '\0');
+
+ /* This isn't legal unless the unc path is complete!
+ */
+ if (seglen < segend)
+ return APR_EBADPATH;
+ if (pathlen + seglen + 1 >= sizeof(path))
+ return APR_ENAMETOOLONG;
+ memcpy(path + pathlen, addpath, seglen + i);
+
+ /* Always add the trailing slash to a UNC segment
+ */
+ path[pathlen + seglen] = ((flags & APR_FILEPATH_NATIVE)
+ ? '\\' : '/');
+ pathlen += seglen + 1;
+
+ /* Recanonicalize the UNC root with the new UNC segment,
+ * and if we succeed, reset this test and the rootlen,
+ * and replace our path with the canonical UNC root path
+ */
+ path[pathlen] = '\0';
+ /* This call _should_ test the path
+ */
+ testtype = apr_filepath_root(&testroot, &testpath,
+ APR_FILEPATH_TRUENAME
+ | (flags & APR_FILEPATH_NATIVE),
+ p);
+ if (testtype == APR_SUCCESS) {
+ rootlen = pathlen = (testpath - path);
+ memcpy(path, testroot, pathlen);
+ fixunc = 0;
+ }
+ else if (testtype != APR_EINCOMPLETE) {
+ /* apr_filepath_root was very unexpected so fail already
+ */
+ return testtype;
+ }
+ }
+ else
+#endif
+ {
+ /* An actual segment, append it to the destination path
+ */
+ apr_size_t i = (addpath[segend] != '\0');
+ if (pathlen + seglen + i >= sizeof(path))
+ return APR_ENAMETOOLONG;
+ memcpy(path + pathlen, addpath, seglen + i);
+ if (i)
+ path[pathlen + seglen] = ((flags & APR_FILEPATH_NATIVE)
+ ? '\\' : '/');
+ pathlen += seglen + i;
+ }
+ }
+
+ /* Skip over trailing slash to the next segment
+ */
+ if (addpath[segend])
+ ++segend;
+
+ addpath += segend;
+ }
+
+ /* keptlen will be the baselen unless the addpath contained
+ * backpath elements. If so, and APR_FILEPATH_NOTABOVEROOT
+ * is specified (APR_FILEPATH_SECUREROOTTEST was caught above),
+ * compare the string beyond the root to assure the result path
+ * is still within given basepath. Note that the root path
+ * segment is thoroughly tested prior to path parsing.
+ */
+ if ((flags & APR_FILEPATH_NOTABOVEROOT) && baselen) {
+ if (memcmp(basepath, path + rootlen, baselen) != 0)
+ return APR_EABOVEROOT;
+
+ /* Ahem... if we have a basepath without a trailing slash,
+ * we better be sure that /foo wasn't replaced with /foobar!
+ */
+ if (basepath[baselen - 1] != '/' && basepath[baselen - 1] != '\\'
+ && path[rootlen + baselen] && path[rootlen + baselen] != '/'
+ && path[rootlen + baselen] != '\\')
+ return APR_EABOVEROOT;
+ }
+
+ if (addpath && (flags & APR_FILEPATH_TRUENAME)) {
+ /* We can always skip the root, it's already true-named. */
+ if (rootlen > keptlen)
+ keptlen = rootlen;
+ if ((path[keptlen] == '/') || (path[keptlen] == '\\')) {
+ /* By rights, keptlen may grown longer than pathlen.
+ * we wont' use it again (in that case) so we don't care.
+ */
+ ++keptlen;
+ }
+ /* Go through all the new segments */
+ while (keptlen < pathlen) {
+ apr_finfo_t finfo;
+ char saveslash = 0;
+ seglen = 0;
+ /* find any slash and set it aside for a minute. */
+ for (seglen = 0; keptlen + seglen < pathlen; ++seglen) {
+ if ((path[keptlen + seglen] == '/') ||
+ (path[keptlen + seglen] == '\\')) {
+ saveslash = path[keptlen + seglen];
+ break;
+ }
+ }
+ /* Null term for stat! */
+ path[keptlen + seglen] = '\0';
+ if ((rv = apr_stat(&finfo, path,
+ APR_FINFO_LINK | APR_FINFO_TYPE | APR_FINFO_NAME, p))
+ == APR_SUCCESS) {
+ apr_size_t namelen = strlen(finfo.name);
+
+#if defined(OS2) /* only has case folding, never aliases that change the length */
+
+ if (memcmp(finfo.name, path + keptlen, seglen) != 0) {
+ memcpy(path + keptlen, finfo.name, namelen);
+ }
+#else /* WIN32 || NETWARE; here there be aliases that gire and gimble and change length */
+
+ if ((namelen != seglen) ||
+ (memcmp(finfo.name, path + keptlen, seglen) != 0))
+ {
+ if (namelen <= seglen) {
+ memcpy(path + keptlen, finfo.name, namelen);
+ if ((namelen < seglen) && saveslash) {
+ memmove(path + keptlen + namelen + 1,
+ path + keptlen + seglen + 1,
+ pathlen - keptlen - seglen);
+ }
+ pathlen += namelen - seglen;
+ seglen = namelen;
+ }
+ else { /* namelen > seglen */
+ if (pathlen + namelen - seglen >= sizeof(path))
+ return APR_ENAMETOOLONG;
+ if (saveslash) {
+ memmove(path + keptlen + namelen + 1,
+ path + keptlen + seglen + 1,
+ pathlen - keptlen - seglen);
+ }
+ memcpy(path + keptlen, finfo.name, namelen);
+ pathlen += namelen - seglen;
+ seglen = namelen;
+ }
+ }
+#endif /* !OS2 (Whatever that alias was we're over it) */
+
+ /* That's it, the rest is path info.
+ * I don't know how we aught to handle this. Should
+ * we define a new error to indicate 'more info'?
+ * Should we split out the rest of the path?
+ */
+ if ((finfo.filetype != APR_DIR) &&
+ (finfo.filetype != APR_LNK) && saveslash)
+ rv = APR_ENOTDIR;
+#ifdef XXX_FIGURE_THIS_OUT
+ {
+ /* the example inserts a null between the end of
+ * the filename and the next segment, and increments
+ * the path length so we would return both segments.
+ */
+ if (saveslash) {
+ keptlen += seglen;
+ path[keptlen] = saveslash;
+ if (pathlen + 1 >= sizeof(path))
+ return APR_ENAMETOOLONG;
+ memmove(path + keptlen + 1,
+ path + keptlen,
+ pathlen - keptlen);
+ path[keptlen] = '\0';
+ ++pathlen;
+ break;
+ }
+ }
+#endif
+ }
+
+ /* put back the '/' */
+ if (saveslash) {
+ path[keptlen + seglen] = saveslash;
+ ++seglen;
+ }
+ keptlen += seglen;
+
+ if (rv != APR_SUCCESS) {
+ if (APR_STATUS_IS_ENOENT(rv))
+ break;
+ if (APR_STATUS_IS_EPATHWILD(rv))
+ /* This path included wildcards. The path elements
+ * that did not contain wildcards are canonicalized,
+ * so we will return the path, although later elements
+ * don't necessarily exist, and aren't canonical.
+ */
+ break;
+ else if (APR_STATUS_IS_ENOTDIR(rv))
+ /* This is a little more serious, we just added a name
+ * onto a filename (think http's PATH_INFO)
+ * If the caller is foolish enough to do this, we expect
+ * the've already canonicalized the root) that they knew
+ * what they are doing :(
+ */
+ break;
+ else
+ return rv;
+ }
+ }
+ }
+
+ *newpath = apr_pstrmemdup(p, path, pathlen);
+ return APR_SUCCESS;
+}
+
+
+APR_DECLARE(apr_status_t) apr_filepath_list_split(apr_array_header_t **pathelts,
+ const char *liststr,
+ apr_pool_t *p)
+{
+ return apr_filepath_list_split_impl(pathelts, liststr, ';', p);
+}
+
+APR_DECLARE(apr_status_t) apr_filepath_list_merge(char **liststr,
+ apr_array_header_t *pathelts,
+ apr_pool_t *p)
+{
+ return apr_filepath_list_merge_impl(liststr, pathelts, ';', p);
+}
+
+
+APR_DECLARE(apr_status_t) apr_filepath_encoding(int *style, apr_pool_t *p)
+{
+#if APR_HAS_UNICODE_FS
+ IF_WIN_OS_IS_UNICODE
+ {
+ *style = APR_FILEPATH_ENCODING_UTF8;
+ return APR_SUCCESS;
+ }
+#endif
+
+ *style = APR_FILEPATH_ENCODING_LOCALE;
+ return APR_SUCCESS;
+}