From: Zebediah Figura zfigura@codeweavers.com
--- dlls/shlwapi/tests/url.c | 1165 +++++++++++++++++++++++++++++++++++--- 1 file changed, 1083 insertions(+), 82 deletions(-)
diff --git a/dlls/shlwapi/tests/url.c b/dlls/shlwapi/tests/url.c index 53cc6d4a066..e51a080c330 100644 --- a/dlls/shlwapi/tests/url.c +++ b/dlls/shlwapi/tests/url.c @@ -950,53 +950,861 @@ static void test_UrlEscapeW(void) } }
-/* ########################### */ +struct canonicalize_test +{ + const char *url; + DWORD flags; + const char *expect; +};
static void test_UrlCanonicalizeA(void) { - unsigned int i; CHAR szReturnUrl[4*INTERNET_MAX_URL_LENGTH]; CHAR longurl[4*INTERNET_MAX_URL_LENGTH]; + char url[200], expect[200]; + unsigned int f, i, j; DWORD dwSize; DWORD urllen; HRESULT hr;
+ static const struct canonicalize_test unk_scheme_tests[] = + { + /* Single and double dots behave as one would expect, with the following + * notable rules: + * + * (1) A single or double dot as the first element (the "hostname") is + * always emitted as-is. + * + * (2) If a double dot would undo the hostname, it is emitted as-is + * instead. + * + * (3) If a single or double dot is the last element (either because of + * the above rule or because of URL_DONT_SIMPLIFY), a trailing + * backslash is appended. + * + * A trailing backslash is always appended after the hostname. + */ + + {"//", 0, "///"}, + {"//a", 0, "//a/"}, + {"//a/", 0, "//a/"}, + {"//a/b", 0, "//a/b"}, + {"//a/b/", 0, "//a/b/"}, + {"//.", 0, "//./"}, + {"//./", 0, "//./"}, + {"//./a", 0, "//./a"}, + {"//././a", 0, "//./a"}, + {"//a/.", 0, "//a/"}, + {"//a/./", 0, "//a/"}, + {"//a/./b", 0, "//a/b"}, + {"///./a", 0, "///a"}, + {"//a/.b/", 0, "//a/.b/"}, + {"//a/b./", 0, "//a/b./"}, + + {"//..", 0, "//../"}, + {"//../", 0, "//../"}, + {"//../a", 0, "//../a"}, + {"//../a/..", 0, "//../"}, + {"//.././..", 0, "//../../"}, + {"//../a/../..", 0, "//../../"}, + {"//./a/../..", 0, "//./../"}, + {"//a/..", 0, "//a/../"}, + {"//a/../../b/./c/..", 0, "//a/../../b/"}, + {"//a/b/..", 0, "//a/"}, + {"//a/b/...", 0, "//a/b/..."}, + {"//a/b/../", 0, "//a/"}, + {"//a/b/../c", 0, "//a/c"}, + {"//a/b/../c/..", 0, "//a/"}, + {"//a/b/../c/../..", 0, "//a/../"}, + {"//a/b/../../../c", 0, "//a/../../c"}, + {"///..", 0, "///../"}, + {"////..", 0, "///"}, + {"//a/..b/", 0, "//a/..b/"}, + {"//a/b../", 0, "//a/b../"}, + {"//A/B", 0, "//A/B"}, + + {"//././a", URL_DONT_SIMPLIFY, "//././a"}, + {"//a/.", URL_DONT_SIMPLIFY, "//a/./"}, + {"//a/./", URL_DONT_SIMPLIFY, "//a/./"}, + {"//a/./b", URL_DONT_SIMPLIFY, "//a/./b"}, + {"///./a", URL_DONT_SIMPLIFY, "///./a"}, + + {"//..", URL_DONT_SIMPLIFY, "//../"}, + {"//../", URL_DONT_SIMPLIFY, "//../"}, + {"//../a", URL_DONT_SIMPLIFY, "//../a"}, + {"//../a/..", URL_DONT_SIMPLIFY, "//../a/../"}, + {"//../a/...", URL_DONT_SIMPLIFY, "//../a/..."}, + {"//.././..", URL_DONT_SIMPLIFY, "//.././../"}, + {"//../a/../..", URL_DONT_SIMPLIFY, "//../a/../../"}, + {"//./a/../..", URL_DONT_SIMPLIFY, "//./a/../../"}, + {"//a/..", URL_DONT_SIMPLIFY, "//a/../"}, + {"//a/../../b/./c/..", URL_DONT_SIMPLIFY, "//a/../../b/./c/../"}, + {"//a/b/..", URL_DONT_SIMPLIFY, "//a/b/../"}, + {"//a/b/../", URL_DONT_SIMPLIFY, "//a/b/../"}, + {"//a/b/../c", URL_DONT_SIMPLIFY, "//a/b/../c"}, + {"//a/b/../c/..", URL_DONT_SIMPLIFY, "//a/b/../c/../"}, + {"//a/b/../c/../..", URL_DONT_SIMPLIFY, "//a/b/../c/../../"}, + {"///..", URL_DONT_SIMPLIFY, "///../"}, + {"////..", URL_DONT_SIMPLIFY, "////../"}, + + /* After ? or #, dots are not simplified. */ + {"//a/b?c/./d", 0, "//a/b?c/./d"}, + {"//a/b#c/./d", 0, "//a/b#c/./d"}, + {"//a/b#c/.", 0, "//a/b#c/."}, + /* ? and # can also be considered a boundary for trailing dots. */ + {"//a/b/.?", 0, "//a/b/?"}, + {"//a/b/..?", 0, "//a/?"}, + {"//a/b/..?", URL_DONT_SIMPLIFY, "//a/b/../?"}, + {"//a/b/.#", 0, "//a/b/#"}, + {"//a/b/..#", 0, "//a/#"}, + {"//a/b/..#", URL_DONT_SIMPLIFY, "//a/b/../#"}, + {"//a/..?", 0, "//a/../?"}, + {"//a/..#", 0, "//a/../#"}, + {"//..?", 0, "//../?"}, + {"//..#", 0, "//../#"}, + {"//?/a/./", 0, "///?/a/./"}, + {"//#/a/./", 0, "///#/a/./"}, + /* The first ? is reordered before the first #. */ + {"//a/b#c?d", 0, "//a/b?d#c"}, + {"//a/b?c#d?e", 0, "//a/b?c#d?e"}, + {"//a/b#c?d#e", 0, "//a/b?d#e#c"}, + {"//a/b#c#d?e", 0, "//a/b?e#c#d"}, + {"//a/b#c?d?e", 0, "//a/b?d?e#c"}, + + /* Backslashes are not treated as path separators. */ + {"//a/b\c/../.\", 0, "//a/.\"}, + {"//a\b/../", 0, "//a\b/../"}, + {"//a/b\../", 0, "//a/b\../"}, + {"//a/b/..\", 0, "//a/b/..\"}, + + /* Whitespace and unsafe characters are not (by default) escaped. */ + {"//a/b &c", 0, "//a/b &c"}, + + /* If one slash is omitted, the rules are much the same, except that + * there is no "hostname". Single dots are always collapsed; double dots + * are collapsed unless they would undo the "scheme". */ + + {"/a", 0, "/a"}, + {"/a/", 0, "/a/"}, + {"/.", 0, "/"}, + {"/./", 0, "/"}, + {"/././a", 0, "/a"}, + {"/a/.", 0, "/a/"}, + {"/a/./", 0, "/a/"}, + {"/a/./b", 0, "/a/b"}, + + {"/..", 0, "/../"}, + {"/../", 0, "/../"}, + {"/../a", 0, "/../a"}, + {"/../a/..", 0, "/../"}, + {"/a/..", 0, "/"}, + {"/a/../..", 0, "/../"}, + {"/a/b/..", 0, "/a/"}, + {"/a/b/../", 0, "/a/"}, + {"/a/b/../c", 0, "/a/c"}, + {"/a/b/../c/..", 0, "/a/"}, + {"/a/b/../c/../..", 0, "/"}, + + {"/a/b?c/./d", 0, "/a/b?c/./d"}, + {"/a/b#c/./d", 0, "/a/b#c/./d"}, + {"/a/b#c?d", 0, "/a/b?d#c"}, + + /* Just as above, backslashes are not treated as path separators. */ + {"/a/b\c/../.\", 0, "/a/.\"}, + {"/a/b\/c", 0, "/a/b\/c"}, + {"/a/b\.c", 0, "/a/b\.c"}, + /* If the first character after the slash is a backslash, it is skipped. + * It is not interpreted as a forward slash. + * The tests above show that this is not due to the backslash being + * interpreted as an escape character. */ + {"/\././a", 0, "/a"}, + /* The sequence // does not result in use of the double-slash rules. + * Rather, the resulting // is treated as an empty path element. */ + {"/\/././a", 0, "//a"}, + {"/\/././a/", 0, "//a/"}, + {"/\/..", 0, "/"}, + {"//a/\b", 0, "//a/\b"}, + + {"/a/b &c", 0, "/a/b &c"}, + + {"//a/b%20%26c", URL_UNESCAPE, "//a/b &c"}, + }; + static const struct { const char *url; DWORD flags; const char *expect; + const char *expect_ftp; } - tests[] = + http_tests[] = + { + /* A set of schemes including http differs from the "default" behaviour + * in the following ways: + * + * (1) If a double dot would undo the hostname, it is dropped instead. + * + * (2) If the first element after the hostname is a single or double + * dot, no further dots are simplified. + * + * (3) Trailing backslashes are not automatically appended after dots. + */ + + {"//", 0, "///"}, + {"//a", 0, "//a/"}, + {"//a/", 0, "//a/"}, + {"//a/b", 0, "//a/b"}, + {"//a/b/", 0, "//a/b/"}, + {"//.", 0, "//./"}, + {"//./", 0, "//./"}, + {"//././a/.", 0, "//././a/."}, + {"//a/.", 0, "//a/."}, + {"//a/./b/./../", 0, "//a/./b/./../"}, + {"//a/b/.", 0, "//a/b/"}, + {"//a/b/.", URL_DONT_SIMPLIFY, "//a/b/."}, + {"//a/b/./", 0, "//a/b/"}, + {"//a/b/./c", 0, "//a/b/c"}, + {"///./a", 0, "///./a"}, + {"////./a", 0, "////a"}, + + {"//..", 0, "//../"}, + {"//../", 0, "//../"}, + {"//../a", 0, "//../a"}, + {"//../a/..", 0, "//../"}, + {"//../a/../..", 0, "//../"}, + {"//./a/../..", 0, "//./"}, + {"//a/../", 0, "//a/../"}, + {"//a/../../b/./../", 0, "//a/../../b/./../"}, + {"//a/.././", 0, "//a/.././"}, + {"//a/b/..", 0, "//a/"}, + {"//a/b/..", URL_DONT_SIMPLIFY, "//a/b/.."}, + {"//a/b/../", 0, "//a/"}, + {"//a/b/.././", 0, "//a/"}, + {"//a/b/../c", 0, "//a/c"}, + {"//a/b/../c/..", 0, "//a/"}, + {"//a/b/../c/../..", 0, "//a/"}, + {"//a/b/../../../c", 0, "//a/c"}, + {"///a/.", 0, "///a/"}, + {"///..", 0, "///.."}, + {"////..", 0, "///"}, + {"//a//../../..", 0, "//a/"}, + + {"//a/b?c/./d", 0, "//a/b?c/./d"}, + {"//a/b#c/./d", 0, "//a/b#c/./d"}, + {"//a/b#c?d", 0, "//a/b#c?d"}, + {"//a/b?c#d", 0, "//a/b?c#d"}, + + {"//localhost/b", 0, "//localhost/b"}, + + /* Most of these schemes translates backslashes to forward slashes, + * including the initial pair, and interpret them appropriately. + * + * A few schemes, including ftp, don't translate backslashes to forward + * slashes, but still interpret them as path separators, with the + * exception that the hostname must end in a forward slash. */ + + {"//a/b\", 0, "//a/b/", "//a/b\"}, + {"//a/b\./c", 0, "//a/b/c", "//a/b\c"}, + {"//a/b/.\c", 0, "//a/b/c"}, + {"//a/b\c/../.\", 0, "//a/b/", "//a/b\"}, + {"//a\b", 0, "//a/b", "//a\b/"}, + {"//a\b/..", 0, "//a/", "//a\b/.."}, + {"//a/b\c", 0, "//a/b/c", "//a/b\c"}, + {"/\a\..", 0, "//a/..", "/\a\../"}, + {"\/a\..", 0, "//a/..", "\/a\../"}, + + {"//a/b &c", 0, "//a/b &c"}, + + /* If one or both slashes is missing, the portion after the colon is + * treated like a normal path, without a hostname. Single and double + * dots are always collapsed, and double dots which would rewind past + * the scheme are dropped instead. */ + + {"a", 0, "a"}, + {"a/", 0, "a/"}, + {"a/.", 0, "a/"}, + {"a/..", 0, ""}, + {"a/../..", 0, ""}, + {"a/../..", URL_DONT_SIMPLIFY, "a/../.."}, + {"", 0, ""}, + {"/", 0, "/"}, + {"/.", 0, "/"}, + {"/..", 0, ""}, + {"/../..", 0, ""}, + {".", 0, ""}, + {"..", 0, ""}, + {"./", 0, ""}, + {"../", 0, ""}, + + {"a/b?c/.\d", 0, "a/b?c/.\d"}, + {"a/b#c/.\d", 0, "a/b#c/.\d"}, + + {"a\b\", 0, "a/b/", "a\b\"}, + + {"a/b &c", 0, "a/b &c"}, + + {"/foo/bar/baz", URL_ESCAPE_SEGMENT_ONLY, "/foo/bar/baz"}, + {"/foo/bar/baz?a#b", URL_ESCAPE_SEGMENT_ONLY, "/foo/bar/baz?a#b"}, + + {"//www.winehq.org/tests\n", URL_ESCAPE_SPACES_ONLY | URL_ESCAPE_UNSAFE, "//www.winehq.org/tests"}, + {"//www.winehq.org/tests\r", URL_ESCAPE_SPACES_ONLY | URL_ESCAPE_UNSAFE, "//www.winehq.org/tests"}, + {"//www.winehq.org/tests/foo bar", URL_ESCAPE_SPACES_ONLY | URL_DONT_ESCAPE_EXTRA_INFO, "//www.winehq.org/tests/foo%20bar"}, + {"//www.winehq.org/tests/foo%20bar", 0, "//www.winehq.org/tests/foo%20bar"}, + {"//www.winehq.org/tests/foo%20bar", URL_UNESCAPE, "//www.winehq.org/tests/foo bar"}, + {"//www.winehq.org/%E6%A1%9C.html", 0, "//www.winehq.org/%E6%A1%9C.html"}, + }; + + static const struct canonicalize_test opaque_tests[] = { + /* Opaque protocols, predictably, do not modify the portion after the + * scheme. */ + {"//a/b/./c/../d\e", 0, "//a/b/./c/../d\e"}, + {"/a/b/./c/../d\e", 0, "/a/b/./c/../d\e"}, + {"a/b/./c/../d\e", 0, "a/b/./c/../d\e"}, {"", 0, ""}, - {"http://www.winehq.org/tests/../tests/../..", 0, "http://www.winehq.org/%22%7D, - {"http://www.winehq.org/..", 0, "http://www.winehq.org/..%22%7D, - {"http://www.winehq.org/tests/tests2/../../tests", 0, "http://www.winehq.org/tests%22%7D, - {"http://www.winehq.org/tests/../tests", 0, "http://www.winehq.org/tests%22%7D, - {"http://www.winehq.org/tests%5Cn", URL_WININET_COMPATIBILITY|URL_ESCAPE_SPACES_ONLY|URL_ESCAPE_UNSAFE, "http://www.winehq.org/tests%22%7D, - {"http://www.winehq.org/tests%5Cr", URL_WININET_COMPATIBILITY|URL_ESCAPE_SPACES_ONLY|URL_ESCAPE_UNSAFE, "http://www.winehq.org/tests%22%7D, - {"http://www.winehq.org/tests%5Cr", 0, "http://www.winehq.org/tests%22%7D, - {"http://www.winehq.org/tests%5Cr", URL_DONT_SIMPLIFY, "http://www.winehq.org/tests%22%7D, - {"http://www.winehq.org/tests/../tests/", 0, "http://www.winehq.org/tests/%22%7D, - {"http://www.winehq.org/tests/../tests/..", 0, "http://www.winehq.org/%22%7D, - {"http://www.winehq.org/tests/../tests/../", 0, "http://www.winehq.org/%22%7D, - {"http://www.winehq.org/tests/..", 0, "http://www.winehq.org/%22%7D, - {"http://www.winehq.org/tests/../", 0, "http://www.winehq.org/%22%7D, - {"http://www.winehq.org/tests/..?query=x&return=y", 0, "http://www.winehq.org/?query=x&return=y%22%7D, - {"http://www.winehq.org/tests/../?query=x&return=y", 0, "http://www.winehq.org/?query=x&return=y%22%7D, - {"\tht\ttp\t://www\t.w\tineh\t\tq.or\tg\t/\ttests/..\t?\tquer\ty=x\t\t&re\tturn=y\t\t", 0, "http://www.winehq.org/?query=x&return=y%22%7D, - {"http://www.winehq.org/tests/..#example", 0, "http://www.winehq.org/#example%22%7D, - {"http://www.winehq.org/tests/../#example", 0, "http://www.winehq.org/#example%22%7D, - {"http://www.winehq.org/tests%5C%5C../#example", 0, "http://www.winehq.org/#example%22%7D, - {"http://www.winehq.org/tests/..%5C%5C#example", 0, "http://www.winehq.org/#example%22%7D, - {"http://www.winehq.org%5C%5Ctests/../#example", 0, "http://www.winehq.org/#example%22%7D, - {"http://www.winehq.org/tests/../#example", URL_DONT_SIMPLIFY, "http://www.winehq.org/tests/../#example%22%7D, - {"http://www.winehq.org/tests/foo bar", URL_ESCAPE_SPACES_ONLY | URL_DONT_ESCAPE_EXTRA_INFO, "http://www.winehq.org/tests/foo%20bar%22%7D, - {"http://www.winehq.org/tests/foo%20bar", URL_UNESCAPE, "http://www.winehq.org/tests/foo bar"}, - {"http://www.winehq.org", 0, "http://www.winehq.org/%22%7D, - {"http:///www.winehq.org", 0, "http:///www.winehq.org%22%7D, - {"http:////www.winehq.org", 0, "http:////www.winehq.org%22%7D, + {"//a/b &c", 0, "//a/b &c"}, + {"//a/b%20%26c", URL_UNESCAPE, "//a/b &c"}, + }; + + static const struct canonicalize_test file_tests[] = + { + /* file:// is almost identical to http://, except that a URL beginning + * with file://// (four or more slashes) is stripped down to two + * slashes. The first non-empty element is interpreted as a hostname; + * and the rest follows the usual rules. + * + * The intent here is probably to detect UNC paths, although it's + * unclear why an arbitrary number of slashes are skipped in that case. + */ + + {"file://", 0, "file:///"}, + {"file://a", 0, "file://a/"}, + {"file://a/", 0, "file://a/"}, + {"file://a//", 0, "file://a//"}, + {"file://a/b", 0, "file://a/b"}, + {"file://a/b/", 0, "file://a/b/"}, + {"file://.", 0, "file://./"}, + {"file://./", 0, "file://./"}, + {"file://././a/.", 0, "file://././a/."}, + {"file://a/.", 0, "file://a/."}, + {"file://a/./b/./../", 0, "file://a/./b/./../"}, + {"file://a/b/.", 0, "file://a/b/"}, + {"file://a/b/.", URL_DONT_SIMPLIFY, "file://a/b/."}, + {"file://a/b/./", 0, "file://a/b/"}, + {"file://a/b/./c", 0, "file://a/b/c"}, + {"file:///./a", 0, "file:///./a"}, + {"file:////./a", 0, "file://./a"}, + + {"file://..", 0, "file://../"}, + {"file://../", 0, "file://../"}, + {"file://../a", 0, "file://../a"}, + {"file://../a/..", 0, "file://../"}, + {"file://../a/../..", 0, "file://../"}, + {"file://./a/../..", 0, "file://./"}, + {"file://a/../", 0, "file://a/../"}, + {"file://a/../../b/./../", 0, "file://a/../../b/./../"}, + {"file://a/.././", 0, "file://a/.././"}, + {"file://a/b/..", 0, "file://a/"}, + {"file://a/b/../", 0, "file://a/"}, + {"file://a/b/.././", 0, "file://a/"}, + {"file://a/b/../c", 0, "file://a/c"}, + {"file://a/b/../c/..", 0, "file://a/"}, + {"file://a/b/../c/../..", 0, "file://a/"}, + {"file://a/b/../../../c", 0, "file://a/c"}, + {"file:///.", 0, "file:///."}, + {"file:///..", 0, "file:///.."}, + {"file:///a/.", 0, "file:///a/"}, + + {"file:////", 0, "file:///"}, + {"file:////a/./b/../c", 0, "file://a/./b/../c"}, + {"file://///a/./b/../c", 0, "file://a/./b/../c"}, + {"file://////a/./b/../c", 0, "file://a/./b/../c"}, + {"file:////a/b/./../c", 0, "file://a/c"}, + {"file:////a/b/./../..", 0, "file://a/"}, + {"file://///a/b/./../c", 0, "file://a/c"}, + {"file://////a/b/./../c", 0, "file://a/c"}, + {"file:////.", 0, "file://./"}, + {"file:////..", 0, "file://../"}, + {"file:////./b/./../c", 0, "file://./c"}, + {"file:////./b/./../..", 0, "file://./"}, + {"file://///./b/./../c", 0, "file://./c"}, + {"file://////./b/./../c", 0, "file://./c"}, + + /* Drive-like paths get an extra slash (i.e. an empty hostname, to + * signal that the host is the local machine). The drive letter is + * treated as the path root. */ + {"file://a:", 0, "file:///a:"}, + {"file://a:/b", 0, "file:///a:/b"}, + {"file://a:/b/../..", 0, "file:///a:/"}, + {"file://a:/./../..", 0, "file:///a:/./../.."}, + {"file://a|/b", 0, "file:///a|/b"}, + {"file://ab:/c", 0, "file://ab:/c"}, + {"file:///a:", 0, "file:///a:"}, + {"file:////a:", 0, "file:///a:"}, + {"file://///a:", 0, "file:///a:"}, + {"file://host/a:/b/../..", 0, "file://host/a:/"}, + + /* URL_FILE_USE_PATHURL (and URL_WININET_COMPATIBILITY) have their own + * set of rules: + * + * (1) Dot processing works exactly like the "unknown scheme" rules, + * instead of the file/http rules demonstrated above. + * + * (2) Some number of backslashes is appended after the two forward + * slashes. The number basically corresponds to the detected path + * type (two for a remote path, one for a local path, none for a + * local drive path). A local path is one where the hostname is + * empty or "localhost". If all path elements are empty then no + * backslashes are appended. + */ + + {"file://", URL_FILE_USE_PATHURL, "file://"}, + {"file://a", URL_FILE_USE_PATHURL, "file://\\a"}, + {"file://a/", URL_FILE_USE_PATHURL, "file://\\a"}, + {"file://a//", URL_FILE_USE_PATHURL, "file://\\a\\"}, + {"file://a/b", URL_FILE_USE_PATHURL, "file://\\a\b"}, + {"file://a//b", URL_FILE_USE_PATHURL, "file://\\a\\b"}, + {"file://a/.", URL_FILE_USE_PATHURL, "file://\\a\"}, + {"file://a/../../b/./c/..", URL_FILE_USE_PATHURL, "file://\\a\..\..\b\"}, + {"file://./../../b/./c/..", URL_FILE_USE_PATHURL, "file://\\.\..\..\b\"}, + {"file://../../../b/./c/..", URL_FILE_USE_PATHURL, "file://\\..\..\..\b\"}, + {"file://a/b/.", URL_FILE_USE_PATHURL, "file://\\a\b\"}, + {"file://a/b/.", URL_FILE_USE_PATHURL | URL_DONT_SIMPLIFY, "file://\\a\b\.\"}, + {"file://a/b/../../../c", URL_FILE_USE_PATHURL, "file://\\a\..\..\c"}, + + {"file:///", URL_FILE_USE_PATHURL, "file://"}, + {"file:///.", URL_FILE_USE_PATHURL, "file://\"}, + {"file:///..", URL_FILE_USE_PATHURL, "file://\..\"}, + {"file:///../../b/./c/..", URL_FILE_USE_PATHURL, "file://\..\..\b\"}, + {"file:///a/b/./c/..", URL_FILE_USE_PATHURL, "file://\a\b\"}, + + {"file:////", URL_FILE_USE_PATHURL, "file://"}, + {"file:////.", URL_FILE_USE_PATHURL, "file://\\."}, + {"file:////a/./b/../c", URL_FILE_USE_PATHURL, "file://\\a\c"}, + {"file://///", URL_FILE_USE_PATHURL, "file://"}, + {"file://///a/./b/../c", URL_FILE_USE_PATHURL, "file://\\a\c"}, + + {"file://a:", URL_FILE_USE_PATHURL, "file://a:"}, + {"file://a:/", URL_FILE_USE_PATHURL, "file://a:\"}, + {"file://a:/b", URL_FILE_USE_PATHURL, "file://a:\b"}, + {"file://a:/b/../..", URL_FILE_USE_PATHURL, "file://a:\..\"}, + {"file://a|/b", URL_FILE_USE_PATHURL, "file://a|\b"}, + {"file:///a:", URL_FILE_USE_PATHURL, "file://a:"}, + {"file:////a:", URL_FILE_USE_PATHURL, "file://a:"}, + + /* URL_WININET_COMPATIBILITY is almost identical, but ensures a trailing + * backslash in two cases: + * + * (1) if all path elements are empty, + * + * (2) if the path consists of just the hostname. + */ + + {"file://", URL_WININET_COMPATIBILITY, "file://\"}, + {"file://a", URL_WININET_COMPATIBILITY, "file://\\a\"}, + {"file://a/", URL_WININET_COMPATIBILITY, "file://\\a\"}, + {"file://a//", URL_WININET_COMPATIBILITY, "file://\\a\\"}, + {"file://a/b", URL_WININET_COMPATIBILITY, "file://\\a\b"}, + {"file://a//b", URL_WININET_COMPATIBILITY, "file://\\a\\b"}, + {"file://a/.", URL_WININET_COMPATIBILITY, "file://\\a\"}, + {"file://a/../../b/./c/..", URL_WININET_COMPATIBILITY, "file://\\a\..\..\b\"}, + {"file://./../../b/./c/..", URL_WININET_COMPATIBILITY, "file://\\.\..\..\b\"}, + {"file://../../../b/./c/..", URL_WININET_COMPATIBILITY, "file://\\..\..\..\b\"}, + {"file://a/b/../../../c", URL_WININET_COMPATIBILITY, "file://\\a\..\..\c"}, + + {"file:///", URL_WININET_COMPATIBILITY, "file://\"}, + {"file:///.", URL_WININET_COMPATIBILITY, "file://\"}, + {"file:///..", URL_WININET_COMPATIBILITY, "file://\..\"}, + {"file:///../../b/./c/..", URL_WININET_COMPATIBILITY, "file://\..\..\b\"}, + {"file:///a/b/./c/..", URL_WININET_COMPATIBILITY, "file://\a\b\"}, + + {"file:////", URL_WININET_COMPATIBILITY, "file://\"}, + {"file:////.", URL_WININET_COMPATIBILITY, "file://\\.\"}, + {"file:////a/./b/../c", URL_WININET_COMPATIBILITY, "file://\\a\c"}, + {"file://///", URL_WININET_COMPATIBILITY, "file://\"}, + {"file://///a/./b/../c", URL_WININET_COMPATIBILITY, "file://\\a\c"}, + + {"file://a:", URL_WININET_COMPATIBILITY, "file://a:"}, + + {"file://", URL_FILE_USE_PATHURL | URL_WININET_COMPATIBILITY, "file://"}, + + {"file://localhost/a", 0, "file://localhost/a"}, + {"file://localhost//a", 0, "file://localhost//a"}, + {"file://localhost/a:", 0, "file://localhost/a:"}, + {"file://localhost/a:/b/../..", 0, "file://localhost/a:/"}, + {"file://localhost/a:/./../..", 0, "file://localhost/a:/./../.."}, + {"file://localhost", URL_FILE_USE_PATHURL, "file://"}, + {"file://localhost/", URL_FILE_USE_PATHURL, "file://"}, + {"file://localhost/b", URL_FILE_USE_PATHURL, "file://\b"}, + {"file://127.0.0.1/b", URL_FILE_USE_PATHURL, "file://\\127.0.0.1\b"}, + {"file://localhost//b", URL_FILE_USE_PATHURL, "file://\b"}, + {"file://localhost///b", URL_FILE_USE_PATHURL, "file://\\b"}, + {"file:///localhost/b", URL_FILE_USE_PATHURL, "file://\localhost\b"}, + {"file:////localhost/b", URL_FILE_USE_PATHURL, "file://\b"}, + {"file://///localhost/b", URL_FILE_USE_PATHURL, "file://\b"}, + {"file://localhost/a:", URL_FILE_USE_PATHURL, "file://a:"}, + {"file://localhost/a:/b/../..", URL_FILE_USE_PATHURL, "file://a:\..\"}, + {"file://localhost?a/b", URL_FILE_USE_PATHURL, "file://"}, + {"file://localhost#a/b", URL_FILE_USE_PATHURL, "file://\\localhost#a/b"}, + {"file://localhostq", URL_FILE_USE_PATHURL, "file://\\localhostq"}, + + {"file://localhost", URL_WININET_COMPATIBILITY, "file://\"}, + {"file://localhost/", URL_WININET_COMPATIBILITY, "file://\"}, + {"file://localhost/b", URL_WININET_COMPATIBILITY, "file://\b"}, + {"file://localhost//b", URL_WININET_COMPATIBILITY, "file://\b"}, + {"file://127.0.0.1/b", URL_WININET_COMPATIBILITY, "file://\\127.0.0.1\b"}, + {"file://localhost/a:", URL_WININET_COMPATIBILITY, "file://a:"}, + {"file://localhost?a/b", URL_WININET_COMPATIBILITY, "file://\?a/b"}, + {"file://localhost#a/b", URL_WININET_COMPATIBILITY, "file://\\localhost#a/b"}, + + /* # has some weird behaviour: + * + * - Dot processing happens normally after it, including rewinding past + * the #. It's not treated as a path separator for the purposes of + * rewinding. + * + * - However, if neither file flag is used, and the first character + * after the hostname (plus an optional slash) is a hash, no dot + * processing takes place. + * + * - If the previous path segment ends in .htm or .html, the rest of + * the URL is emitted verbatim (no dot or slash canonicalization). + * This does not apply to the hostname. If URL_FILE_USE_PATHURL is + * used, though, the rest of the URL including the # is omitted. + * + * - It is treated as a path terminator for dots, but only if neither + * file flag is used. It does not begin a path element. + * + * - If there is a # anywhere in the output string (and the string + * doesn't fall under the .html exception), all subsequent slashes + * are converted to forward slashes instead of backslashes. + * This means that rewinding past the hash will revert to backslashes. + * (This of course only affects the case where file flags are used; + * if no file flags are used then slashes are converted to forward + * slashes anyway.) + */ + {"file://a/b#c/../d\e", 0, "file://a/d/e"}, + {"file://a/b#c/./d\e", 0, "file://a/b#c/d/e"}, + {"file://a/b.htm#c/../d\e", 0, "file://a/b.htm#c/../d\e"}, + {"file://a/b.html#c/../d\e", 0, "file://a/b.html#c/../d\e"}, + {"file://a/b.hTmL#c/../d\e", 0, "file://a/b.hTmL#c/../d\e"}, + {"file://a/b.xhtml#c/../d\e", 0, "file://a/d/e"}, + {"file://a/b.php#c/../d\e", 0, "file://a/d/e"}, + {"file://a/b.asp#c/../d\e", 0, "file://a/d/e"}, + {"file://a/b.aspx#c/../d\e", 0, "file://a/d/e"}, + {"file://a/b.ht#c/../d\e", 0, "file://a/d/e"}, + {"file://a/b.txt#c/../d\e", 0, "file://a/d/e"}, + {"file://a/b.htmlq#c/../d\e", 0, "file://a/d/e"}, + {"file://a/b.html/q#c/../d\e", 0, "file://a/b.html/d/e"}, + {"file://a/.html#c/../d\e", 0, "file://a/.html#c/../d\e"}, + {"file://a/html#c/../d\e", 0, "file://a/d/e"}, + {"file://a/b#c/./d.html#e/../f", 0, "file://a/b#c/d.html#e/../f"}, + {"file://a.html#/b/../c", 0, "file://a.html#/c"}, + {"file://a/b#c/../d/e", URL_FILE_USE_PATHURL, "file://\\a\d\e"}, + {"file://a/b#c/./d/e", URL_FILE_USE_PATHURL, "file://\\a\b#c/d/e"}, + {"file://a/b.html#c/../d\e", URL_FILE_USE_PATHURL, "file://\\a\b.html"}, + {"file://a/b.html#c/../d\e", URL_FILE_USE_PATHURL | URL_WININET_COMPATIBILITY, "file://\\a\b.html"}, + {"file://a/b#c/../d/e", URL_WININET_COMPATIBILITY, "file://\\a\d\e"}, + {"file://a/b#c/./d/e", URL_WININET_COMPATIBILITY, "file://\\a\b#c/d/e"}, + {"file://a/b.html#c/../d\e", URL_WININET_COMPATIBILITY, "file://\\a\b.html#c/../d\e"}, + {"file://a/c#/../d", 0, "file://a/d"}, + {"file://a/c#/../d", URL_FILE_USE_PATHURL, "file://\\a\d"}, + {"file://a/c#/../d", URL_WININET_COMPATIBILITY, "file://\\a\d"}, + {"file://a/#c/../d\e", 0, "file://a/#c/../d\e"}, + {"file://a/#c/../d/e", URL_FILE_USE_PATHURL, "file://\\a\d\e"}, + {"file://a/#c/../d/e", URL_WININET_COMPATIBILITY, "file://\\a\d\e"}, + {"file://a//#c/../d", 0, "file://a//#c/../d"}, + {"file://a//#c/../d", URL_FILE_USE_PATHURL, "file://\\a\\d"}, + {"file://a//#c/../d", URL_WININET_COMPATIBILITY, "file://\\a\\d"}, + {"file://a/\#c/../d", 0, "file://a//#c/../d"}, + {"file://a///#c/../d", 0, "file://a///d"}, + {"file://a/b/#c/../d", 0, "file://a/b/d"}, + {"file://a/b/.#c", 0, "file://a/b/#c"}, + {"file://a/b/..#c", 0, "file://a/#c"}, + {"file://a/b/.#c", URL_FILE_USE_PATHURL, "file://\\a\b\.#c"}, + {"file://a/b/..#c", URL_FILE_USE_PATHURL, "file://\\a\b\..#c"}, + {"file://a/b/.#c", URL_WININET_COMPATIBILITY, "file://\\a\b\.#c"}, + {"file://a/b/..#c", URL_WININET_COMPATIBILITY, "file://\\a\b\..#c"}, + {"file://a/b#../c", 0, "file://a/b#../c"}, + {"file://a/b/#../c", 0, "file://a/b/#../c"}, + {"file://a/b#../c", URL_FILE_USE_PATHURL, "file://\\a\b#../c"}, + {"file://a/b#../c", URL_WININET_COMPATIBILITY, "file://\\a\b#../c"}, + {"file://#/b\./", 0, "file://#/b/"}, + {"file://#/./b\./", 0, "file://#/./b/./"}, + {"file://#/b\./", URL_FILE_USE_PATHURL, "file://\\#/b/"}, + {"file://#/b\./", URL_WININET_COMPATIBILITY, "file://\\#/b/"}, + {"file://a#/b\./", 0, "file://a#/b/"}, + {"file://a#/./b\./", 0, "file://a#/./b/./"}, + {"file://a#/b\./", URL_FILE_USE_PATHURL, "file://\\a#/b/"}, + {"file://a#/b\./", URL_WININET_COMPATIBILITY, "file://\\a#/b/"}, + {"file://a#/b\./", URL_FILE_USE_PATHURL | URL_DONT_SIMPLIFY, "file://\\a#/b/./"}, + {"file://a#/b\.", URL_FILE_USE_PATHURL | URL_DONT_SIMPLIFY, "file://\\a#/b/./"}, + {"file://a#/b/../../", 0, "file://a#/"}, + + /* ? is similar, with the following exceptions: + * + * - URLs ending in .htm(l) are not treated specially. + * + * - With URL_FILE_USE_PATHURL, the rest of the URL including the ? is + * just omitted (much like the .html case above). + * + * - With URL_WININET_COMPATIBILITY, the rest of the URL is always + * emitted verbatim (completely opaque, like other schemes). + */ + + {"file://a/b?c/../d\e", 0, "file://a/d/e"}, + {"file://a/b.html?c/../d\e", 0, "file://a/d/e"}, + {"file://a/b?c/../d\e", URL_FILE_USE_PATHURL, "file://\\a\b"}, + {"file://a/b.html?c/../d\e", URL_FILE_USE_PATHURL, "file://\\a\b.html"}, + {"file://a/b?c/../d\e", URL_WININET_COMPATIBILITY, "file://\\a\b?c/../d\e"}, + {"file://a/b.html?c/../d\e", URL_WININET_COMPATIBILITY, "file://\\a\b.html?c/../d\e"}, + {"file://a/b?c/../d", URL_FILE_USE_PATHURL | URL_WININET_COMPATIBILITY, "file://\\a\b"}, + {"file://a/?c/../d", 0, "file://a/?c/../d"}, + {"file://a/?c/../d", URL_FILE_USE_PATHURL, "file://\\a"}, + {"file://a/?c/../d", URL_WININET_COMPATIBILITY, "file://\\a\?c/../d"}, + {"file://a//?c/../d", 0, "file://a//?c/../d"}, + {"file://a//?c/../d", URL_FILE_USE_PATHURL, "file://\\a\\"}, + {"file://a//?c/../d", URL_WININET_COMPATIBILITY, "file://\\a\\?c/../d"}, + {"file://a/\?c/../d", 0, "file://a//?c/../d"}, + {"file://a///?c/../d", 0, "file://a///d"}, + {"file://a/b/?c/../d", 0, "file://a/b/d"}, + {"file://a/b/.?c", 0, "file://a/b/?c"}, + {"file://a/b/..?c", 0, "file://a/?c"}, + {"file://a/b/.?c", URL_FILE_USE_PATHURL, "file://\\a\b\"}, + {"file://a/b/..?c", URL_FILE_USE_PATHURL, "file://\\a\"}, + {"file://a/b/.?c", URL_WININET_COMPATIBILITY, "file://\\a\b\?c"}, + {"file://a/b/..?c", URL_WININET_COMPATIBILITY, "file://\\a\?c"}, + {"file://?/a\./", 0, "file://?/a/"}, + {"file://?/./a\./", 0, "file://?/./a/./"}, + {"file://?/a\./", URL_FILE_USE_PATHURL, "file://"}, + {"file://?/a\./", URL_WININET_COMPATIBILITY, "file://\?/a\./"}, + {"file://a?/a\./", 0, "file://a?/a/"}, + {"file://a?/./a\./", 0, "file://a?/./a/./"}, + {"file://a?/a\./", URL_FILE_USE_PATHURL, "file://\\a"}, + {"file://a?/a\./", URL_WININET_COMPATIBILITY, "file://\\a\?/a\./"}, + + {"file://a/b.html?c#d/..", 0, "file://a/"}, + {"file://a/b.html?c.html#d/..", 0, "file://a/b.html?c.html#d/.."}, + {"file://a/b?\#c\d", 0, "file://a/b?/#c/d"}, + {"file://a/b?\#c\d", URL_WININET_COMPATIBILITY, "file://\\a\b?\#c\d"}, + {"file://a/b?\#c\d", URL_FILE_USE_PATHURL, "file://\\a\b"}, + {"file://a/b#\?c\d", 0, "file://a/b#/?c/d"}, + {"file://a/b#\?c\d", URL_WININET_COMPATIBILITY, "file://\\a\b#/?c\d"}, + {"file://a/b#\?c\d", URL_FILE_USE_PATHURL, "file://\\a\b#/"}, + {"file://a/b.html#c?d", URL_WININET_COMPATIBILITY, "file://\\a\b.html?d#c"}, + + /* file: treats backslashes like forward slashes, including the + * initial pair. */ + {"file://a/b\", 0, "file://a/b/"}, + {"file://a/b\c/../.\", 0, "file://a/b/"}, + {"file://a\b", 0, "file://a/b"}, + {"file:/\a\..", 0, "file://a/.."}, + {"file:\/a\..", 0, "file://a/.."}, + {"file:\\a\b", URL_FILE_USE_PATHURL, "file://\\a\b"}, + {"file:\\a\b", URL_WININET_COMPATIBILITY, "file://\\a\b"}, + {"file:\///a/./b/../c", 0, "file://a/./b/../c"}, + {"file:/\//a/./b/../c", 0, "file://a/./b/../c"}, + {"file://\/a/./b/../c", 0, "file://a/./b/../c"}, + {"file:///\a/./b/../c", 0, "file://a/./b/../c"}, + + {"file://a/b &c", 0, "file://a/b &c"}, + {"file://a/b &c", URL_FILE_USE_PATHURL, "file://\\a\b &c"}, + {"file://a/b &c", URL_WININET_COMPATIBILITY, "file://\\a\b &c"}, + {"file://a/b !"$%&'()*+,-:;<=>@[]^_`{|}~c", URL_ESCAPE_UNSAFE, "file://a/b%20!%22$%%26'()*+,-:;%3C=%3E@%5B%5D%5E_%60%7B%7C%7D~c"}, + {"file://a/b%20%26c", 0, "file://a/b%20%26c"}, + {"file://a/b%20%26c", URL_FILE_USE_PATHURL, "file://\\a\b &c"}, + {"file://a/b%20%26c", URL_WININET_COMPATIBILITY, "file://\\a\b%20%26c"}, + + /* Omitting one slash behaves as if the URL had been written with an + * empty hostname, and the output adds two slashes as such. */ + + {"file:/", 0, "file:///"}, + {"file:/a", 0, "file:///a"}, + {"file:/./a", 0, "file:///./a"}, + {"file:/../a/..", 0, "file:///../a/.."}, + {"file:/./..", 0, "file:///./.."}, + {"file:/a/.", 0, "file:///a/"}, + {"file:/a/../..", 0, "file:///"}, + {"file:/a:", 0, "file:///a:"}, + {"file:/a:/b/../..", 0, "file:///a:/"}, + + /* The same applies to the flags. */ + + {"file:/", URL_FILE_USE_PATHURL, "file://"}, + {"file:/a", URL_FILE_USE_PATHURL, "file://\a"}, + {"file:/.", URL_FILE_USE_PATHURL, "file://\"}, + {"file:/./a", URL_FILE_USE_PATHURL, "file://\a"}, + {"file:/../a", URL_FILE_USE_PATHURL, "file://\..\a"}, + {"file:/a/../..", URL_FILE_USE_PATHURL, "file://\..\"}, + {"file:/a/.", URL_FILE_USE_PATHURL | URL_DONT_SIMPLIFY, "file://\a\.\"}, + {"file:/a:", URL_FILE_USE_PATHURL, "file://a:"}, + {"file:/a:/b/../..", URL_FILE_USE_PATHURL, "file://a:\..\"}, + + {"file:/", URL_WININET_COMPATIBILITY, "file://\"}, + {"file:/a", URL_WININET_COMPATIBILITY, "file://\a"}, + {"file:/.", URL_WININET_COMPATIBILITY, "file://\"}, + {"file:/a:", URL_WININET_COMPATIBILITY, "file://a:"}, + + {"file:/a/b#c/../d", 0, "file:///a/d"}, + {"file:/a/b?c/../d", 0, "file:///a/d"}, + + {"file:/a\b\", 0, "file:///a/b/"}, + {"file:\a/b/", 0, "file:///a/b/"}, + {"file:\a\b", URL_FILE_USE_PATHURL, "file://\a\b"}, + {"file:\a\b", URL_WININET_COMPATIBILITY, "file://\a\b"}, + + {"file:/a/b &c", 0, "file:///a/b &c"}, + + /* Omitting both slashes causes all dots to be collapsed, in the same + * way as bare http. */ + + {"file:a", 0, "file:a"}, + {"file:a/", 0, "file:a/"}, + {"file:a/.", 0, "file:a/"}, + {"file:a/..", 0, "file:"}, + {"file:a/../..", 0, "file:"}, + {"file:", 0, "file:"}, + {"file:.", 0, "file:"}, + {"file:..", 0, "file:"}, + {"file:./", 0, "file:"}, + {"file:../", 0, "file:"}, + + {"file:a:", 0, "file:///a:"}, + + /* URL_FILE_USE_PATHURL treats everything here as a local (relative?) + * path. In the case that the path resolves to the current directory + * a single backslash is emitted. */ + {"file:", URL_FILE_USE_PATHURL, "file://"}, + {"file:a", URL_FILE_USE_PATHURL, "file://a"}, + {"file:a/.", URL_FILE_USE_PATHURL, "file://a\"}, + {"file:a/../..", URL_FILE_USE_PATHURL, "file://..\"}, + {"file:./a", URL_FILE_USE_PATHURL, "file://a"}, + {"file:../a", URL_FILE_USE_PATHURL, "file://..\a"}, + {"file:a/.", URL_FILE_USE_PATHURL | URL_DONT_SIMPLIFY, "file://a\.\"}, + {"file:a:", URL_FILE_USE_PATHURL, "file://a:"}, + + /* URL_WININET_COMPATIBILITY doesn't emit a double slash. */ + {"file:", URL_WININET_COMPATIBILITY, "file:"}, + {"file:a", URL_WININET_COMPATIBILITY, "file:a"}, + {"file:./a", URL_WININET_COMPATIBILITY, "file:a"}, + {"file:../a", URL_WININET_COMPATIBILITY, "file:..\a"}, + {"file:../b/./c/../d", URL_WININET_COMPATIBILITY | URL_DONT_SIMPLIFY, "file:..\b\.\c\..\d"}, + {"file:a:", URL_WININET_COMPATIBILITY, "file://a:"}, + + {"file:a/b?c/../d", 0, "file:a/d"}, + {"file:a/b#c/../d", 0, "file:a/d"}, + + {"file:a\b\", 0, "file:a/b/"}, + + {"file:a/b &c", 0, "file:a/b &c"}, + + {"fIlE://A/B", 0, "file://A/B"}, + {"fIlE://A/B", URL_FILE_USE_PATHURL, "file://\\A\B"}, + {"fIlE://A/B", URL_WININET_COMPATIBILITY, "file://\\A\B"}, + {"fIlE:A:/B", 0, "file:///A:/B"}, + {"fIlE:A:/B", URL_FILE_USE_PATHURL, "file://A:\B"}, + {"fIlE:A:/B", URL_WININET_COMPATIBILITY, "file://A:\B"}, + {"fIlE://lOcAlHoSt/B", 0, "file://lOcAlHoSt/B"}, + {"fIlE://lOcAlHoSt/B", URL_FILE_USE_PATHURL, "file://\B"}, + + /* Drive paths are automatically converted to file paths. Dots are + * collapsed unless the first segment after q: or q:/ is a dot. */ + + {"q:a", 0, "file:///q:a"}, + {"q:a/.", 0, "file:///q:a/"}, + {"q:a/..", 0, "file:///q:"}, + {"q:a/../..", 0, "file:///q:"}, + {"q:./a/..", 0, "file:///q:./a/.."}, + {"q:../a/..", 0, "file:///q:../a/.."}, + {"q:/", 0, "file:///q:/"}, + {"q:/a", 0, "file:///q:/a"}, + {"q:/a/.", 0, "file:///q:/a/"}, + {"q:/a/..", 0, "file:///q:/"}, + {"q:/./a/..", 0, "file:///q:/./a/.."}, + {"q:/../a/..", 0, "file:///q:/../a/.."}, + {"q://./a", 0, "file:///q://a"}, + {"q://../a", 0, "file:///q:/a"}, + + /* File flags use the "unknown scheme" rules, and the root of the path + * is the first slash. */ + + {"q:/a", URL_FILE_USE_PATHURL, "file://q:\a"}, + {"q:/a/../..", URL_FILE_USE_PATHURL, "file://q:\..\"}, + {"q:a/../../b/..", URL_FILE_USE_PATHURL, "file://q:a\..\..\"}, + {"q:./../../b/..", URL_FILE_USE_PATHURL, "file://q:.\..\..\"}, + {"q:/a", URL_WININET_COMPATIBILITY, "file://q:\a"}, + {"q:/a/../..", URL_WININET_COMPATIBILITY, "file://q:\..\"}, + {"q:a/../../b/..", URL_WININET_COMPATIBILITY, "file://q:a\..\..\"}, + {"q:./../../b/..", URL_WININET_COMPATIBILITY, "file://q:.\..\..\"}, + + {"q:/a/b?c/../d", 0, "file:///q:/a/d"}, + {"q:/a/b#c/../d", 0, "file:///q:/a/d"}, + {"q:a?b", URL_FILE_USE_PATHURL, "file://q:a"}, + + {"q:a\b\", 0, "file:///q:a/b/"}, + {"q:\a/b", 0, "file:///q:/a/b"}, + + /* Drive paths are also unique in that unsafe characters (and spaces) + * are automatically escaped—but not if the file flags are used. */ + + {"q:/a/b !"$%&'()*+,-:;<=>@[]^_`{|}~c", 0, "file:///q:/a/b%20!%22$%25%26'()*+,-:;%3C=%3E@%5B%5D%5E_%60%7B%7C%7D~c"}, + {"q:/a/b &c", URL_FILE_USE_PATHURL, "file://q:\a\b &c"}, + {"q:/a/b &c", URL_WININET_COMPATIBILITY, "file://q:\a\b &c"}, + + {"q:/a/b%20%26c", 0, "file:///q:/a/b%2520%2526c"}, + {"q:/a/b%20%26c", URL_UNESCAPE, "file:///q:/a/b &c"}, + {"q:/a/b%20%26c", URL_UNESCAPE | URL_ESCAPE_UNSAFE, "file:///q:/a/b%20%26c"}, + {"q:/a/b%20%26c", URL_FILE_USE_PATHURL, "file://q:\a\b &c"}, + {"q:/a/b%20%26c", URL_FILE_USE_PATHURL | URL_UNESCAPE, "file://q:\a\b &c"}, + {"q:/a/b%20%26c", URL_WININET_COMPATIBILITY, "file://q:\a\b%20%26c"}, + {"q:/a/b%20%26c", URL_WININET_COMPATIBILITY | URL_UNESCAPE, "file://q:\a\b &c"}, + + {"q|a", 0, "file:///q%7Ca"}, + {"-:a", 0, "-:a"}, + {"Q:A", 0, "file:///Q:A"}, + + /* A double initial backslash is also converted to a file path. The same + * rules for hostnames apply. */ + + {"\\", 0, "file:///"}, + {"\\a", 0, "file://a/"}, + {"\\../a\b/..\c/.\", 0, "file://../a/c/"}, + {"\\a/./b/../c", 0, "file://a/./b/../c"}, + /* And, of course, four or more slashes gets collapsed... */ + {"\\//./b/./../c", 0, "file://./c"}, + {"\\///./b/./../c", 0, "file://./c"}, + + {"\\a/b?c/../d", 0, "file://a/d"}, + {"\\a/b#c/../d", 0, "file://a/d"}, + + /* Drive paths are "recognized" too, though. The following isn't + * actually a local path, but UrlCanonicalize() doesn't seem to realize + * that. */ + {"\\a:/b", 0, "file:///a:/b"}, + + {"\\", URL_FILE_USE_PATHURL, "file://"}, + {"\\a", URL_FILE_USE_PATHURL, "file://\\a"}, + {"\\a/./..", URL_FILE_USE_PATHURL, "file://\\a\..\"}, + {"\\a:/b", URL_FILE_USE_PATHURL, "file://a:\b"}, + {"\\", URL_WININET_COMPATIBILITY, "file://\"}, + {"\\a", URL_WININET_COMPATIBILITY, "file://\\a\"}, + {"\\a/./..", URL_WININET_COMPATIBILITY, "file://\\a\..\"}, + {"\\a:/b", URL_WININET_COMPATIBILITY, "file://a:\b"}, + + /* And, as with drive paths, unsafe characters are escaped. */ + {"\\a/b !"$%&'()*+,-:;<=>@[]^_`{|}~c", 0, "file://a/b%20!%22$%25%26'()*+,-:;%3C=%3E@%5B%5D%5E_%60%7B%7C%7D~c"}, + {"\\a/b &c", URL_FILE_USE_PATHURL, "file://\\a\b &c"}, + {"\\a/b &c", URL_WININET_COMPATIBILITY, "file://\\a\b &c"}, + + {"\\/b", 0, "file:///b"}, + {"\\/b", URL_FILE_USE_PATHURL, "file://\b"}, + {"\\localhost/b", URL_FILE_USE_PATHURL, "file://\b"}, + {"\\127.0.0.1/b", URL_FILE_USE_PATHURL, "file://\\127.0.0.1\b"}, + {"\\localhost/b", URL_WININET_COMPATIBILITY, "file://\b"}, + {"\\127.0.0.1/b", URL_WININET_COMPATIBILITY, "file://\\127.0.0.1\b"}, + + {"\\A/B", 0, "file://A/B"}, + {"file:///c:/tests/foo%20bar", URL_UNESCAPE, "file:///c:/tests/foo bar"}, {"file:///c:/tests\foo%20bar", URL_UNESCAPE, "file:///c:/tests/foo bar"}, {"file:///c:/tests/foo%20bar", 0, "file:///c:/tests/foo%20bar"}, @@ -1027,67 +1835,188 @@ static void test_UrlCanonicalizeA(void) {"file://C:/user/file", URL_WININET_COMPATIBILITY, "file://C:\user\file"}, {"file:///C:/user/file", URL_WININET_COMPATIBILITY, "file://C:\user\file"}, {"file:////C:/user/file", URL_WININET_COMPATIBILITY, "file://C:\user\file"}, - {"http:///www.winehq.org", 0, "http:///www.winehq.org%22%7D, - {"http:///www.winehq.org", URL_WININET_COMPATIBILITY, "http:///www.winehq.org%22%7D, - {"http://www.winehq.org/site/about", URL_FILE_USE_PATHURL, "http://www.winehq.org/site/about%22%7D, - {"file_://www.winehq.org/site/about", URL_FILE_USE_PATHURL, "file_://www.winehq.org/site/about"}, - {"c:\dir\file", 0, "file:///c:/dir/file"}, - {"file:///c:\dir\file", 0, "file:///c:/dir/file"}, - {"c:dir\file", 0, "file:///c:dir/file"}, - {"c:\tests\foo bar", URL_FILE_USE_PATHURL, "file://c:\tests\foo bar"}, - {"c:\tests\foo bar", 0, "file:///c:/tests/foo%20bar"}, - {"c\t:\t\te\tsts\fo\to \tbar\t", 0, "file:///c:/tests/foo%20bar"}, - {"res://file", 0, "res://file/"}, - {"res://file", URL_FILE_USE_PATHURL, "res://file/"}, - {"res:///c:/tests/foo%20bar", URL_UNESCAPE, "res:///c:/tests/foo bar"}, - {"res:///c:/tests\foo%20bar", URL_UNESCAPE, "res:///c:/tests\foo bar"}, - {"res:///c:/tests/foo%20bar", 0, "res:///c:/tests/foo%20bar"}, - {"res:///c:/tests/foo%20bar", URL_FILE_USE_PATHURL, "res:///c:/tests/foo%20bar"}, - {"res://c:/tests/../tests/foo%20bar", URL_FILE_USE_PATHURL, "res://c:/tests/foo%20bar"}, - {"res://c:/tests\../tests/foo%20bar", URL_FILE_USE_PATHURL, "res://c:/tests/foo%20bar"}, - {"res://c:/tests/foo%20bar", URL_FILE_USE_PATHURL, "res://c:/tests/foo%20bar"}, - {"res:///c://tests/foo%20bar", URL_FILE_USE_PATHURL, "res:///c://tests/foo%20bar"}, - {"res:///c:\tests\foo bar", 0, "res:///c:\tests\foo bar"}, - {"res:///c:\tests\foo bar", URL_DONT_SIMPLIFY, "res:///c:\tests\foo bar"}, - {"res://c:\tests\foo bar/res", URL_FILE_USE_PATHURL, "res://c:\tests\foo bar/res"}, - {"res://c:\tests/res\foo%20bar/strange\sth", 0, "res://c:\tests/res\foo%20bar/strange\sth"}, - {"res://c:\tests/res\foo%20bar/strange\sth", URL_FILE_USE_PATHURL, "res://c:\tests/res\foo%20bar/strange\sth"}, - {"res://c:\tests/res\foo%20bar/strange\sth", URL_UNESCAPE, "res://c:\tests/res\foo bar/strange\sth"}, - {"/A/../B/./C/../../test_remove_dot_segments", 0, "/test_remove_dot_segments"}, - {"/A/../B/./C/../../test_remove_dot_segments", URL_FILE_USE_PATHURL, "/test_remove_dot_segments"}, - {"/A/../B/./C/../../test_remove_dot_segments", URL_WININET_COMPATIBILITY, "/test_remove_dot_segments"}, - {"/A/B\C/D\E", 0, "/A/B\C/D\E"}, - {"/A/B\C/D\E", URL_FILE_USE_PATHURL, "/A/B\C/D\E"}, - {"/A/B\C/D\E", URL_WININET_COMPATIBILITY, "/A/B\C/D\E"}, - {"///A/../B", 0, "///B"}, - {"///A/../B", URL_FILE_USE_PATHURL, "///B"}, - {"///A/../B", URL_WININET_COMPATIBILITY, "///B"}, - {"A", 0, "A"}, - {"../A", 0, "../A"}, - {"A/../B", 0, "B"}, - {"/uri-res/N2R?urn:sha1:B3K", URL_DONT_ESCAPE_EXTRA_INFO | URL_WININET_COMPATIBILITY /*0x82000000*/, "/uri-res/N2R?urn:sha1:B3K"} /*LimeWire online installer calls this*/, - {"http:www.winehq.org/dir/../index.html", 0, "http:www.winehq.org/index.html"}, - {"http://localhost/test.html", URL_FILE_USE_PATHURL, "http://localhost/test.html%22%7D, - {"http://localhost/te%20st.html", URL_FILE_USE_PATHURL, "http://localhost/te%20st.html%22%7D, - {"http://www.winehq.org/%E6%A1%9C.html", URL_FILE_USE_PATHURL, "http://www.winehq.org/%E6%A1%9C.html%22%7D, - {"mk:@MSITStore:C:\Program Files/AutoCAD 2008\Help/acad_acg.chm::/WSfacf1429558a55de1a7524c1004e616f8b-322b.htm", 0, "mk:@MSITStore:C:\Program Files/AutoCAD 2008\Help/acad_acg.chm::/WSfacf1429558a55de1a7524c1004e616f8b-322b.htm"}, - {"ftp:@MSITStore:C:\Program Files/AutoCAD 2008\Help/acad_acg.chm::/WSfacf1429558a55de1a7524c1004e616f8b-322b.htm", 0, "ftp:@MSITStore:C:\Program Files/AutoCAD 2008\Help/acad_acg.chm::/WSfacf1429558a55de1a7524c1004e616f8b-322b.htm"}, - {"file:@MSITStore:C:\Program Files/AutoCAD 2008\Help/acad_acg.chm::/WSfacf1429558a55de1a7524c1004e616f8b-322b.htm", 0, "file:@MSITStore:C:/Program Files/AutoCAD 2008/Help/acad_acg.chm::/WSfacf1429558a55de1a7524c1004e616f8b-322b.htm"}, - {"http:@MSITStore:C:\Program Files/AutoCAD 2008\Help/acad_acg.chm::/WSfacf1429558a55de1a7524c1004e616f8b-322b.htm", 0, "http:@MSITStore:C:/Program Files/AutoCAD 2008/Help/acad_acg.chm::/WSfacf1429558a55de1a7524c1004e616f8b-322b.htm"}, - {"http:@MSITStore:C:\Program Files/AutoCAD 2008\Help/acad_acg.chm::/WSfacf1429558a55de1a7524c1004e616f8b-322b.htm", URL_FILE_USE_PATHURL, "http:@MSITStore:C:/Program Files/AutoCAD 2008/Help/acad_acg.chm::/WSfacf1429558a55de1a7524c1004e616f8b-322b.htm"}, - {"mk:@MSITStore:C:\Program Files/AutoCAD 2008\Help/acad_acg.chm::/WSfacf1429558a55de1a7524c1004e616f8b-322b.htm", URL_FILE_USE_PATHURL, "mk:@MSITStore:C:\Program Files/AutoCAD 2008\Help/acad_acg.chm::/WSfacf1429558a55de1a7524c1004e616f8b-322b.htm"}, };
+ static const struct canonicalize_test misc_tests[] = + { + {"", 0, ""}, + + /* If both slashes are omitted, everything afterwards is replicated + * as-is, with the exception that the final period is dropped from + * "scheme:." */ + + {"wine:.", 0, "wine:"}, + {"wine:.", URL_DONT_SIMPLIFY, "wine:."}, + {"wine:./", 0, "wine:./"}, + {"wine:..", 0, "wine:.."}, + {"wine:../", 0, "wine:../"}, + {"wine:a", 0, "wine:a"}, + {"wine:a/", 0, "wine:a/"}, + {"wine:a/b/./../c", 0, "wine:a/b/./../c"}, + + {"wine:a/b?c/./d", 0, "wine:a/b?c/./d"}, + {"wine:a/b#c/./d", 0, "wine:a/b#c/./d"}, + {"wine:a/b#c?d", 0, "wine:a/b?d#c"}, + {"wine:.#c?d", 0, "wine:?d#c"}, + + /* A backslash directly after the colon is not treated specially. */ + {"wine:\././a", 0, "wine:\././a"}, + + {"wine:a/b &c", 0, "wine:a/b &c"}, + + /* If there's no scheme or hostname, things mostly follow the "unknown + * scheme" rules, except that a would-be empty string results in a + * single slash instead. */ + + {"a", 0, "a"}, + {"a/", 0, "a/"}, + {".", 0, "/"}, + {".", URL_DONT_SIMPLIFY, "./"}, + {"./", 0, "/"}, + {"./.", 0, "/"}, + {"././a", 0, "a"}, + {"a/.", 0, "a/"}, + {"a/./", 0, "a/"}, + {"a/./b", 0, "a/b"}, + + {"..", 0, "../"}, + {"../", 0, "../"}, + {"../a", 0, "../a"}, + {"../a/..", 0, "../"}, + {"a/..", 0, "/"}, + {"a/../..", 0, "../"}, + {"a/b/..", 0, "a/"}, + {"a/b/../", 0, "a/"}, + {"a/b/../c", 0, "a/c"}, + {"a/b/../c/..", 0, "a/"}, + {"a/b/../c/../..", 0, "/"}, + + {"a/b?c/./d", 0, "a/b?c/./d"}, + {"a/b#c/./d", 0, "a/b#c/./d"}, + {"a/b#c?d", 0, "a/b?d#c"}, + {"?c", 0, "?c"}, + {".?c", 0, "/?c"}, + + {"?c/./d", 0, "?c/./d"}, + {"#c/./d", 0, "#c/./d"}, + + {"a\b/..", 0, "/"}, + + {"a/b &c", 0, "a/b &c"}, + + /* A colon by itself is not interpreted as any sort of scheme. */ + {"://../../a", 0, "a"}, + + /* mk: is another idiosyncratic scheme, although thankfully it behaves + * rather simply. It has no concept of a hostname; if two slashes follow + * the scheme it simply treats them as two empty path elements. */ + {"mk:", 0, "mk:"}, + {"mk:.", 0, "mk:"}, + {"mk:..", 0, "mk:"}, + {"mk:/", 0, "mk:/"}, + {"mk:/.", 0, "mk:/"}, + {"mk:/..", 0, "mk:"}, + {"mk:a", 0, "mk:a"}, + {"mk:a:", 0, "mk:a:"}, + {"mk://", 0, "mk://"}, + {"mk://.", 0, "mk://"}, + {"mk://..", 0, "mk:/"}, + {"mk://../..", 0, "mk:"}, + {"mk://../..", URL_DONT_SIMPLIFY, "mk://../.."}, + {"mk://../../..", 0, "mk:"}, + + /* Backslashes are not translated into forward slashes. They are treated + * as path separators, but in a somewhat buggy manner: only dots before + * a forward slash are collapsed, and a double dot rewinds to the + * previous forward slash. */ + {"mk:a/.\", 0, "mk:a/.\"}, + {"mk:a/.\b", 0, "mk:a/.\b"}, + {"mk:a\.\b", 0, "mk:a\.\b"}, + {"mk:a\./b", 0, "mk:a\b"}, + {"mk:a./b", 0, "mk:a./b"}, + {"mk:a\b/..\c", 0, "mk:a\b/..\c"}, + {"mk:a\b\..\c", 0, "mk:a\b\..\c"}, + {"mk:a/b\../c", 0, "mk:a/c"}, + {"mk:a\b../c", 0, "mk:a\b../c"}, + + /* Progids get a forward slash appended if there isn't one already, and + * dots don't rewind past them. Despite the fact that progids are + * supposed to end with a colon, UrlCanonicalize() considers them to + * end with the slash. + * + * If the first path segment is a dot or double dot, it's treated as + * a relative path, like http, but only before a forward slash. */ + + {"mk:@", 0, "mk:@/"}, + {"mk:@progid", 0, "mk:@progid/"}, + {"mk:@progid:a", 0, "mk:@progid:a/"}, + {"mk:@progid:a/b", 0, "mk:@progid:a/b"}, + {"mk:@Progid:a/b/../..", 0, "mk:@Progid:a/"}, + {"mk:@progid/a", 0, "mk:@progid/a"}, + {"mk:@progid\a", 0, "mk:@progid\a/"}, + {"mk:@progid/a/../..", 0, "mk:@progid/"}, + {"mk:@progid/.", 0, "mk:@progid/."}, + {"mk:@progid/.?", 0, "mk:@progid/.?"}, + {"mk:@progid/./..", 0, "mk:@progid/./.."}, + {"mk:@progid/../..", 0, "mk:@progid/../.."}, + {"mk:@progid/a\.\b", 0, "mk:@progid/a\.\b"}, + {"mk:@progid/a\..\b", 0, "mk:@progid/a\..\b"}, + {"mk:@progid/.\..", 0, "mk:@progid/"}, + + {"mk:a/b?c/../d", 0, "mk:a/b?c/../d"}, + {"mk:a/b#c/../d", 0, "mk:a/b#c/../d"}, + {"mk:a/b#c?d", 0, "mk:a/b#c?d"}, + {"mk:@progid/a/b?c/../d", 0, "mk:@progid/a/b?c/../d"}, + {"mk:@progid?c/d/..", 0, "mk:@progid?c/"}, + + {"mk:a/b &c", 0, "mk:a/b &c"}, + + {"mk:@MSITStore:dir/test.chm::/file.html/..", 0, "mk:@MSITStore:dir/test.chm::/"}, + {"mk:@MSITStore:dir/test.chm::/file.html/../..", 0, "mk:@MSITStore:dir/"}, + + /* Whitespace except for plain spaces are stripped before parsing. */ + {" \t\n\rwi\t\n\rne\t\n\r:\t\n\r/\t\n\r/\t\n\r./../a/.\t\n\r./ \t\n\r", 0, "wine://./../"}, + /* Initial and final spaces and C0 control characters are also stripped, + * but not 007F or C1 control characters. */ + {" \a\t\x01 wine://./.. \x1f\n\v ", 0, "wine://./../"}, + {" wine ://./..", 0, "wine :/"}, + {" wine: //a/../b", 0, "wine: //a/../b"}, + {" wine://a/b c/.. ", 0, "wine://a/"}, + {"\x7f/\a/\v/\x01/\x1f/\x80", 0, "\x7f/\a/\v/\x01/\x1f/\x80"}, + + /* Schemes are not case-sensitive, but are flattened to lowercase. + * The hostname for http-like schemes is also flattened to lowercase + * (but not for file; see above). */ + {"wInE://A/B", 0, "wine://A/B"}, + {"hTtP://A/b/../../C", 0, "http://a/C%22%7D, + {"fTP://A/B\./C", 0, "ftp://a/B\C"}, + {"aBoUT://A/B/./", 0, "about://A/B/./"}, + {"mK://..", 0, "mk:/"}, + + /* Characters allowed in a scheme are alphanumeric, hyphen, plus, period. */ + {"0Aa+-.://./..", 0, "0aa+-.://./../"}, + {"a_://./..", 0, "a_:/"}, + {"a,://./..", 0, "a,:/"}, + + {"/uri-res/N2R?urn:sha1:B3K", URL_DONT_ESCAPE_EXTRA_INFO | URL_WININET_COMPATIBILITY, "/uri-res/N2R?urn:sha1:B3K"} /* LimeWire online installer calls this */, + {"mk:@MSITStore:C:\Program Files/AutoCAD 2008\Help/acad_acg.chm::/WSfacf1429558a55de1a7524c1004e616f8b-322b.htm", 0, + "mk:@MSITStore:C:\Program Files/AutoCAD 2008\Help/acad_acg.chm::/WSfacf1429558a55de1a7524c1004e616f8b-322b.htm"}, + }; + + static const DWORD file_flags[] = {0, URL_FILE_USE_PATHURL, URL_WININET_COMPATIBILITY}; + urllen = lstrlenA(winehqA);
/* Parameter checks */ dwSize = ARRAY_SIZE(szReturnUrl); hr = UrlCanonicalizeA(NULL, szReturnUrl, &dwSize, URL_UNESCAPE); ok(hr == E_INVALIDARG, "Got unexpected hr %#lx.\n", hr); + ok(dwSize == ARRAY_SIZE(szReturnUrl), "got size %lu\n", dwSize);
dwSize = ARRAY_SIZE(szReturnUrl); hr = UrlCanonicalizeA(winehqA, NULL, &dwSize, URL_UNESCAPE); ok(hr == E_INVALIDARG, "Got unexpected hr %#lx.\n", hr); + ok(dwSize == ARRAY_SIZE(szReturnUrl), "got size %lu\n", dwSize);
hr = UrlCanonicalizeA(winehqA, szReturnUrl, NULL, URL_UNESCAPE); ok(hr == E_INVALIDARG, "Got unexpected hr %#lx.\n", hr); @@ -1095,6 +2024,7 @@ static void test_UrlCanonicalizeA(void) dwSize = 0; hr = UrlCanonicalizeA(winehqA, szReturnUrl, &dwSize, URL_UNESCAPE); ok(hr == E_INVALIDARG, "Got unexpected hr %#lx.\n", hr); + ok(!dwSize, "got size %lu\n", dwSize);
/* buffer has no space for the result */ dwSize=urllen-1; @@ -1153,10 +2083,71 @@ static void test_UrlCanonicalizeA(void) longurl[sizeof(longurl)-1] = '\0'; hr = UrlCanonicalizeA(longurl, szReturnUrl, &dwSize, URL_WININET_COMPATIBILITY | URL_ESCAPE_UNSAFE); ok(hr == S_OK, "hr = %lx\n", hr); + ok(dwSize == strlen(szReturnUrl), "got size %lu\n", dwSize); + + for (f = 0; f < ARRAY_SIZE(file_flags); ++f) + { + for (i = 0; i < ARRAY_SIZE(unk_scheme_tests); ++i) + { + check_url_canonicalize(unk_scheme_tests[i].url, + unk_scheme_tests[i].flags | file_flags[f], unk_scheme_tests[i].expect); + sprintf(url, "wine:%s", unk_scheme_tests[i].url); + sprintf(expect, "wine:%s", unk_scheme_tests[i].expect); + check_url_canonicalize(url, unk_scheme_tests[i].flags | file_flags[f], expect); + } + + for (i = 0; i < ARRAY_SIZE(http_tests); ++i) + { + static const struct + { + const char *prefix; + BOOL ftp_like; + } + prefixes[] = + { + {"ftp", TRUE}, + {"gopher"}, + {"http"}, + {"https"}, + {"local", TRUE}, + {"news"}, + {"nntp"}, + {"res", TRUE}, + {"snews"}, + {"telnet"}, + {"wais", TRUE}, + }; + + for (j = 0; j < ARRAY_SIZE(prefixes); ++j) + { + sprintf(url, "%s:%s", prefixes[j].prefix, http_tests[i].url); + if (prefixes[j].ftp_like && http_tests[i].expect_ftp) + sprintf(expect, "%s:%s", prefixes[j].prefix, http_tests[i].expect_ftp); + else + sprintf(expect, "%s:%s", prefixes[j].prefix, http_tests[i].expect); + + check_url_canonicalize(url, http_tests[i].flags | file_flags[f], expect); + } + } + + for (i = 0; i < ARRAY_SIZE(opaque_tests); ++i) + { + static const char *const prefixes[] = {"about", "javascript", "mailto", "shell", "vbscript"}; + + for (j = 0; j < ARRAY_SIZE(prefixes); ++j) + { + sprintf(url, "%s:%s", prefixes[j], opaque_tests[i].url); + sprintf(expect, "%s:%s", prefixes[j], opaque_tests[i].expect); + check_url_canonicalize(url, opaque_tests[i].flags | file_flags[f], expect); + } + }
- /* test url-modification */ - for (i = 0; i < ARRAY_SIZE(tests); i++) - check_url_canonicalize(tests[i].url, tests[i].flags, tests[i].expect); + for (i = 0; i < ARRAY_SIZE(misc_tests); i++) + check_url_canonicalize(misc_tests[i].url, misc_tests[i].flags | file_flags[f], misc_tests[i].expect); + } + + for (i = 0; i < ARRAY_SIZE(file_tests); i++) + check_url_canonicalize(file_tests[i].url, file_tests[i].flags, file_tests[i].expect); }
/* ########################### */ @@ -1175,10 +2166,12 @@ static void test_UrlCanonicalizeW(void) dwSize = ARRAY_SIZE(szReturnUrl); hr = UrlCanonicalizeW(NULL, szReturnUrl, &dwSize, URL_UNESCAPE); ok(hr == E_INVALIDARG, "Got unexpected hr %#lx.\n", hr); + ok(dwSize == ARRAY_SIZE(szReturnUrl), "got size %lu\n", dwSize);
dwSize = ARRAY_SIZE(szReturnUrl); hr = UrlCanonicalizeW(winehqW, NULL, &dwSize, URL_UNESCAPE); ok(hr == E_INVALIDARG, "Got unexpected hr %#lx.\n", hr); + ok(dwSize == ARRAY_SIZE(szReturnUrl), "got size %lu\n", dwSize);
hr = UrlCanonicalizeW(winehqW, szReturnUrl, NULL, URL_UNESCAPE); ok(hr == E_INVALIDARG, "Got unexpected hr %#lx.\n", hr); @@ -1186,6 +2179,7 @@ static void test_UrlCanonicalizeW(void) dwSize = 0; hr = UrlCanonicalizeW(winehqW, szReturnUrl, &dwSize, URL_UNESCAPE); ok(hr == E_INVALIDARG, "Got unexpected hr %#lx.\n", hr); + ok(!dwSize, "got size %lu\n", dwSize);
/* buffer has no space for the result */ dwSize = (urllen-1); @@ -1228,6 +2222,13 @@ static void test_UrlCanonicalizeW(void) "got 0x%lx with %lu and size %lu for %u (expected 'S_OK' and size %lu)\n", hr, GetLastError(), dwSize, lstrlenW(szReturnUrl), urllen);
+ /* Only ASCII alphanumeric characters are allowed in a scheme. */ + dwSize = ARRAY_SIZE(szReturnUrl); + hr = UrlCanonicalizeW(L"f\xe8ve://./..", szReturnUrl, &dwSize, 0); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + ok(!wcscmp(szReturnUrl, L"f\xe8ve:/"), "Got URL %s.\n", debugstr_w(szReturnUrl)); + ok(dwSize == wcslen(szReturnUrl), "got size %lu\n", dwSize); + /* check that the characters 1..32 are chopped from the end of the string */ for (i = 1; i < 65536; i++) {