shlwapi: UrlEscape

Huw D M Davies h.davies1 at physics.ox.ac.uk
Thu Sep 30 09:38:37 CDT 2004


        Huw Davies <huw at codeweavers.com>
        UrlEscape has different rules depending on the protocol
        Added a load of tests
-- 
Huw Davies
huw at codeweavers.com
Index: dlls/shlwapi/url.c
===================================================================
RCS file: /home/wine/wine/dlls/shlwapi/url.c,v
retrieving revision 1.39
diff -u -r1.39 url.c
--- dlls/shlwapi/url.c	29 Sep 2004 21:12:04 -0000	1.39
+++ dlls/shlwapi/url.c	30 Sep 2004 14:25:43 -0000
@@ -30,6 +30,7 @@
 #include "wine/unicode.h"
 #include "wininet.h"
 #include "winreg.h"
+#include "winternl.h"
 #define NO_SHLWAPI_STREAM
 #include "shlwapi.h"
 #include "wine/debug.h"
@@ -118,96 +119,6 @@
  0x25, 0x45, 0x27, 0x75, 0x92, 0xB8, 0xA3, 0xC8, 0xDE, 0xEB, 0xF8, 0xF3, 0xDB,
  0x0A, 0x98, 0x83, 0x7B, 0xE5, 0xCB, 0x4C, 0x78, 0xD1 };
 
-static inline BOOL URL_NeedEscapeA(CHAR ch, DWORD dwFlags)
-{
-
-    if (isalnum(ch))
-        return FALSE;
-
-    if(dwFlags & URL_ESCAPE_SPACES_ONLY) {
-        if(ch == ' ')
-	    return TRUE;
-	else
-	    return FALSE;
-    }
-
-    if ((dwFlags & URL_ESCAPE_PERCENT) && (ch == '%'))
-	return TRUE;
-
-    if (ch <= 31 || ch >= 127)
-	return TRUE;
-
-    else {
-        switch (ch) {
-	case ' ':
-	case '<':
-	case '>':
-	case '\"':
-	case '{':
-	case '}':
-	case '|':
-/*	case '\\': */
-	case '^':
-	case ']':
-	case '[':
-	case '`':
-	case '&':
-	    return TRUE;
-
-	case '/':
-	case '?':
-	    if (dwFlags & URL_ESCAPE_SEGMENT_ONLY) return TRUE;
-	default:
-	    return FALSE;
-	}
-    }
-}
-
-static inline BOOL URL_NeedEscapeW(WCHAR ch, DWORD dwFlags)
-{
-
-    if (isalnumW(ch))
-        return FALSE;
-
-    if(dwFlags & URL_ESCAPE_SPACES_ONLY) {
-        if(ch == L' ')
-	    return TRUE;
-	else
-	    return FALSE;
-    }
-
-    if ((dwFlags & URL_ESCAPE_PERCENT) && (ch == L'%'))
-	return TRUE;
-
-    if (ch <= 31 || ch >= 127)
-	return TRUE;
-
-    else {
-        switch (ch) {
-	case L' ':
-	case L'<':
-	case L'>':
-	case L'\"':
-	case L'{':
-	case L'}':
-	case L'|':
-	case L'\\':
-	case L'^':
-	case L']':
-	case L'[':
-	case L'`':
-	case L'&':
-	    return TRUE;
-
-	case L'/':
-	case L'?':
-	    if (dwFlags & URL_ESCAPE_SEGMENT_ONLY) return TRUE;
-	default:
-	    return FALSE;
-	}
-    }
-}
-
 static BOOL URL_JustLocation(LPCWSTR str)
 {
     while(*str && (*str == L'/')) str++;
@@ -857,6 +768,107 @@
 
 /*************************************************************************
  *      UrlEscapeA	[SHLWAPI.@]
+ */
+
+HRESULT WINAPI UrlEscapeA(
+	LPCSTR pszUrl,
+	LPSTR pszEscaped,
+	LPDWORD pcchEscaped,
+	DWORD dwFlags)
+{
+    WCHAR bufW[INTERNET_MAX_URL_LENGTH];
+    WCHAR *escapedW = bufW;
+    UNICODE_STRING urlW;
+    HRESULT ret;
+    DWORD lenW = sizeof(bufW)/sizeof(WCHAR), lenA;
+
+    if(!RtlCreateUnicodeStringFromAsciiz(&urlW, pszUrl))
+        return E_INVALIDARG;
+    if((ret = UrlEscapeW(urlW.Buffer, escapedW, &lenW, dwFlags)) == E_POINTER) {
+        escapedW = HeapAlloc(GetProcessHeap(), 0, lenW * sizeof(WCHAR));
+        ret = UrlEscapeW(urlW.Buffer, escapedW, &lenW, dwFlags);
+    }
+    if(ret == S_OK) {
+        RtlUnicodeToMultiByteSize(&lenA, escapedW, lenW * sizeof(WCHAR));
+        if(*pcchEscaped > lenA) {
+            RtlUnicodeToMultiByteN(pszEscaped, *pcchEscaped - 1, &lenA, escapedW, lenW * sizeof(WCHAR));
+            pszEscaped[lenA] = 0;
+            *pcchEscaped = lenA;
+        } else {
+            *pcchEscaped = lenA + 1;
+            ret = E_POINTER;
+        }
+    }
+    if(escapedW != bufW) HeapFree(GetProcessHeap(), 0, escapedW);
+    RtlFreeUnicodeString(&urlW);
+    return ret;
+}
+
+#define WINE_URL_BASH_AS_SLASH    0x01
+#define WINE_URL_COLLAPSE_SLASHES 0x02
+#define WINE_URL_ESCAPE_SLASH     0x04
+#define WINE_URL_ESCAPE_HASH      0x08
+#define WINE_URL_ESCAPE_QUESTION  0x10
+#define WINE_URL_STOP_ON_HASH     0x20
+#define WINE_URL_STOP_ON_QUESTION 0x40
+
+static inline BOOL URL_NeedEscapeW(WCHAR ch, DWORD dwFlags, DWORD int_flags)
+{
+
+    if (isalnumW(ch))
+        return FALSE;
+
+    if(dwFlags & URL_ESCAPE_SPACES_ONLY) {
+        if(ch == ' ')
+	    return TRUE;
+	else
+	    return FALSE;
+    }
+
+    if ((dwFlags & URL_ESCAPE_PERCENT) && (ch == '%'))
+	return TRUE;
+
+    if (ch <= 31 || ch >= 127)
+	return TRUE;
+
+    else {
+        switch (ch) {
+	case ' ':
+	case '<':
+	case '>':
+	case '\"':
+	case '{':
+	case '}':
+	case '|':
+	case '\\':
+	case '^':
+	case ']':
+	case '[':
+	case '`':
+	case '&':
+	    return TRUE;
+
+	case '/':
+            if (int_flags & WINE_URL_ESCAPE_SLASH) return TRUE;
+            return FALSE;
+
+	case '?':
+	    if (int_flags & WINE_URL_ESCAPE_QUESTION) return TRUE;
+            return FALSE;
+
+        case '#':
+            if (int_flags & WINE_URL_ESCAPE_HASH) return TRUE;
+            return FALSE;
+
+	default:
+	    return FALSE;
+	}
+    }
+}
+
+
+/*************************************************************************
+ *      UrlEscapeW	[SHLWAPI.@]
  *
  * Converts unsafe characters in a Url into escape sequences.
  *
@@ -871,12 +883,12 @@
  *           contains its length.
  *  Failure: E_POINTER, if pszEscaped is not large enough. In this case
  *           pcchEscaped is set to the required length.
-
+ *
  * Converts unsafe characters into their escape sequences.
  *
  * NOTES
  * - By default this function stops converting at the first '?' or
- *  '#' character (MSDN does not document this).
+ *  '#' character.
  * - If dwFlags contains URL_ESCAPE_SPACES_ONLY then only spaces are
  *   converted, but the conversion continues past a '?' or '#'.
  * - Note that this function did not work well (or at all) in shlwapi version 4.
@@ -888,82 +900,6 @@
  *|     URL_ESCAPE_SEGMENT_ONLY
  *|     URL_ESCAPE_PERCENT
  */
-HRESULT WINAPI UrlEscapeA(
-	LPCSTR pszUrl,
-	LPSTR pszEscaped,
-	LPDWORD pcchEscaped,
-	DWORD dwFlags)
-{
-    LPCSTR src;
-    DWORD needed = 0, ret;
-    BOOL stop_escaping = FALSE;
-    char next[3], *dst = pszEscaped;
-    INT len;
-
-    TRACE("(%s %p %lx 0x%08lx)\n", debugstr_a(pszUrl), pszEscaped,
-	  pcchEscaped?*pcchEscaped:0, dwFlags);
-
-    if(!pszUrl || !pszEscaped || !pcchEscaped)
-	return E_INVALIDARG;
-
-    if(dwFlags & ~(URL_ESCAPE_SPACES_ONLY |
-		   URL_ESCAPE_SEGMENT_ONLY |
-		   URL_DONT_ESCAPE_EXTRA_INFO |
-		   URL_ESCAPE_PERCENT))
-        FIXME("Unimplemented flags: %08lx\n", dwFlags);
-
-    /* fix up flags */
-    if (dwFlags & URL_ESCAPE_SPACES_ONLY)
-	/* if SPACES_ONLY specified, reset the other controls */
-	dwFlags &= ~(URL_DONT_ESCAPE_EXTRA_INFO |
-		     URL_ESCAPE_PERCENT |
-		     URL_ESCAPE_SEGMENT_ONLY);
-
-    else
-	/* if SPACES_ONLY *not* specified then assume DONT_ESCAPE_EXTRA_INFO */
-	dwFlags |= URL_DONT_ESCAPE_EXTRA_INFO;
-
-    for(src = pszUrl; *src; src++) {
-        if(!(dwFlags & URL_ESCAPE_SEGMENT_ONLY) &&
-	   (dwFlags & URL_DONT_ESCAPE_EXTRA_INFO) &&
-	   (*src == '#' || *src == '?'))
-	    stop_escaping = TRUE;
-
-	if(URL_NeedEscapeA(*src, dwFlags) && stop_escaping == FALSE) {
-	    /* TRACE("escaping %c\n", *src); */
-	    next[0] = '%';
-	    next[1] = hexDigits[(*src >> 4) & 0xf];
-	    next[2] = hexDigits[*src & 0xf];
-	    len = 3;
-	} else {
-	    /* TRACE("passing %c\n", *src); */
-	    next[0] = *src;
-	    len = 1;
-	}
-
-	if(needed + len <= *pcchEscaped) {
-	    memcpy(dst, next, len);
-	    dst += len;
-	}
-	needed += len;
-    }
-
-    if(needed < *pcchEscaped) {
-        *dst = '\0';
-	ret = S_OK;
-    } else {
-        needed++; /* add one for the '\0' */
-	ret = E_POINTER;
-    }
-    *pcchEscaped = needed;
-    return ret;
-}
-
-/*************************************************************************
- *      UrlEscapeW	[SHLWAPI.@]
- *
- * See UrlEscapeA.
- */
 HRESULT WINAPI UrlEscapeW(
 	LPCWSTR pszUrl,
 	LPWSTR pszEscaped,
@@ -975,6 +911,10 @@
     BOOL stop_escaping = FALSE;
     WCHAR next[5], *dst = pszEscaped;
     INT len;
+    PARSEDURLW parsed_url;
+    DWORD int_flags;
+    DWORD slashes = 0;
+    static const WCHAR localhost[] = {'l','o','c','a','l','h','o','s','t',0};
 
     TRACE("(%s %p %p 0x%08lx)\n", debugstr_w(pszUrl), pszEscaped,
 	  pcchEscaped, dwFlags);
@@ -999,39 +939,101 @@
 	/* if SPACES_ONLY *not* specified the assume DONT_ESCAPE_EXTRA_INFO */
 	dwFlags |= URL_DONT_ESCAPE_EXTRA_INFO;
 
-    for(src = pszUrl; *src; src++) {
-	/*
-	 * if(!(dwFlags & URL_ESCAPE_SPACES_ONLY) &&
-	 *   (*src == L'#' || *src == L'?'))
-	 *    stop_escaping = TRUE;
-	 */
-        if(!(dwFlags & URL_ESCAPE_SEGMENT_ONLY) &&
-	   (dwFlags & URL_DONT_ESCAPE_EXTRA_INFO) &&
-	   (*src == L'#' || *src == L'?'))
-	    stop_escaping = TRUE;
-
-	if(URL_NeedEscapeW(*src, dwFlags) && stop_escaping == FALSE) {
-	    /* TRACE("escaping %c\n", *src); */
-	    next[0] = L'%';
-	    /*
-	     * I would have assumed that the W form would escape
-	     * the character with 4 hex digits (or even 8),
-	     * however, experiments show that native shlwapi escapes
-	     * with only 2 hex digits.
-	     *   next[1] = hexDigits[(*src >> 12) & 0xf];
-	     *   next[2] = hexDigits[(*src >> 8) & 0xf];
-	     *   next[3] = hexDigits[(*src >> 4) & 0xf];
-	     *   next[4] = hexDigits[*src & 0xf];
-	     *   len = 5;
-	     */
-	    next[1] = hexDigits[(*src >> 4) & 0xf];
-	    next[2] = hexDigits[*src & 0xf];
-	    len = 3;
-	} else {
-	    /* TRACE("passing %c\n", *src); */
-	    next[0] = *src;
-	    len = 1;
-	}
+
+    int_flags = 0;
+    if(dwFlags & URL_ESCAPE_SEGMENT_ONLY) {
+        int_flags = WINE_URL_ESCAPE_QUESTION | WINE_URL_ESCAPE_HASH | WINE_URL_ESCAPE_SLASH;
+    } else {
+        parsed_url.cbSize = sizeof(parsed_url);
+        if(ParseURLW(pszUrl, &parsed_url) != S_OK)
+            parsed_url.nScheme = URL_SCHEME_INVALID;
+
+        TRACE("scheme = %d (%s)\n", parsed_url.nScheme, debugstr_wn(parsed_url.pszProtocol, parsed_url.cchProtocol));
+
+        if(dwFlags & URL_DONT_ESCAPE_EXTRA_INFO)
+            int_flags = WINE_URL_STOP_ON_HASH | WINE_URL_STOP_ON_QUESTION;
+
+        switch(parsed_url.nScheme) {
+        case URL_SCHEME_FILE:
+            int_flags |= WINE_URL_BASH_AS_SLASH | WINE_URL_COLLAPSE_SLASHES | WINE_URL_ESCAPE_HASH;
+            int_flags &= ~WINE_URL_STOP_ON_HASH;
+            break;
+
+        case URL_SCHEME_HTTP:
+        case URL_SCHEME_HTTPS:
+            int_flags |= WINE_URL_BASH_AS_SLASH;
+            if(parsed_url.pszSuffix[0] != '/' && parsed_url.pszSuffix[0] != '\\')
+                int_flags |= WINE_URL_ESCAPE_SLASH;
+            break;
+
+        case URL_SCHEME_MAILTO:
+            int_flags |= WINE_URL_ESCAPE_SLASH | WINE_URL_ESCAPE_QUESTION | WINE_URL_ESCAPE_HASH;
+            int_flags &= ~(WINE_URL_STOP_ON_QUESTION | WINE_URL_STOP_ON_HASH);
+            break;
+
+        case URL_SCHEME_INVALID:
+            break;
+
+        case URL_SCHEME_FTP:
+        default:
+            if(parsed_url.pszSuffix[0] != '/')
+                int_flags |= WINE_URL_ESCAPE_SLASH;
+            break;
+        }
+    }
+
+    for(src = pszUrl; *src; ) {
+        WCHAR cur = *src;
+        len = 0;
+        
+        if((int_flags & WINE_URL_COLLAPSE_SLASHES) && src == pszUrl + parsed_url.cchProtocol + 1) {
+            int localhost_len = sizeof(localhost)/sizeof(WCHAR) - 1;
+            while(cur == '/' || cur == '\\') {
+                slashes++;
+                cur = *++src;
+            }
+            if(slashes == 2 && !strncmpiW(src, localhost, localhost_len)) { /* file://localhost/ -> file:/// */
+                if(*(src + localhost_len) == '/' || *(src + localhost_len) == '\\')
+                src += localhost_len + 1;
+                slashes = 3;
+            }
+
+            switch(slashes) {
+            case 1:
+            case 3:
+                next[0] = next[1] = next[2] = '/';
+                len = 3;
+                break;
+            case 0:
+                len = 0;
+                break;
+            default:
+                next[0] = next[1] = '/';
+                len = 2;
+                break;
+            }
+        }
+        if(len == 0) {
+
+            if(cur == '#' && (int_flags & WINE_URL_STOP_ON_HASH))
+                stop_escaping = TRUE;
+
+            if(cur == '?' && (int_flags & WINE_URL_STOP_ON_QUESTION))
+                stop_escaping = TRUE;
+
+            if(cur == '\\' && (int_flags & WINE_URL_BASH_AS_SLASH) && !stop_escaping) cur = '/';
+
+            if(URL_NeedEscapeW(cur, dwFlags, int_flags) && stop_escaping == FALSE) {
+                next[0] = L'%';
+                next[1] = hexDigits[(cur >> 4) & 0xf];
+                next[2] = hexDigits[cur & 0xf];
+                len = 3;
+            } else {
+                next[0] = cur;
+                len = 1;
+            }
+            src++;
+        }
 
 	if(needed + len <= *pcchEscaped) {
 	    memcpy(dst, next, len*sizeof(WCHAR));
@@ -1041,7 +1043,7 @@
     }
 
     if(needed < *pcchEscaped) {
-        *dst = L'\0';
+        *dst = '\0';
 	ret = S_OK;
     } else {
         needed++; /* add one for the '\0' */
Index: dlls/shlwapi/tests/path.c
===================================================================
RCS file: /home/wine/wine/dlls/shlwapi/tests/path.c,v
retrieving revision 1.11
diff -u -r1.11 path.c
--- dlls/shlwapi/tests/path.c	25 Sep 2004 00:29:47 -0000	1.11
+++ dlls/shlwapi/tests/path.c	30 Sep 2004 14:25:43 -0000
@@ -76,6 +76,77 @@
     {"http://www.winehq.org/tests4\r", URL_ESCAPE_SPACES_ONLY, 0, S_OK, "http://www.winehq.org/tests4\r"},
     {"http://www.winehq.org/tests5\r", URL_WININET_COMPATIBILITY|URL_ESCAPE_SPACES_ONLY, 0, S_OK, "http://www.winehq.org/tests5\r"},
     {"/direct/swhelp/series6/6.2i_latestservicepack.dat\r", URL_ESCAPE_SPACES_ONLY, 0, S_OK, "/direct/swhelp/series6/6.2i_latestservicepack.dat\r"},
+
+    {"file://////foo/bar\\baz", 0, 0, S_OK, "file://foo/bar/baz"},
+    {"file://///foo/bar\\baz", 0, 0, S_OK, "file://foo/bar/baz"},
+    {"file:////foo/bar\\baz", 0, 0, S_OK, "file://foo/bar/baz"},
+    {"file:///localhost/foo/bar\\baz", 0, 0, S_OK, "file:///localhost/foo/bar/baz"},
+    {"file:///foo/bar\\baz", 0, 0, S_OK, "file:///foo/bar/baz"},
+    {"file://loCalHost/foo/bar\\baz", 0, 0, S_OK, "file:///foo/bar/baz"},
+    {"file://foo/bar\\baz", 0, 0, S_OK, "file://foo/bar/baz"},
+    {"file:/localhost/foo/bar\\baz", 0, 0, S_OK, "file:///localhost/foo/bar/baz"},
+    {"file:/foo/bar\\baz", 0, 0, S_OK, "file:///foo/bar/baz"},
+    {"file:foo/bar\\baz", 0, 0, S_OK, "file:foo/bar/baz"},
+    {"file:\\foo/bar\\baz", 0, 0, S_OK, "file:///foo/bar/baz"},
+    {"file:\\\\foo/bar\\baz", 0, 0, S_OK, "file://foo/bar/baz"},
+    {"file:\\\\\\foo/bar\\baz", 0, 0, S_OK, "file:///foo/bar/baz"},
+    {"file:\\\\localhost\\foo/bar\\baz", 0, 0, S_OK, "file:///foo/bar/baz"},
+    {"file:///f oo/b?a r\\baz", 0, 0, S_OK, "file:///f%20oo/b?a r\\baz"},
+    {"file:///foo/b#a r\\baz", 0, 0, S_OK, "file:///foo/b%23a%20r/baz"},
+    {"file:///f o^&`{}|][\"<>\\%o/b#a r\\baz", 0, 0, S_OK, "file:///f%20o%5E%26%60%7B%7D%7C%5D%5B%22%3C%3E/%o/b%23a%20r/baz"},
+    {"file:///f o%o/b?a r\\b%az", URL_ESCAPE_PERCENT, 0, S_OK, "file:///f%20o%25o/b?a r\\b%az"},
+    {"file:/foo/bar\\baz", URL_ESCAPE_SEGMENT_ONLY, 0, S_OK, "file:%2Ffoo%2Fbar%5Cbaz"},
+
+    {"foo/b%ar\\ba?z\\", URL_ESCAPE_SEGMENT_ONLY, 0, S_OK, "foo%2Fb%ar%5Cba%3Fz%5C"},
+    {"foo/b%ar\\ba?z\\", URL_ESCAPE_PERCENT | URL_ESCAPE_SEGMENT_ONLY, 0, S_OK, "foo%2Fb%25ar%5Cba%3Fz%5C"},
+    {"foo/bar\\ba?z\\", 0, 0, S_OK, "foo/bar%5Cba?z\\"},
+    {"/foo/bar\\ba?z\\", 0, 0, S_OK, "/foo/bar%5Cba?z\\"},
+    {"/foo/bar\\ba#z\\", 0, 0, S_OK, "/foo/bar%5Cba#z\\"},
+    {"/foo/%5C", 0, 0, S_OK, "/foo/%5C"},
+    {"/foo/%5C", URL_ESCAPE_PERCENT, 0, S_OK, "/foo/%255C"},
+
+    {"http://////foo/bar\\baz", 0, 0, S_OK, "http://////foo/bar/baz"},
+    {"http://///foo/bar\\baz", 0, 0, S_OK, "http://///foo/bar/baz"},
+    {"http:////foo/bar\\baz", 0, 0, S_OK, "http:////foo/bar/baz"},
+    {"http:///foo/bar\\baz", 0, 0, S_OK, "http:///foo/bar/baz"},
+    {"http://localhost/foo/bar\\baz", 0, 0, S_OK, "http://localhost/foo/bar/baz"},
+    {"http://foo/bar\\baz", 0, 0, S_OK, "http://foo/bar/baz"},
+    {"http:/foo/bar\\baz", 0, 0, S_OK, "http:/foo/bar/baz"},
+    {"http:foo/bar\\ba?z\\", 0, 0, S_OK, "http:foo%2Fbar%2Fba?z\\"},
+    {"http:foo/bar\\ba#z\\", 0, 0, S_OK, "http:foo%2Fbar%2Fba#z\\"},
+    {"http:\\foo/bar\\baz", 0, 0, S_OK, "http:/foo/bar/baz"},
+    {"http:\\\\foo/bar\\baz", 0, 0, S_OK, "http://foo/bar/baz"},
+    {"http:\\\\\\foo/bar\\baz", 0, 0, S_OK, "http:///foo/bar/baz"},
+    {"http:\\\\\\\\foo/bar\\baz", 0, 0, S_OK, "http:////foo/bar/baz"},
+    {"http:/fo ?o/b ar\\baz", 0, 0, S_OK, "http:/fo%20?o/b ar\\baz"},
+    {"http:fo ?o/b ar\\baz", 0, 0, S_OK, "http:fo%20?o/b ar\\baz"},
+    {"http:/foo/bar\\baz", URL_ESCAPE_SEGMENT_ONLY, 0, S_OK, "http:%2Ffoo%2Fbar%5Cbaz"},
+
+    {"https://foo/bar\\baz", 0, 0, S_OK, "https://foo/bar/baz"},
+    {"https:/foo/bar\\baz", 0, 0, S_OK, "https:/foo/bar/baz"},
+    {"https:\\foo/bar\\baz", 0, 0, S_OK, "https:/foo/bar/baz"},
+
+    {"foo:////foo/bar\\baz", 0, 0, S_OK, "foo:////foo/bar%5Cbaz"},
+    {"foo:///foo/bar\\baz", 0, 0, S_OK, "foo:///foo/bar%5Cbaz"},
+    {"foo://localhost/foo/bar\\baz", 0, 0, S_OK, "foo://localhost/foo/bar%5Cbaz"},
+    {"foo://foo/bar\\baz", 0, 0, S_OK, "foo://foo/bar%5Cbaz"},
+    {"foo:/foo/bar\\baz", 0, 0, S_OK, "foo:/foo/bar%5Cbaz"},
+    {"foo:foo/bar\\baz", 0, 0, S_OK, "foo:foo%2Fbar%5Cbaz"},
+    {"foo:\\foo/bar\\baz", 0, 0, S_OK, "foo:%5Cfoo%2Fbar%5Cbaz"},
+    {"foo:/foo/bar\\ba?\\z", 0, 0, S_OK, "foo:/foo/bar%5Cba?\\z"},
+    {"foo:/foo/bar\\ba#\\z", 0, 0, S_OK, "foo:/foo/bar%5Cba#\\z"},
+
+    {"mailto:/fo/o at b\\%a?\\r.b#\\az", 0, 0, S_OK, "mailto:%2Ffo%2Fo at b%5C%a%3F%5Cr.b%23%5Caz"},
+    {"mailto:fo/o at b\\%a?\\r.b#\\az", 0, 0, S_OK, "mailto:fo%2Fo at b%5C%a%3F%5Cr.b%23%5Caz"},
+    {"mailto:fo/o at b\\%a?\\r.b#\\az", URL_ESCAPE_PERCENT, 0, S_OK, "mailto:fo%2Fo at b%5C%25a%3F%5Cr.b%23%5Caz"},
+
+    {"ftp:fo/o at bar.baz/foo/bar", 0, 0, S_OK, "ftp:fo%2Fo at bar.baz%2Ffoo%2Fbar"},
+    {"ftp:/fo/o at bar.baz/foo/bar", 0, 0, S_OK, "ftp:/fo/o at bar.baz/foo/bar"},
+    {"ftp://fo/o@bar.baz/fo?o\\bar", 0, 0, S_OK, "ftp://fo/o@bar.baz/fo?o\\bar"},
+    {"ftp://fo/o@bar.baz/fo#o\\bar", 0, 0, S_OK, "ftp://fo/o@bar.baz/fo#o\\bar"},
+    {"ftp://localhost/o@bar.baz/fo#o\\bar", 0, 0, S_OK, "ftp://localhost/o@bar.baz/fo#o\\bar"},
+    {"ftp:///fo/o@bar.baz/foo/bar", 0, 0, S_OK, "ftp:///fo/o@bar.baz/foo/bar"},
+    {"ftp:////fo/o@bar.baz/foo/bar", 0, 0, S_OK, "ftp:////fo/o@bar.baz/foo/bar"}
 };
 
 typedef struct _TEST_URL_COMBINE {
@@ -178,10 +261,22 @@
 {
     CHAR szReturnUrl[INTERNET_MAX_URL_LENGTH];
     DWORD dwEscaped;
-
+    WCHAR ret_urlW[INTERNET_MAX_URL_LENGTH];
+    WCHAR *urlW, *expected_urlW;
     dwEscaped=INTERNET_MAX_URL_LENGTH;
-    ok(UrlEscapeA(szUrl, szReturnUrl, &dwEscaped, dwFlags) == dwExpectReturn, "UrlEscapeA didn't return 0x%08lx\n", dwExpectReturn);
-    ok(strcmp(szReturnUrl,szExpectUrl)==0, "Expected \"%s\", but got \"%s\"\n", szExpectUrl, szReturnUrl);
+
+    ok(UrlEscapeA(szUrl, szReturnUrl, &dwEscaped, dwFlags) == dwExpectReturn, "UrlEscapeA didn't return 0x%08lx from \"%s\"\n", dwExpectReturn, szUrl);
+    ok(strcmp(szReturnUrl,szExpectUrl)==0, "Expected \"%s\", but got \"%s\" from \"%s\"\n", szExpectUrl, szReturnUrl, szUrl);
+    
+    dwEscaped = INTERNET_MAX_URL_LENGTH;
+    urlW = GetWideString(szUrl);
+    expected_urlW = GetWideString(szExpectUrl);
+    ok(UrlEscapeW(urlW, ret_urlW, &dwEscaped, dwFlags) == dwExpectReturn, "UrlEscapeW didn't return 0x%08lx from \"%s\"\n", dwExpectReturn, szUrl);
+    WideCharToMultiByte(CP_ACP,0,ret_urlW,-1,szReturnUrl,INTERNET_MAX_URL_LENGTH,0,0);
+    ok(strcmpW(ret_urlW, expected_urlW)==0, "Expected \"%s\", but got \"%s\" from \"%s\" flags %08lx\n", szExpectUrl, szReturnUrl, szUrl, dwFlags);
+    FreeWideString(urlW);
+    FreeWideString(expected_urlW);
+
 }
 static void test_url_canonicalize(const char *szUrl, DWORD dwFlags, HRESULT dwExpectReturn, const char *szExpectUrl)
 {



More information about the wine-patches mailing list