Index: trunk/src/VBox/Runtime/common/misc/uri.cpp
===================================================================
--- trunk/src/VBox/Runtime/common/misc/uri.cpp	(revision 58067)
+++ trunk/src/VBox/Runtime/common/misc/uri.cpp	(revision 58068)
@@ -159,4 +159,5 @@
 {
     AssertReturn(pszString, VERR_INVALID_POINTER);
+    AssertPtrReturn(pszDst, VERR_INVALID_POINTER);
 
     /*
@@ -259,9 +260,117 @@
         size_t cchDecoded = pchDst - pszDecoded;
         Assert(cchDecoded <= cchString);
-        // if (cchString - cchDecoded > 64)  -  enable later!
+        if (cchString - cchDecoded > 64)
             RTStrRealloc(&pszDecoded, cchDecoded + 1);
     }
     return pszDecoded;
 }
+
+
+/**
+ * Calculates the decoded string length.
+ *
+ * @returns Number of chars (excluding the terminator).
+ * @param   pszString       The string to decode.
+ * @param   cchMax          The maximum string length (e.g. RTSTR_MAX).
+ */
+static size_t rtUriCalcDecodedLength(const char *pszString, size_t cchMax)
+{
+    size_t cchDecoded;
+    if (pszString)
+    {
+        size_t cchSrcLeft = cchDecoded = RTStrNLen(pszString, cchMax);
+        while (cchSrcLeft-- > 0)
+        {
+            char const ch = *pszString++;
+            if (ch != '%')
+            { /* typical */}
+            else if (   cchSrcLeft >= 2
+                     && RT_C_IS_XDIGIT(pszString[0])
+                     && RT_C_IS_XDIGIT(pszString[1]))
+            {
+                cchDecoded -= 2;
+                pszString  += 2;
+                cchSrcLeft -= 2;
+            }
+        }
+    }
+    else
+        cchDecoded = 0;
+    return cchDecoded;
+}
+
+
+/**
+ * Decodes a string into a buffer.
+ *
+ * @returns IPRT status code.
+ * @param   pchSrc      The source string.
+ * @param   cchSrc      The max number of bytes to decode in the source string.
+ * @param   pszDst      The destination buffer.
+ * @param   cbDst       The size of the buffer (including terminator).
+ */
+static int rtUriDecodeIntoBuffer(const char *pchSrc, size_t cchSrc, char *pszDst, size_t cbDst)
+{
+    AssertPtrReturn(pchSrc, VERR_INVALID_POINTER);
+    AssertPtrReturn(pszDst, VERR_INVALID_POINTER);
+
+    /*
+     * Knowing that the pszString itself is valid UTF-8, we only have to
+     * validate the escape sequences.
+     */
+    cchSrc = RTStrNLen(pchSrc, cchSrc);
+    while (cchSrc > 0)
+    {
+        const char *pchPct = (const char *)memchr(pchSrc, '%', cchSrc);
+        if (pchPct)
+        {
+            size_t cchBefore = pchPct - pchSrc;
+            AssertReturn(cchBefore + 1 < cbDst, VERR_BUFFER_OVERFLOW);
+            if (cchBefore)
+            {
+                memcpy(pszDst, pchSrc, cchBefore);
+                pszDst += cchBefore;
+                cbDst  -= cchBefore;
+                pchSrc += cchBefore;
+                cchSrc -= cchBefore;
+            }
+
+            char chHigh, chLow;
+            if (   cchSrc >= 3
+                && RT_C_IS_XDIGIT(chHigh = pchSrc[1])
+                && RT_C_IS_XDIGIT(chLow  = pchSrc[2]))
+            {
+                uint8_t b = RT_C_IS_DIGIT(chHigh) ? chHigh - '0' : (chHigh & ~0x20) - 'A' + 10;
+                b <<= 4;
+                b |= RT_C_IS_DIGIT(chLow) ? chLow - '0' : (chLow & ~0x20) - 'A' + 10;
+                *pszDst++ = (char)b;
+                pchSrc += 3;
+                cchSrc -= 3;
+            }
+            else
+            {
+                AssertFailed();
+                *pszDst++ = *pchSrc++;
+                cchSrc--;
+            }
+            cbDst -= 1;
+        }
+        else
+        {
+            AssertReturn(cchSrc < cbDst, VERR_BUFFER_OVERFLOW);
+            memcpy(pszDst, pchSrc, cchSrc);
+            pszDst += cchSrc;
+            cbDst  -= cchSrc;
+            pchSrc += cchSrc;
+            cchSrc  = 0;
+            break;
+        }
+    }
+
+    AssertReturn(cbDst > 0, VERR_BUFFER_OVERFLOW);
+    *pszDst = '\0';
+    return VINF_SUCCESS;
+}
+
 
 
@@ -838,109 +947,227 @@
 
 
-RTDECL(char *) RTUriFilePath(const char *pszUri, uint32_t uFormat)
-{
-    return RTUriFileNPath(pszUri, uFormat, RTSTR_MAX);
-}
-
-
-RTDECL(char *) RTUriFileNPath(const char *pszUri, uint32_t uFormat, size_t cchMax)
-{
-    AssertPtrReturn(pszUri, NULL);
-    AssertReturn(uFormat == URI_FILE_FORMAT_AUTO || uFormat == URI_FILE_FORMAT_UNIX || uFormat == URI_FILE_FORMAT_WIN, NULL);
-
-    /* Auto is based on the current OS. */
-    if (uFormat == URI_FILE_FORMAT_AUTO)
-#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
-        uFormat = URI_FILE_FORMAT_WIN;
-#else
-        uFormat = URI_FILE_FORMAT_UNIX;
-#endif
-
-    /* Check that this is a file URI. */
-    if (RTStrNICmp(pszUri, RT_STR_TUPLE("file:")) != 0)
-        return NULL;
-
+RTDECL(int) RTUriFilePathEx(const char *pszUri, uint32_t fPathStyle, char **ppszPath, size_t cbPath, size_t *pcchPath)
+{
+    /*
+     * Validate and adjust input.
+     */
+    if (pcchPath)
+    {
+        AssertPtrReturn(pcchPath, VERR_INVALID_POINTER);
+        *pcchPath = ~(size_t)0;
+    }
+    AssertPtrReturn(ppszPath, VERR_INVALID_POINTER);
+    AssertReturn(!(fPathStyle & ~RTPATH_STR_F_STYLE_MASK) && fPathStyle != RTPATH_STR_F_STYLE_RESERVED, VERR_INVALID_FLAGS);
+    if (fPathStyle == RTPATH_STR_F_STYLE_HOST)
+        fPathStyle = RTPATH_STYLE;
+    AssertPtrReturn(pszUri, VERR_INVALID_POINTER);
+
+    /*
+     * Check that this is a file URI.
+     */
+    if (RTStrNICmp(pszUri, RT_STR_TUPLE("file:")) == 0)
+    { /* likely */ }
+    else
+        return VERR_URI_NOT_FILE_SCHEME;
+
+    /*
+     * We may have a number of variations here, mostly thanks to
+     * various windows software.  First the canonical variations:
+     *     - file:///C:/Windows/System32/kernel32.dll
+     *     - file:///C|/Windows/System32/kernel32.dll
+     *     - file:///C:%5CWindows%5CSystem32%5Ckernel32.dll
+     *     - file://localhost/C:%5CWindows%5CSystem32%5Ckernel32.dll
+     *     - file://cifsserver.dev/systemshare%5CWindows%5CSystem32%5Ckernel32.dll
+     *     - file://cifsserver.dev:139/systemshare%5CWindows%5CSystem32%5Ckernel32.dll  (not quite sure here, but whatever)
+     *
+     * Legacy variant without any slashes after the schema:
+     *     - file:C:/Windows/System32/kernel32.dll
+     *     - file:C|/Windows/System32%5Ckernel32.dll
+     *     - file:~/.bashrc
+     *            \--path-/
+     *
+     * Legacy variant with exactly one slashes after the schema:
+     *     - file:/C:/Windows/System32%5Ckernel32.dll
+     *     - file:/C|/Windows/System32/kernel32.dll
+     *     - file:/usr/bin/env
+     *            \---path---/
+     *
+     * Legacy variant with two slashes after the schema and an unescaped DOS path:
+     *     - file://C:/Windows/System32\kernel32.dll (**)
+     *     - file://C|/Windows/System32\kernel32.dll
+     *                \---path---------------------/
+     *              -- authority, with ':' as non-working port separator
+     *
+     * Legacy variant with exactly four slashes after the schema and an unescaped DOS path.
+     *     - file:////C:/Windows\System32\user32.dll
+     *
+     * Legacy variant with four or more slashes after the schema and an unescaped UNC path:
+     *     - file:////cifsserver.dev/systemshare/System32%\kernel32.dll
+     *     - file://///cifsserver.dev/systemshare/System32\kernel32.dll
+     *              \---path--------------------------------------------/
+     *
+     * The the two unescaped variants shouldn't be handed to rtUriParse, which
+     * is good as we cannot actually handle the one marked by (**).  So, handle
+     * those two special when parsing.
+     */
     RTURIPARSED Parsed;
-    int rc = rtUriParse(pszUri, &Parsed);
+    int         rc;
+    size_t      cSlashes = 0;
+    while (pszUri[5 + cSlashes] == '/')
+        cSlashes++;
+    if (   (cSlashes == 2 || cSlashes == 4)
+        && RT_C_IS_ALPHA(pszUri[5 + cSlashes])
+        && (pszUri[5 + cSlashes + 1] == ':' || pszUri[5 + cSlashes + 1] == '|'))
+    {
+        RT_ZERO(Parsed); /* RTURIPARSED_F_CONTAINS_ESCAPED_CHARS is now clear. */
+        Parsed.offPath = 5 + cSlashes;
+        Parsed.cchPath = strlen(&pszUri[Parsed.offPath]);
+        rc = RTStrValidateEncoding(&pszUri[Parsed.offPath]);
+    }
+    else if (cSlashes >= 4)
+    {
+        RT_ZERO(Parsed);
+        Parsed.fFlags  = cSlashes > 4 ? RTURIPARSED_F_CONTAINS_ESCAPED_CHARS : 0;
+        Parsed.offPath = 5 + cSlashes - 2;
+        Parsed.cchPath = strlen(&pszUri[Parsed.offPath]);
+        rc = RTStrValidateEncoding(&pszUri[Parsed.offPath]);
+    }
+    else
+        rc = rtUriParse(pszUri, &Parsed);
     if (RT_SUCCESS(rc))
     {
-        /* No path detected? Take authority as path then. */
-        if (!Parsed.cchPath)
-        {
-            Parsed.cchPath      = Parsed.cchAuthority;
-            Parsed.offPath      = Parsed.offAuthority;
-            Parsed.cchAuthority = 0;
-        }
-    }
-
-    if (   RT_SUCCESS(rc)
-        && Parsed.cchPath)
-    {
+        /*
+         * Ignore localhost as hostname (it's implicit).
+         */
+        static char const s_szLocalhost[] = "localhost";
+        if (    Parsed.cchAuthorityHost == sizeof(s_szLocalhost) - 1U
+            &&  RTStrNICmp(&pszUri[Parsed.offAuthorityHost], RT_STR_TUPLE(s_szLocalhost)) == 0)
+        {
+            Parsed.cchAuthorityHost = 0;
+            Parsed.cchAuthority     = 0;
+        }
+
+        /*
+         * Ignore leading path slash/separator if we detect a DOS drive letter
+         * and we don't have a host name.
+         */
+        if (   Parsed.cchPath >= 3
+            && Parsed.cchAuthorityHost    == 0
+            && pszUri[Parsed.offPath]     == '/'           /* Leading path slash/separator. */
+            && (   pszUri[Parsed.offPath + 2] == ':'       /* Colon after drive letter. */
+                || pszUri[Parsed.offPath + 2] == '|')      /* Colon alternative. */
+            && RT_C_IS_ALPHA(pszUri[Parsed.offPath + 1]) ) /* Drive letter. */
+        {
+            Parsed.offPath++;
+            Parsed.cchPath--;
+        }
+
         /*
          * Calculate the size of the encoded result.
+         *
+         * Since we're happily returning "C:/Windows/System32/kernel.dll"
+         * style paths when the caller requested UNIX style paths, we will
+         * return straight UNC paths too ("//cifsserver/share/dir/file").
          */
-        size_t cbResult = 0;
-
-        /* Skip the leading slash if a DOS drive letter (e.g. "C:") is detected right after it. */
-        if (   Parsed.cchPath >= 3
-            && pszUri[Parsed.offPath]  == '/'        /* Leading slash. */
-            && RT_C_IS_ALPHA(pszUri[Parsed.offPath + 1]) /* Drive letter. */
-            && pszUri[Parsed.offPath + 2]  == ':')
-        {
-            Parsed.offPath++;
-            Parsed.cchPath--;
-        }
-
-        /* Windows: Authority given? Include authority as part of UNC path */
-        if (uFormat == URI_FILE_FORMAT_WIN && Parsed.cchAuthority)
-        {
-            cbResult += 2; /* UNC slashes "\\". */
-            cbResult += Parsed.cchAuthority;
-        }
-
-        cbResult += Parsed.cchPath;
-        cbResult += 1; /* Zero termination. */
-
-        /*
-         * Compose encoded string.
-         */
-        char *pszResult;
-        char *pszTmp = pszResult = RTStrAlloc(cbResult);
-        if (pszTmp)
-        {
-            size_t cbTmp = cbResult;
-
-            /* Windows: If an authority is given, add the required UNC prefix. */
-            if (uFormat == URI_FILE_FORMAT_WIN && Parsed.cchAuthority)
-            {
-                rc = RTStrCatP(&pszTmp, &cbTmp, "\\\\");
+        size_t cchDecodedHost = 0;
+        size_t cbResult;
+        if (Parsed.fFlags & RTURIPARSED_F_CONTAINS_ESCAPED_CHARS)
+        {
+            cchDecodedHost = rtUriCalcDecodedLength(&pszUri[Parsed.offAuthorityHost], Parsed.cchAuthorityHost);
+            cbResult = cchDecodedHost + rtUriCalcDecodedLength(&pszUri[Parsed.offPath], Parsed.cchPath) + 1;
+        }
+        else
+        {
+            cchDecodedHost = 0;
+            cbResult = Parsed.cchAuthorityHost + Parsed.cchPath + 1;
+        }
+        if (pcchPath)
+            *pcchPath = cbResult - 1;
+        if (cbResult > 1)
+        {
+            /*
+             * Prepare the necessary buffer space for the result.
+             */
+            char  *pszDst;
+            char  *pszFreeMe = NULL;
+            if (!cbPath || *ppszPath == NULL)
+            {
+                cbPath = RT_MAX(cbPath, cbResult);
+                *ppszPath = pszFreeMe = pszDst = RTStrAlloc(cbPath);
+                AssertReturn(pszDst, VERR_NO_STR_MEMORY);
+            }
+            else if (cbResult <= cbPath)
+                pszDst = *ppszPath;
+            else
+                return VERR_BUFFER_OVERFLOW;
+
+            /*
+             * Compose the result.
+             */
+            if (Parsed.fFlags & RTURIPARSED_F_CONTAINS_ESCAPED_CHARS)
+            {
+                rc = rtUriDecodeIntoBuffer(&pszUri[Parsed.offAuthorityHost],Parsed.cchAuthorityHost,
+                                           pszDst, cchDecodedHost + 1);
+                Assert(RT_SUCCESS(rc) && strlen(pszDst) == cchDecodedHost);
                 if (RT_SUCCESS(rc))
-                    rc = RTStrCatPEx(&pszTmp, &cbTmp, &pszUri[Parsed.offAuthority], Parsed.cchAuthority);
+                    rc = rtUriDecodeIntoBuffer(&pszUri[Parsed.offPath], Parsed.cchPath,
+                                               &pszDst[cchDecodedHost], cbResult - cchDecodedHost);
+                Assert(RT_SUCCESS(rc) && strlen(pszDst) == cbResult - 1);
+            }
+            else
+            {
+                memcpy(pszDst, &pszUri[Parsed.offAuthorityHost], Parsed.cchAuthorityHost);
+                memcpy(&pszDst[Parsed.cchAuthorityHost], &pszUri[Parsed.offPath], Parsed.cchPath);
+                pszDst[cbResult - 1] = '\0';
             }
             if (RT_SUCCESS(rc))
-                rc = RTStrCatPEx(&pszTmp, &cbTmp, &pszUri[Parsed.offPath], Parsed.cchPath);
-            AssertRC(rc); /* Shall not happen! */
-            if (RT_SUCCESS(rc))
             {
                 /*
-                 * Decode the string and switch the slashes around the request way before returning.
+                 * Convert colon DOS driver letter colon alternative.
+                 * We do this regardless of the desired path style.
                  */
-                char *pszPath = rtUriPercentDecodeN(pszResult, cbResult - 1 /* Minus termination */);
-                if (pszPath)
-                {
-                    RTStrFree(pszResult);
-
-                    if (uFormat == URI_FILE_FORMAT_UNIX)
-                        return RTPathChangeToUnixSlashes(pszPath, true);
-                    Assert(uFormat == URI_FILE_FORMAT_WIN);
-                    return RTPathChangeToDosSlashes(pszPath, true);
-                }
-
-                /* Failed. */
+                if (   RT_C_IS_ALPHA(pszDst[0])
+                    && pszDst[1] == '|')
+                    pszDst[1] = ':';
+
+                /*
+                 * Fix slashes.
+                 */
+                if (fPathStyle == RTPATH_STR_F_STYLE_DOS)
+                    RTPathChangeToDosSlashes(pszDst, true);
+                else if (fPathStyle == RTPATH_STR_F_STYLE_UNIX)
+                    RTPathChangeToUnixSlashes(pszDst, true); /** @todo not quite sure how this actually makes sense... */
+                else
+                    AssertFailed();
+                return rc;
             }
-            RTStrFree(pszResult);
-        }
-    }
+
+            /* bail out */
+            RTStrFree(pszFreeMe);
+        }
+        else
+            rc = VERR_PATH_ZERO_LENGTH;
+    }
+    return rc;
+}
+
+
+RTDECL(char *) RTUriFilePath(const char *pszUri, uint32_t uFormat)
+{
+    uint32_t fPathStyle;
+    switch (uFormat)
+    {
+        case URI_FILE_FORMAT_WIN:  fPathStyle = RTPATH_STR_F_STYLE_DOS; break;
+        case URI_FILE_FORMAT_UNIX: fPathStyle = RTPATH_STR_F_STYLE_UNIX; break;
+        case URI_FILE_FORMAT_AUTO: fPathStyle = RTPATH_STR_F_STYLE_HOST; break;
+        default: AssertFailedReturn(NULL);
+    }
+
+    char *pszPath = NULL;
+    int rc = RTUriFilePathEx(pszUri, fPathStyle, &pszPath, 0 /*cbPath*/, NULL /*pcchPath*/);
+    if (RT_SUCCESS(rc))
+        return pszPath;
     return NULL;
 }
 
+
Index: trunk/src/VBox/Runtime/testcase/tstRTUri.cpp
===================================================================
--- trunk/src/VBox/Runtime/testcase/tstRTUri.cpp	(revision 58067)
+++ trunk/src/VBox/Runtime/testcase/tstRTUri.cpp	(revision 58068)
@@ -305,4 +305,5 @@
 {
     const char     *pszPath;
+    uint32_t        fPathPathStyle;
     const char     *pszUri;
     uint32_t        uFormat;
@@ -313,4 +314,5 @@
     {   /* #0: */
         /* .pszPath          =*/ "C:\\over\\ <>#%\"{}|^[]`\\there",
+        /* .fPathPathStyle   =*/ RTPATH_STR_F_STYLE_DOS,
         /* .pszUri           =*/ "file:///C:%5Cover/%20%3C%3E%23%25%22%7B%7D%7C%5E%5B%5D%60%5Cthere",
         /* .uFormat          =*/ URI_FILE_FORMAT_WIN,
@@ -322,4 +324,5 @@
     {   /* #1: */
         /* .pszPath          =*/ "/over/ <>#%\"{}|^[]`/there",
+        /* .fPathPathStyle   =*/ RTPATH_STR_F_STYLE_UNIX,
         /* .pszUri           =*/ "file:///over/%20%3C%3E%23%25%22%7B%7D%7C%5E%5B%5D%60/there",
         /* .uFormat          =*/ URI_FILE_FORMAT_UNIX,
@@ -331,4 +334,5 @@
     {   /* #2: */
         /* .pszPath          =*/ NULL,
+        /* .fPathPathStyle   =*/ RTPATH_STR_F_STYLE_UNIX,
         /* .pszUri           =*/ "file://",
         /* .uFormat          =*/ URI_FILE_FORMAT_UNIX,
@@ -340,4 +344,5 @@
     {   /* #3: */
         /* .pszPath          =*/ NULL,
+        /* .fPathPathStyle   =*/ RTPATH_STR_F_STYLE_DOS,
         /* .pszUri           =*/ "file://",
         /* .uFormat          =*/ URI_FILE_FORMAT_WIN,
@@ -349,4 +354,5 @@
     {   /* #4: */
         /* .pszPath          =*/ "/",
+        /* .fPathPathStyle   =*/ RTPATH_STR_F_STYLE_UNIX,
         /* .pszUri           =*/ "file:///",
         /* .uFormat          =*/ URI_FILE_FORMAT_UNIX,
@@ -358,4 +364,5 @@
     {   /* #5: */
         /* .pszPath          =*/ "\\",
+        /* .fPathPathStyle   =*/ RTPATH_STR_F_STYLE_DOS,
         /* .pszUri           =*/ "file:///",
         /* .uFormat          =*/ URI_FILE_FORMAT_WIN,
@@ -367,4 +374,5 @@
     {   /* #6: */
         /* .pszPath          =*/ "/foo/bar",
+        /* .fPathPathStyle   =*/ RTPATH_STR_F_STYLE_UNIX,
         /* .pszUri           =*/ "file:///foo/bar",
         /* .uFormat          =*/ URI_FILE_FORMAT_UNIX,
@@ -376,4 +384,5 @@
     {   /* #7: */
         /* .pszPath          =*/ "\\foo\\bar",
+        /* .fPathPathStyle   =*/ RTPATH_STR_F_STYLE_DOS,
         /* .pszUri           =*/ "file:///foo%5Cbar",
         /* .uFormat          =*/ URI_FILE_FORMAT_WIN,
@@ -385,4 +394,5 @@
     {   /* #8: */
         /* .pszPath          =*/ "C:/over/ <>#%\"{}|^[]`/there",
+        /* .fPathPathStyle   =*/ RTPATH_STR_F_STYLE_UNIX,
         /* .pszUri           =*/ "file:///C:/over/%20%3C%3E%23%25%22%7B%7D%7C%5E%5B%5D%60/there",
         /* .uFormat          =*/ URI_FILE_FORMAT_UNIX,
@@ -394,4 +404,5 @@
     {   /* #9: */
         /* .pszPath          =*/ "\\over\\ <>#%\"{}|^[]`\\there",
+        /* .fPathPathStyle   =*/ RTPATH_STR_F_STYLE_DOS,
         /* .pszUri           =*/ "file:///over/%20%3C%3E%23%25%22%7B%7D%7C%5E%5B%5D%60/there",
         /* .uFormat          =*/ URI_FILE_FORMAT_WIN,
@@ -403,4 +414,5 @@
     {   /* #10: */
         /* .pszPath          =*/ "/usr/bin/grep",
+        /* .fPathPathStyle   =*/ RTPATH_STR_F_STYLE_UNIX,
         /* .pszUri           =*/ "file:///usr/bin/grep",
         /* .uFormat          =*/ URI_FILE_FORMAT_UNIX,
@@ -412,4 +424,5 @@
     {   /* #11: */
         /* .pszPath          =*/ "\\usr\\bin\\grep",
+        /* .fPathPathStyle   =*/ RTPATH_STR_F_STYLE_DOS,
         /* .pszUri           =*/ "file:///usr/bin/grep",
         /* .uFormat          =*/ URI_FILE_FORMAT_WIN,
@@ -421,4 +434,5 @@
     {   /* #12: */
         /* .pszPath          =*/ "/somerootsubdir/isos/files.lst",
+        /* .fPathPathStyle   =*/ RTPATH_STR_F_STYLE_UNIX,
         /* .pszUri           =*/ "file:///somerootsubdir/isos/files.lst",
         /* .uFormat          =*/ URI_FILE_FORMAT_UNIX,
@@ -430,4 +444,5 @@
     {   /* #13: */
         /* .pszPath          =*/ "\\not-a-cifsserver\\isos\\files.lst",
+        /* .fPathPathStyle   =*/ RTPATH_STR_F_STYLE_DOS,
         /* .pszUri           =*/ "file:///not-a-cifsserver/isos/files.lst",
         /* .uFormat          =*/ URI_FILE_FORMAT_WIN,
@@ -439,4 +454,5 @@
     {   /* #14: */
         /* .pszPath          =*/ "/rootsubdir/isos/files.lst",
+        /* .fPathPathStyle   =*/ RTPATH_STR_F_STYLE_UNIX,
         /* .pszUri           =*/ "file:///rootsubdir/isos/files.lst",
         /* .uFormat          =*/ URI_FILE_FORMAT_UNIX,
@@ -448,4 +464,5 @@
     {   /* #15: */
         /* .pszPath          =*/ "\\not-a-cifsserver-either\\isos\\files.lst",
+        /* .fPathPathStyle   =*/ RTPATH_STR_F_STYLE_DOS,
         /* .pszUri           =*/ "file:///not-a-cifsserver-either/isos/files.lst",
         /* .uFormat          =*/ URI_FILE_FORMAT_WIN,
@@ -457,4 +474,5 @@
     {   /* #16: */
         /* .pszPath          =*/ "\\\\cifsserver\\isos\\files.lst",
+        /* .fPathPathStyle   =*/ RTPATH_STR_F_STYLE_DOS,
         /* .pszUri           =*/ "file:////cifsserver/isos/files.lst",
         /* .uFormat          =*/ URI_FILE_FORMAT_WIN,
@@ -466,4 +484,5 @@
     {   /* #17: */
         /* .pszPath          =*/ "c:boot.ini",
+        /* .fPathPathStyle   =*/ RTPATH_STR_F_STYLE_DOS,
         /* .pszUri           =*/ "file://localhost/c:boot.ini",
         /* .uFormat          =*/ URI_FILE_FORMAT_WIN,
@@ -475,4 +494,5 @@
     {   /* #18: */
         /* .pszPath          =*/ "c:boot.ini",
+        /* .fPathPathStyle   =*/ RTPATH_STR_F_STYLE_DOS,
         /* .pszUri           =*/ "file:///c|boot.ini",
         /* .uFormat          =*/ URI_FILE_FORMAT_WIN,
@@ -481,4 +501,124 @@
         /* PathCreateFromUrl =   "c:boot.ini" - same */
         /* UrlCreateFromPath =   "file:///c:boot.ini" - same */
+    },
+    {   /* #19: */
+        /* .pszPath          =*/ "c:\\legacy-no-slash.ini",
+        /* .fPathPathStyle   =*/ RTPATH_STR_F_STYLE_DOS,
+        /* .pszUri           =*/ "file:c:\\legacy-no-slash%2Eini",
+        /* .uFormat          =*/ URI_FILE_FORMAT_WIN,
+        /* .pszCreatedPath   =*/ "c:\\legacy-no-slash.ini",
+        /* .pszCreatedUri    =*/ "file:///c:/legacy-no-slash.ini",
+        /* PathCreateFromUrl =   "c:\\legacy-no-slash.ini" - same */
+        /* UrlCreateFromPath =   "file:///c:/legacy-no-slash.ini" - same */
+    },
+    {   /* #20: */
+        /* .pszPath          =*/ "c:\\legacy-no-slash.ini",
+        /* .fPathPathStyle   =*/ RTPATH_STR_F_STYLE_DOS,
+        /* .pszUri           =*/ "file:c|\\legacy-no-slash%2Eini",
+        /* .uFormat          =*/ URI_FILE_FORMAT_WIN,
+        /* .pszCreatedPath   =*/ "c:\\legacy-no-slash.ini",
+        /* .pszCreatedUri    =*/ "file:///c:/legacy-no-slash.ini",
+        /* PathCreateFromUrl =   "c:\\legacy-no-slash.ini" - same */
+        /* UrlCreateFromPath =   "file:///c:/legacy-no-slash.ini" - same */
+    },
+    {   /* #21: */
+        /* .pszPath          =*/ "c:\\legacy-single-slash.ini",
+        /* .fPathPathStyle   =*/ RTPATH_STR_F_STYLE_DOS,
+        /* .pszUri           =*/ "file:/c:\\legacy-single-slash%2Eini",
+        /* .uFormat          =*/ URI_FILE_FORMAT_WIN,
+        /* .pszCreatedPath   =*/ "c:\\legacy-single-slash.ini",
+        /* .pszCreatedUri    =*/ "file:///c:/legacy-single-slash.ini",
+        /* PathCreateFromUrl =   "c:\\legacy-single-slash.ini" - same */
+        /* UrlCreateFromPath =   "file:///c:/legacy-single-slash.ini" - same */
+    },
+    {   /* #22: */
+        /* .pszPath          =*/ "c:\\legacy-single-slash.ini",
+        /* .fPathPathStyle   =*/ RTPATH_STR_F_STYLE_DOS,
+        /* .pszUri           =*/ "file:/c:\\legacy-single-slash%2Eini",
+        /* .uFormat          =*/ URI_FILE_FORMAT_WIN,
+        /* .pszCreatedPath   =*/ "c:\\legacy-single-slash.ini",
+        /* .pszCreatedUri    =*/ "file:///c:/legacy-single-slash.ini",
+        /* PathCreateFromUrl =   "c:\\legacy-single-slash.ini" - same */
+        /* UrlCreateFromPath =   "file:///c:/legacy-single-slash.ini" - same */
+    },
+    {   /* #23: */
+        /* .pszPath          =*/ "\\legacy-single-slash.ini",
+        /* .fPathPathStyle   =*/ RTPATH_STR_F_STYLE_DOS,
+        /* .pszUri           =*/ "file:/legacy-single-slash%2Eini",
+        /* .uFormat          =*/ URI_FILE_FORMAT_WIN,
+        /* .pszCreatedPath   =*/ "\\legacy-single-slash.ini",
+        /* .pszCreatedUri    =*/ "file:///legacy-single-slash.ini",
+        /* PathCreateFromUrl =   "\\legacy-single-slash.ini" - same */
+        /* UrlCreateFromPath =   "file:///legacy-single-slash.ini" - same */
+    },
+    {   /* #24: */
+        /* .pszPath          =*/ "C:\\legacy-double-slash%2E.ini",
+        /* .fPathPathStyle   =*/ RTPATH_STR_F_STYLE_DOS,
+        /* .pszUri           =*/ "file://C:\\legacy-double-slash%2E.ini",
+        /* .uFormat          =*/ URI_FILE_FORMAT_WIN,
+        /* .pszCreatedPath   =*/ "C:\\legacy-double-slash%2E.ini",
+        /* .pszCreatedUri    =*/ "file:///C:/legacy-double-slash%252E.ini",
+        /* PathCreateFromUrl =   "C:\\legacy-double-slash%2E.ini" - same */
+        /* UrlCreateFromPath =   "file:///C:/legacy-double-slash.ini" - same */
+    },
+    {   /* #25: */
+        /* .pszPath          =*/ "C:\\legacy-double-slash%2E.ini",
+        /* .fPathPathStyle   =*/ RTPATH_STR_F_STYLE_DOS,
+        /* .pszUri           =*/ "file://C|/legacy-double-slash%2E.ini",
+        /* .uFormat          =*/ URI_FILE_FORMAT_WIN,
+        /* .pszCreatedPath   =*/ "C:\\legacy-double-slash%2E.ini",
+        /* .pszCreatedUri    =*/ "file:///C:/legacy-double-slash%252E.ini",
+        /* PathCreateFromUrl =   "C:\\legacy-double-slash%2E.ini" - same */
+        /* UrlCreateFromPath =   "file:///C:/legacy-double-slash%252E.ini" - same */
+    },
+    {   /* #26: */
+        /* .pszPath          =*/ "C:\\legacy-4-slashes%2E.ini",
+        /* .fPathPathStyle   =*/ RTPATH_STR_F_STYLE_DOS,
+        /* .pszUri           =*/ "file:////C|/legacy-4-slashes%2E.ini",
+        /* .uFormat          =*/ URI_FILE_FORMAT_WIN,
+        /* .pszCreatedPath   =*/ "C:\\legacy-4-slashes%2E.ini",
+        /* .pszCreatedUri    =*/ "file:///C:/legacy-4-slashes%252E.ini",
+        /* PathCreateFromUrl =   "C:\\legacy-4-slashes%2E.ini" - same */
+        /* UrlCreateFromPath =   "file:///C:/legacy-4-slashes%252E.ini" - same */
+    },
+    {   /* #27: */
+        /* .pszPath          =*/ "C:\\legacy-4-slashes%2E.ini",
+        /* .fPathPathStyle   =*/ RTPATH_STR_F_STYLE_DOS,
+        /* .pszUri           =*/ "file:////C:/legacy-4-slashes%2E.ini",
+        /* .uFormat          =*/ URI_FILE_FORMAT_WIN,
+        /* .pszCreatedPath   =*/ "C:\\legacy-4-slashes%2E.ini",
+        /* .pszCreatedUri    =*/ "file:///C:/legacy-4-slashes%252E.ini",
+        /* PathCreateFromUrl =   "C:\\legacy-4-slashes%2E.ini" - same */
+        /* UrlCreateFromPath =   "file:///C:/legacy-4-slashes%252E.ini" - same */
+    },
+    {   /* #28: */
+        /* .pszPath          =*/ "\\\\cifsserver\\share\\legacy-4-slashes%2E.ini",
+        /* .fPathPathStyle   =*/ RTPATH_STR_F_STYLE_DOS,
+        /* .pszUri           =*/ "file:////cifsserver/share/legacy-4-slashes%2E.ini",
+        /* .uFormat          =*/ URI_FILE_FORMAT_WIN,
+        /* .pszCreatedPath   =*/ "\\\\cifsserver\\share\\legacy-4-slashes%2E.ini",
+        /* .pszCreatedUri    =*/ "file://cifsserver/share/legacy-4-slashes%252E.ini",
+        /* PathCreateFromUrl =   "\\\\cifsserver\\share\\legacy-4-slashes%2E.ini" - same */
+        /* UrlCreateFromPath =   "file://cifsserver/share/legacy-4-slashes%252E.ini" - same */
+    },
+    {   /* #29: */
+        /* .pszPath          =*/ "\\\\cifsserver\\share\\legacy-5-slashes.ini",
+        /* .fPathPathStyle   =*/ RTPATH_STR_F_STYLE_DOS,
+        /* .pszUri           =*/ "file://///cifsserver/share/legacy-5-slashes%2Eini",
+        /* .uFormat          =*/ URI_FILE_FORMAT_WIN,
+        /* .pszCreatedPath   =*/ "\\\\cifsserver\\share\\legacy-5-slashes.ini",
+        /* .pszCreatedUri    =*/ "file://cifsserver/share/legacy-5-slashes.ini",
+        /* PathCreateFromUrl =   "\\\\cifsserver\\share\\legacy-5-slashes.ini" - same */
+        /* UrlCreateFromPath =   "file://cifsserver/share/legacy-5-slashes.ini" - same */
+    },
+    {   /* #30: */
+        /* .pszPath          =*/ "\\\\C|\\share\\legacy-5-slashes.ini",
+        /* .fPathPathStyle   =*/ RTPATH_STR_F_STYLE_DOS,
+        /* .pszUri           =*/ "file://///C|/share/legacy-5-slashes%2Eini",
+        /* .uFormat          =*/ URI_FILE_FORMAT_WIN,
+        /* .pszCreatedPath   =*/ "\\\\C|\\share\\legacy-5-slashes.ini",
+        /* .pszCreatedUri    =*/ "file://C%7C/share/legacy-5-slashes.ini",
+        /* PathCreateFromUrl =   "\\\\C|\\share\\legacy-5-slashes.ini" - same */
+        /* UrlCreateFromPath =   "file:///C%7C/share/legacy-5-slashes.ini" - differs */
     },
 };
@@ -585,5 +725,5 @@
             WCHAR wszResult2[_1K];
             DWORD cwcResult2 = RT_ELEMENTS(wszResult2);
-            hrc = UrlEscapeW(wszResult, wszResult2, &cwcResult2, URL_DONT_ESCAPE_EXTRA_INFO );
+            hrc = UrlEscapeW(wszResult, wszResult2, &cwcResult2, URL_DONT_ESCAPE_EXTRA_INFO | URL_ESCAPE_AS_UTF8 );
             if (SUCCEEDED(hrc))
             {
@@ -616,8 +756,8 @@
     for (size_t i = 0; i < RT_ELEMENTS(g_aCreateFileURIs); ++i)
     {
-        uint32_t const fPathStyle = g_aCreateFileURIs[i].uFormat == URI_FILE_FORMAT_WIN
-                                  ? RTPATH_STR_F_STYLE_DOS : RTPATH_STR_F_STYLE_UNIX;
+        uint32_t const     fPathStyle = g_aCreateFileURIs[i].fPathPathStyle;
+        const char * const pszPath    = g_aCreateFileURIs[i].pszPath;
         char *pszUri = (char *)&pszUri;
-        int rc = RTUriFileCreateEx(g_aCreateFileURIs[i].pszPath, fPathStyle, &pszUri, 0, NULL);
+        int rc = RTUriFileCreateEx(pszPath, fPathStyle, &pszUri, 0, NULL);
         if (RT_SUCCESS(rc))
         {
@@ -629,13 +769,12 @@
                 }
                 else
-                    RTTestIFailed("#%u: '%s'/%#x => '%s', expected '%s'", i,
-                                  g_aCreateFileURIs[i].pszPath, fPathStyle,
-                                  pszUri, g_aCreateFileURIs[i].pszCreatedUri);
+                    RTTestIFailed("#%u: '%s'/%#x => '%s', expected '%s'",
+                                  i, pszPath, fPathStyle, pszUri, g_aCreateFileURIs[i].pszCreatedUri);
             }
             else
                 RTTestIFailed("#%u: bad testcase; pszCreatedUri is NULL\n", i);
         }
-        else if (rc != VERR_INVALID_POINTER || g_aCreateFileURIs[i].pszPath != NULL)
-            RTTestIFailed("#%u: '%s'/%#x => %Rrc", i, g_aCreateFileURIs[i].pszPath, fPathStyle, rc);
+        else if (rc != VERR_INVALID_POINTER || pszPath != NULL)
+            RTTestIFailed("#%u: '%s'/%#x => %Rrc", i, pszPath, fPathStyle, rc);
     }
 }
@@ -702,5 +841,5 @@
     /* File Uri path */
     RTTestISub("RTUriFilePath");
-    for (size_t i = 0; i < RT_ELEMENTS(g_aCreateFileURIs) - 3; ++i) /** @todo r=bird: Fixed last three tests in RTUriFilePath */
+    for (size_t i = 0; i < RT_ELEMENTS(g_aCreateFileURIs); ++i)
         CHECK_STR_API(RTUriFilePath(g_aCreateFileURIs[i].pszUri, g_aCreateFileURIs[i].uFormat),
                       g_aCreateFileURIs[i].pszCreatedPath);
