4 * Copyright 2000 Huw D M Davies for CodeWeavers.
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
22 #include "wine/port.h"
30 #include "wine/unicode.h"
34 #define NO_SHLWAPI_STREAM
36 #include "wine/debug.h"
38 HMODULE WINAPI MLLoadLibraryW(LPCWSTR,HMODULE,DWORD);
39 BOOL WINAPI MLFreeLibrary(HMODULE);
40 HRESULT WINAPI MLBuildResURLW(LPCWSTR,HMODULE,DWORD,LPCWSTR,LPWSTR,DWORD);
42 WINE_DEFAULT_DEBUG_CHANNEL(shell);
44 /* The following schemes were identified in the native version of
45 * SHLWAPI.DLL version 5.50
48 URL_SCHEME scheme_number;
49 WCHAR scheme_name[12];
50 } shlwapi_schemes[] = {
51 {URL_SCHEME_FTP, {'f','t','p',0}},
52 {URL_SCHEME_HTTP, {'h','t','t','p',0}},
53 {URL_SCHEME_GOPHER, {'g','o','p','h','e','r',0}},
54 {URL_SCHEME_MAILTO, {'m','a','i','l','t','o',0}},
55 {URL_SCHEME_NEWS, {'n','e','w','s',0}},
56 {URL_SCHEME_NNTP, {'n','n','t','p',0}},
57 {URL_SCHEME_TELNET, {'t','e','l','n','e','t',0}},
58 {URL_SCHEME_WAIS, {'w','a','i','s',0}},
59 {URL_SCHEME_FILE, {'f','i','l','e',0}},
60 {URL_SCHEME_MK, {'m','k',0}},
61 {URL_SCHEME_HTTPS, {'h','t','t','p','s',0}},
62 {URL_SCHEME_SHELL, {'s','h','e','l','l',0}},
63 {URL_SCHEME_SNEWS, {'s','n','e','w','s',0}},
64 {URL_SCHEME_LOCAL, {'l','o','c','a','l',0}},
65 {URL_SCHEME_JAVASCRIPT, {'j','a','v','a','s','c','r','i','p','t',0}},
66 {URL_SCHEME_VBSCRIPT, {'v','b','s','c','r','i','p','t',0}},
67 {URL_SCHEME_ABOUT, {'a','b','o','u','t',0}},
68 {URL_SCHEME_RES, {'r','e','s',0}},
72 LPCWSTR pScheme; /* [out] start of scheme */
73 DWORD szScheme; /* [out] size of scheme (until colon) */
74 LPCWSTR pUserName; /* [out] start of Username */
75 DWORD szUserName; /* [out] size of Username (until ":" or "@") */
76 LPCWSTR pPassword; /* [out] start of Password */
77 DWORD szPassword; /* [out] size of Password (until "@") */
78 LPCWSTR pHostName; /* [out] start of Hostname */
79 DWORD szHostName; /* [out] size of Hostname (until ":" or "/") */
80 LPCWSTR pPort; /* [out] start of Port */
81 DWORD szPort; /* [out] size of Port (until "/" or eos) */
82 LPCWSTR pQuery; /* [out] start of Query */
83 DWORD szQuery; /* [out] size of Query (until eos) */
93 static const CHAR hexDigits[] = "0123456789ABCDEF";
95 static const WCHAR fileW[] = {'f','i','l','e','\0'};
97 static const unsigned char HashDataLookup[256] = {
98 0x01, 0x0E, 0x6E, 0x19, 0x61, 0xAE, 0x84, 0x77, 0x8A, 0xAA, 0x7D, 0x76, 0x1B,
99 0xE9, 0x8C, 0x33, 0x57, 0xC5, 0xB1, 0x6B, 0xEA, 0xA9, 0x38, 0x44, 0x1E, 0x07,
100 0xAD, 0x49, 0xBC, 0x28, 0x24, 0x41, 0x31, 0xD5, 0x68, 0xBE, 0x39, 0xD3, 0x94,
101 0xDF, 0x30, 0x73, 0x0F, 0x02, 0x43, 0xBA, 0xD2, 0x1C, 0x0C, 0xB5, 0x67, 0x46,
102 0x16, 0x3A, 0x4B, 0x4E, 0xB7, 0xA7, 0xEE, 0x9D, 0x7C, 0x93, 0xAC, 0x90, 0xB0,
103 0xA1, 0x8D, 0x56, 0x3C, 0x42, 0x80, 0x53, 0x9C, 0xF1, 0x4F, 0x2E, 0xA8, 0xC6,
104 0x29, 0xFE, 0xB2, 0x55, 0xFD, 0xED, 0xFA, 0x9A, 0x85, 0x58, 0x23, 0xCE, 0x5F,
105 0x74, 0xFC, 0xC0, 0x36, 0xDD, 0x66, 0xDA, 0xFF, 0xF0, 0x52, 0x6A, 0x9E, 0xC9,
106 0x3D, 0x03, 0x59, 0x09, 0x2A, 0x9B, 0x9F, 0x5D, 0xA6, 0x50, 0x32, 0x22, 0xAF,
107 0xC3, 0x64, 0x63, 0x1A, 0x96, 0x10, 0x91, 0x04, 0x21, 0x08, 0xBD, 0x79, 0x40,
108 0x4D, 0x48, 0xD0, 0xF5, 0x82, 0x7A, 0x8F, 0x37, 0x69, 0x86, 0x1D, 0xA4, 0xB9,
109 0xC2, 0xC1, 0xEF, 0x65, 0xF2, 0x05, 0xAB, 0x7E, 0x0B, 0x4A, 0x3B, 0x89, 0xE4,
110 0x6C, 0xBF, 0xE8, 0x8B, 0x06, 0x18, 0x51, 0x14, 0x7F, 0x11, 0x5B, 0x5C, 0xFB,
111 0x97, 0xE1, 0xCF, 0x15, 0x62, 0x71, 0x70, 0x54, 0xE2, 0x12, 0xD6, 0xC7, 0xBB,
112 0x0D, 0x20, 0x5E, 0xDC, 0xE0, 0xD4, 0xF7, 0xCC, 0xC4, 0x2B, 0xF9, 0xEC, 0x2D,
113 0xF4, 0x6F, 0xB6, 0x99, 0x88, 0x81, 0x5A, 0xD9, 0xCA, 0x13, 0xA5, 0xE7, 0x47,
114 0xE6, 0x8E, 0x60, 0xE3, 0x3E, 0xB3, 0xF6, 0x72, 0xA2, 0x35, 0xA0, 0xD7, 0xCD,
115 0xB4, 0x2F, 0x6D, 0x2C, 0x26, 0x1F, 0x95, 0x87, 0x00, 0xD8, 0x34, 0x3F, 0x17,
116 0x25, 0x45, 0x27, 0x75, 0x92, 0xB8, 0xA3, 0xC8, 0xDE, 0xEB, 0xF8, 0xF3, 0xDB,
117 0x0A, 0x98, 0x83, 0x7B, 0xE5, 0xCB, 0x4C, 0x78, 0xD1 };
119 static DWORD get_scheme_code(LPCWSTR scheme, DWORD scheme_len)
123 for(i=0; i < sizeof(shlwapi_schemes)/sizeof(shlwapi_schemes[0]); i++) {
124 if(scheme_len == strlenW(shlwapi_schemes[i].scheme_name)
125 && !memcmp(scheme, shlwapi_schemes[i].scheme_name, scheme_len*sizeof(WCHAR)))
126 return shlwapi_schemes[i].scheme_number;
129 return URL_SCHEME_UNKNOWN;
132 /*************************************************************************
135 * Parse a Url into its constituent parts.
139 * y [O] Undocumented structure holding the parsed information
142 * Success: S_OK. y contains the parsed Url details.
143 * Failure: An HRESULT error code.
145 HRESULT WINAPI ParseURLA(LPCSTR x, PARSEDURLA *y)
147 WCHAR scheme[INTERNET_MAX_SCHEME_LENGTH];
151 TRACE("%s %p\n", debugstr_a(x), y);
153 if(y->cbSize != sizeof(*y))
156 while(*ptr && (isalnum(*ptr) || *ptr == '-'))
159 if (*ptr != ':' || ptr <= x+1) {
160 y->pszProtocol = NULL;
165 y->cchProtocol = ptr-x;
166 y->pszSuffix = ptr+1;
167 y->cchSuffix = strlen(y->pszSuffix);
169 len = MultiByteToWideChar(CP_ACP, 0, x, ptr-x,
170 scheme, sizeof(scheme)/sizeof(WCHAR));
171 y->nScheme = get_scheme_code(scheme, len);
176 /*************************************************************************
179 * Unicode version of ParseURLA.
181 HRESULT WINAPI ParseURLW(LPCWSTR x, PARSEDURLW *y)
183 const WCHAR *ptr = x;
185 TRACE("%s %p\n", debugstr_w(x), y);
187 if(y->cbSize != sizeof(*y))
190 while(*ptr && (isalnumW(*ptr) || *ptr == '-'))
193 if (*ptr != ':' || ptr <= x+1) {
194 y->pszProtocol = NULL;
199 y->cchProtocol = ptr-x;
200 y->pszSuffix = ptr+1;
201 y->cchSuffix = strlenW(y->pszSuffix);
202 y->nScheme = get_scheme_code(x, ptr-x);
207 /*************************************************************************
208 * UrlCanonicalizeA [SHLWAPI.@]
210 * Canonicalize a Url.
213 * pszUrl [I] Url to cCanonicalize
214 * pszCanonicalized [O] Destination for converted Url.
215 * pcchCanonicalized [I/O] Length of pszUrl, destination for length of pszCanonicalized
216 * dwFlags [I] Flags controlling the conversion.
219 * Success: S_OK. The pszCanonicalized contains the converted Url.
220 * Failure: E_POINTER, if *pcchCanonicalized is too small.
222 * MSDN incorrectly describes the flags for this function. They should be:
223 *| URL_DONT_ESCAPE_EXTRA_INFO 0x02000000
224 *| URL_ESCAPE_SPACES_ONLY 0x04000000
225 *| URL_ESCAPE_PERCENT 0x00001000
226 *| URL_ESCAPE_UNSAFE 0x10000000
227 *| URL_UNESCAPE 0x10000000
228 *| URL_DONT_SIMPLIFY 0x08000000
229 *| URL_ESCAPE_SEGMENT_ONLY 0x00002000
231 HRESULT WINAPI UrlCanonicalizeA(LPCSTR pszUrl, LPSTR pszCanonicalized,
232 LPDWORD pcchCanonicalized, DWORD dwFlags)
234 LPWSTR base, canonical;
238 TRACE("(%s, %p, %p, 0x%08x) *pcchCanonicalized: %d\n", debugstr_a(pszUrl), pszCanonicalized,
239 pcchCanonicalized, dwFlags, pcchCanonicalized ? *pcchCanonicalized : -1);
241 if(!pszUrl || !pszCanonicalized || !pcchCanonicalized)
244 base = HeapAlloc(GetProcessHeap(), 0,
245 (2*INTERNET_MAX_URL_LENGTH) * sizeof(WCHAR));
246 canonical = base + INTERNET_MAX_URL_LENGTH;
248 MultiByteToWideChar(0, 0, pszUrl, -1, base, INTERNET_MAX_URL_LENGTH);
249 len = INTERNET_MAX_URL_LENGTH;
251 ret = UrlCanonicalizeW(base, canonical, &len, dwFlags);
253 *pcchCanonicalized = len * 2;
254 HeapFree(GetProcessHeap(), 0, base);
258 len2 = WideCharToMultiByte(0, 0, canonical, -1, 0, 0, 0, 0);
259 if (len2 > *pcchCanonicalized) {
260 *pcchCanonicalized = len2;
261 HeapFree(GetProcessHeap(), 0, base);
264 WideCharToMultiByte(0, 0, canonical, -1, pszCanonicalized, *pcchCanonicalized, 0, 0);
265 *pcchCanonicalized = len;
266 HeapFree(GetProcessHeap(), 0, base);
270 /*************************************************************************
271 * UrlCanonicalizeW [SHLWAPI.@]
273 * See UrlCanonicalizeA.
275 HRESULT WINAPI UrlCanonicalizeW(LPCWSTR pszUrl, LPWSTR pszCanonicalized,
276 LPDWORD pcchCanonicalized, DWORD dwFlags)
280 LPWSTR lpszUrlCpy, wk1, wk2, mp, mp2, root;
282 DWORD nByteLen, nLen, nWkLen;
285 static const WCHAR wszFile[] = {'f','i','l','e',':'};
286 static const WCHAR wszLocalhost[] = {'l','o','c','a','l','h','o','s','t'};
288 TRACE("(%s, %p, %p, 0x%08x) *pcchCanonicalized: %d\n", debugstr_w(pszUrl), pszCanonicalized,
289 pcchCanonicalized, dwFlags, pcchCanonicalized ? *pcchCanonicalized : -1);
291 if(!pszUrl || !pszCanonicalized || !pcchCanonicalized)
295 *pszCanonicalized = 0;
299 nByteLen = (strlenW(pszUrl) + 1) * sizeof(WCHAR); /* length in bytes */
300 lpszUrlCpy = HeapAlloc(GetProcessHeap(), 0,
301 INTERNET_MAX_URL_LENGTH * sizeof(WCHAR));
303 if((dwFlags & URL_FILE_USE_PATHURL) && nByteLen >= sizeof(wszFile)
304 && !memcmp(wszFile, pszUrl, sizeof(wszFile)))
310 * 1 have 2[+] alnum 2,3
311 * 2 have scheme (found :) 4,6,3
312 * 3 failed (no location)
314 * 5 have 1[+] alnum 6,3
315 * 6 have location (found /) save root location
318 wk1 = (LPWSTR)pszUrl;
322 if(pszUrl[1] == ':') { /* Assume path */
323 static const WCHAR wszFilePrefix[] = {'f','i','l','e',':','/','/','/'};
325 memcpy(wk2, wszFilePrefix, sizeof(wszFilePrefix));
326 wk2 += sizeof(wszFilePrefix)/sizeof(WCHAR);
327 if (dwFlags & URL_FILE_USE_PATHURL)
333 dwFlags |= URL_ESCAPE_UNSAFE;
340 if (!isalnumW(*wk1)) {state = 3; break;}
342 if (!isalnumW(*wk1)) {state = 3; break;}
348 if (*wk1++ == ':') state = 2;
352 if (*wk1 != '/') {state = 6; break;}
354 if((dwFlags & URL_FILE_USE_PATHURL) && nByteLen >= sizeof(wszLocalhost)
355 && !memcmp(wszLocalhost, wk1, sizeof(wszLocalhost))){
356 wk1 += sizeof(wszLocalhost)/sizeof(WCHAR);
357 while(*wk1 == '\\' && (dwFlags & URL_FILE_USE_PATHURL))
360 if(*wk1 == '/' && (dwFlags & URL_FILE_USE_PATHURL))
365 nWkLen = strlenW(wk1);
366 memcpy(wk2, wk1, (nWkLen + 1) * sizeof(WCHAR));
372 if(*mp == '/' || *mp == '\\')
378 if (!isalnumW(*wk1) && (*wk1 != '-') && (*wk1 != '.') && (*wk1 != ':'))
380 while(isalnumW(*wk1) || (*wk1 == '-') || (*wk1 == '.') || (*wk1 == ':'))
387 if (*wk1 != '/' && *wk1 != '\\') {state = 3; break;}
388 while(*wk1 == '/' || *wk1 == '\\') {
395 if(dwFlags & URL_DONT_SIMPLIFY) {
400 /* Now at root location, cannot back up any more. */
401 /* "root" will point at the '/' */
405 mp = strchrW(wk1, '/');
406 mp2 = strchrW(wk1, '\\');
407 if(mp2 && (!mp || mp2 < mp))
410 nWkLen = strlenW(wk1);
411 memcpy(wk2, wk1, (nWkLen + 1) * sizeof(WCHAR));
418 memcpy(wk2, wk1, nLen * sizeof(WCHAR));
426 TRACE("found '/.'\n");
427 if (wk1[1] == '/' || wk1[1] == '\\') {
428 /* case of /./ -> skip the ./ */
431 else if (wk1[1] == '.') {
432 /* found /.. look for next / */
433 TRACE("found '/..'\n");
434 if (wk1[2] == '/' || wk1[2] == '\\' ||wk1[2] == '?'
435 || wk1[2] == '#' || !wk1[2]) {
436 /* case /../ -> need to backup wk2 */
437 TRACE("found '/../'\n");
438 *(wk2-1) = '\0'; /* set end of string */
439 mp = strrchrW(root, slash);
440 if (mp && (mp >= root)) {
441 /* found valid backup point */
443 if(wk1[2] != '/' && wk1[2] != '\\')
449 /* did not find point, restore '/' */
459 FIXME("how did we get here - state=%d\n", state);
460 HeapFree(GetProcessHeap(), 0, lpszUrlCpy);
464 TRACE("Simplified, orig <%s>, simple <%s>\n",
465 debugstr_w(pszUrl), debugstr_w(lpszUrlCpy));
467 nLen = lstrlenW(lpszUrlCpy);
468 while ((nLen > 0) && ((lpszUrlCpy[nLen-1] <= ' ')))
469 lpszUrlCpy[--nLen]=0;
471 if(dwFlags & (URL_UNESCAPE | URL_FILE_USE_PATHURL))
472 UrlUnescapeW(lpszUrlCpy, NULL, &nLen, URL_UNESCAPE_INPLACE);
474 if((EscapeFlags = dwFlags & (URL_ESCAPE_UNSAFE |
475 URL_ESCAPE_SPACES_ONLY |
477 URL_DONT_ESCAPE_EXTRA_INFO |
478 URL_ESCAPE_SEGMENT_ONLY ))) {
479 EscapeFlags &= ~URL_ESCAPE_UNSAFE;
480 hr = UrlEscapeW(lpszUrlCpy, pszCanonicalized, pcchCanonicalized,
482 } else { /* No escaping needed, just copy the string */
483 nLen = lstrlenW(lpszUrlCpy);
484 if(nLen < *pcchCanonicalized)
485 memcpy(pszCanonicalized, lpszUrlCpy, (nLen + 1)*sizeof(WCHAR));
490 *pcchCanonicalized = nLen;
493 HeapFree(GetProcessHeap(), 0, lpszUrlCpy);
496 TRACE("result %s\n", debugstr_w(pszCanonicalized));
501 /*************************************************************************
502 * UrlCombineA [SHLWAPI.@]
507 * pszBase [I] Base Url
508 * pszRelative [I] Url to combine with pszBase
509 * pszCombined [O] Destination for combined Url
510 * pcchCombined [O] Destination for length of pszCombined
511 * dwFlags [I] URL_ flags from "shlwapi.h"
514 * Success: S_OK. pszCombined contains the combined Url, pcchCombined
515 * contains its length.
516 * Failure: An HRESULT error code indicating the error.
518 HRESULT WINAPI UrlCombineA(LPCSTR pszBase, LPCSTR pszRelative,
519 LPSTR pszCombined, LPDWORD pcchCombined,
522 LPWSTR base, relative, combined;
523 DWORD ret, len, len2;
525 TRACE("(base %s, Relative %s, Combine size %d, flags %08x) using W version\n",
526 debugstr_a(pszBase),debugstr_a(pszRelative),
527 pcchCombined?*pcchCombined:0,dwFlags);
529 if(!pszBase || !pszRelative || !pcchCombined)
532 base = HeapAlloc(GetProcessHeap(), 0,
533 (3*INTERNET_MAX_URL_LENGTH) * sizeof(WCHAR));
534 relative = base + INTERNET_MAX_URL_LENGTH;
535 combined = relative + INTERNET_MAX_URL_LENGTH;
537 MultiByteToWideChar(0, 0, pszBase, -1, base, INTERNET_MAX_URL_LENGTH);
538 MultiByteToWideChar(0, 0, pszRelative, -1, relative, INTERNET_MAX_URL_LENGTH);
541 ret = UrlCombineW(base, relative, pszCombined?combined:NULL, &len, dwFlags);
544 HeapFree(GetProcessHeap(), 0, base);
548 len2 = WideCharToMultiByte(0, 0, combined, len, 0, 0, 0, 0);
549 if (len2 > *pcchCombined) {
550 *pcchCombined = len2;
551 HeapFree(GetProcessHeap(), 0, base);
554 WideCharToMultiByte(0, 0, combined, len+1, pszCombined, (*pcchCombined)+1,
556 *pcchCombined = len2;
557 HeapFree(GetProcessHeap(), 0, base);
561 /*************************************************************************
562 * UrlCombineW [SHLWAPI.@]
566 HRESULT WINAPI UrlCombineW(LPCWSTR pszBase, LPCWSTR pszRelative,
567 LPWSTR pszCombined, LPDWORD pcchCombined,
570 PARSEDURLW base, relative;
571 DWORD myflags, sizeloc = 0;
572 DWORD len, res1, res2, process_case = 0;
573 LPWSTR work, preliminary, mbase, mrelative;
574 static const WCHAR myfilestr[] = {'f','i','l','e',':','/','/','/','\0'};
577 TRACE("(base %s, Relative %s, Combine size %d, flags %08x)\n",
578 debugstr_w(pszBase),debugstr_w(pszRelative),
579 pcchCombined?*pcchCombined:0,dwFlags);
581 if(!pszBase || !pszRelative || !pcchCombined)
584 base.cbSize = sizeof(base);
585 relative.cbSize = sizeof(relative);
587 /* Get space for duplicates of the input and the output */
588 preliminary = HeapAlloc(GetProcessHeap(), 0, (3*INTERNET_MAX_URL_LENGTH) *
590 mbase = preliminary + INTERNET_MAX_URL_LENGTH;
591 mrelative = mbase + INTERNET_MAX_URL_LENGTH;
594 /* Canonicalize the base input prior to looking for the scheme */
595 myflags = dwFlags & (URL_DONT_SIMPLIFY | URL_UNESCAPE);
596 len = INTERNET_MAX_URL_LENGTH;
597 ret = UrlCanonicalizeW(pszBase, mbase, &len, myflags);
599 /* Canonicalize the relative input prior to looking for the scheme */
600 len = INTERNET_MAX_URL_LENGTH;
601 ret = UrlCanonicalizeW(pszRelative, mrelative, &len, myflags);
603 /* See if the base has a scheme */
604 res1 = ParseURLW(mbase, &base);
606 /* if pszBase has no scheme, then return pszRelative */
607 TRACE("no scheme detected in Base\n");
611 /* mk is a special case */
612 if(base.nScheme == URL_SCHEME_MK) {
613 static const WCHAR wsz[] = {':',':',0};
615 WCHAR *ptr = strstrW(base.pszSuffix, wsz);
620 delta = ptr-base.pszSuffix;
621 base.cchProtocol += delta;
622 base.pszSuffix += delta;
623 base.cchSuffix -= delta;
626 /* get size of location field (if it exists) */
627 work = (LPWSTR)base.pszSuffix;
629 if (*work++ == '/') {
630 if (*work++ == '/') {
631 /* At this point have start of location and
632 * it ends at next '/' or end of string.
634 while(*work && (*work != '/')) work++;
635 sizeloc = (DWORD)(work - base.pszSuffix);
640 /* Change .sizep2 to not have the last leaf in it,
641 * Note: we need to start after the location (if it exists)
643 work = strrchrW((base.pszSuffix+sizeloc), '/');
645 len = (DWORD)(work - base.pszSuffix + 1);
646 base.cchSuffix = len;
651 * .pszSuffix points to location (starting with '//')
652 * .cchSuffix length of location (above) and rest less the last
654 * sizeloc length of location (above) up to but not including
658 res2 = ParseURLW(mrelative, &relative);
660 /* no scheme in pszRelative */
661 TRACE("no scheme detected in Relative\n");
662 relative.pszSuffix = mrelative; /* case 3,4,5 depends on this */
663 relative.cchSuffix = strlenW(mrelative);
664 if (*pszRelative == ':') {
665 /* case that is either left alone or uses pszBase */
666 if (dwFlags & URL_PLUGGABLE_PROTOCOL) {
673 if (isalnum(*mrelative) && (*(mrelative + 1) == ':')) {
674 /* case that becomes "file:///" */
675 strcpyW(preliminary, myfilestr);
679 if ((*mrelative == '/') && (*(mrelative+1) == '/')) {
680 /* pszRelative has location and rest */
684 if (*mrelative == '/') {
685 /* case where pszRelative is root to location */
689 process_case = (*base.pszSuffix == '/' || base.nScheme == URL_SCHEME_MK) ? 5 : 3;
693 /* handle cases where pszRelative has scheme */
694 if ((base.cchProtocol == relative.cchProtocol) &&
695 (strncmpW(base.pszProtocol, relative.pszProtocol, base.cchProtocol) == 0)) {
697 /* since the schemes are the same */
698 if ((*relative.pszSuffix == '/') && (*(relative.pszSuffix+1) == '/')) {
699 /* case where pszRelative replaces location and following */
703 if (*relative.pszSuffix == '/') {
704 /* case where pszRelative is root to location */
708 /* replace either just location if base's location starts with a
709 * slash or otherwise everything */
710 process_case = (*base.pszSuffix == '/') ? 5 : 1;
713 if ((*relative.pszSuffix == '/') && (*(relative.pszSuffix+1) == '/')) {
714 /* case where pszRelative replaces scheme, location,
715 * and following and handles PLUGGABLE
722 } while(FALSE); /* a little trick to allow easy exit from nested if's */
725 switch (process_case) {
728 * Return pszRelative appended to what ever is in pszCombined,
729 * (which may the string "file:///"
731 strcatW(preliminary, mrelative);
734 case 2: /* case where pszRelative replaces scheme, and location */
735 strcpyW(preliminary, mrelative);
739 * Return the pszBase scheme with pszRelative. Basically
740 * keeps the scheme and replaces the domain and following.
742 memcpy(preliminary, base.pszProtocol, (base.cchProtocol + 1)*sizeof(WCHAR));
743 work = preliminary + base.cchProtocol + 1;
744 strcpyW(work, relative.pszSuffix);
748 * Return the pszBase scheme and location but everything
749 * after the location is pszRelative. (Replace document
752 memcpy(preliminary, base.pszProtocol, (base.cchProtocol+1+sizeloc)*sizeof(WCHAR));
753 work = preliminary + base.cchProtocol + 1 + sizeloc;
754 if (dwFlags & URL_PLUGGABLE_PROTOCOL)
756 strcpyW(work, relative.pszSuffix);
760 * Return the pszBase without its document (if any) and
761 * append pszRelative after its scheme.
763 memcpy(preliminary, base.pszProtocol,
764 (base.cchProtocol+1+base.cchSuffix)*sizeof(WCHAR));
765 work = preliminary + base.cchProtocol+1+base.cchSuffix - 1;
768 strcpyW(work, relative.pszSuffix);
772 FIXME("How did we get here????? process_case=%d\n", process_case);
777 /* Reuse mrelative as temp storage as its already allocated and not needed anymore */
778 ret = UrlCanonicalizeW(preliminary, mrelative, pcchCombined, (dwFlags & ~URL_FILE_USE_PATHURL));
779 if(SUCCEEDED(ret) && pszCombined) {
780 lstrcpyW(pszCombined, mrelative);
782 TRACE("return-%d len=%d, %s\n",
783 process_case, *pcchCombined, debugstr_w(pszCombined));
785 HeapFree(GetProcessHeap(), 0, preliminary);
789 /*************************************************************************
790 * UrlEscapeA [SHLWAPI.@]
793 HRESULT WINAPI UrlEscapeA(
799 WCHAR bufW[INTERNET_MAX_URL_LENGTH];
800 WCHAR *escapedW = bufW;
803 DWORD lenW = sizeof(bufW)/sizeof(WCHAR), lenA;
805 if (!pszEscaped || !pcchEscaped || !*pcchEscaped)
808 if(!RtlCreateUnicodeStringFromAsciiz(&urlW, pszUrl))
810 if((ret = UrlEscapeW(urlW.Buffer, escapedW, &lenW, dwFlags)) == E_POINTER) {
811 escapedW = HeapAlloc(GetProcessHeap(), 0, lenW * sizeof(WCHAR));
812 ret = UrlEscapeW(urlW.Buffer, escapedW, &lenW, dwFlags);
815 RtlUnicodeToMultiByteSize(&lenA, escapedW, lenW * sizeof(WCHAR));
816 if(*pcchEscaped > lenA) {
817 RtlUnicodeToMultiByteN(pszEscaped, *pcchEscaped - 1, &lenA, escapedW, lenW * sizeof(WCHAR));
818 pszEscaped[lenA] = 0;
821 *pcchEscaped = lenA + 1;
825 if(escapedW != bufW) HeapFree(GetProcessHeap(), 0, escapedW);
826 RtlFreeUnicodeString(&urlW);
830 #define WINE_URL_BASH_AS_SLASH 0x01
831 #define WINE_URL_COLLAPSE_SLASHES 0x02
832 #define WINE_URL_ESCAPE_SLASH 0x04
833 #define WINE_URL_ESCAPE_HASH 0x08
834 #define WINE_URL_ESCAPE_QUESTION 0x10
835 #define WINE_URL_STOP_ON_HASH 0x20
836 #define WINE_URL_STOP_ON_QUESTION 0x40
838 static inline BOOL URL_NeedEscapeW(WCHAR ch, DWORD dwFlags, DWORD int_flags)
844 if(dwFlags & URL_ESCAPE_SPACES_ONLY) {
851 if ((dwFlags & URL_ESCAPE_PERCENT) && (ch == '%'))
854 if (ch <= 31 || ch >= 127)
875 if (int_flags & WINE_URL_ESCAPE_SLASH) return TRUE;
879 if (int_flags & WINE_URL_ESCAPE_QUESTION) return TRUE;
883 if (int_flags & WINE_URL_ESCAPE_HASH) return TRUE;
893 /*************************************************************************
894 * UrlEscapeW [SHLWAPI.@]
896 * Converts unsafe characters in a Url into escape sequences.
899 * pszUrl [I] Url to modify
900 * pszEscaped [O] Destination for modified Url
901 * pcchEscaped [I/O] Length of pszUrl, destination for length of pszEscaped
902 * dwFlags [I] URL_ flags from "shlwapi.h"
905 * Success: S_OK. pszEscaped contains the escaped Url, pcchEscaped
906 * contains its length.
907 * Failure: E_POINTER, if pszEscaped is not large enough. In this case
908 * pcchEscaped is set to the required length.
910 * Converts unsafe characters into their escape sequences.
913 * - By default this function stops converting at the first '?' or
915 * - If dwFlags contains URL_ESCAPE_SPACES_ONLY then only spaces are
916 * converted, but the conversion continues past a '?' or '#'.
917 * - Note that this function did not work well (or at all) in shlwapi version 4.
920 * Only the following flags are implemented:
921 *| URL_ESCAPE_SPACES_ONLY
922 *| URL_DONT_ESCAPE_EXTRA_INFO
923 *| URL_ESCAPE_SEGMENT_ONLY
924 *| URL_ESCAPE_PERCENT
926 HRESULT WINAPI UrlEscapeW(
933 DWORD needed = 0, ret;
934 BOOL stop_escaping = FALSE;
935 WCHAR next[5], *dst = pszEscaped;
937 PARSEDURLW parsed_url;
940 static const WCHAR localhost[] = {'l','o','c','a','l','h','o','s','t',0};
942 TRACE("(%s %p %p 0x%08x)\n", debugstr_w(pszUrl), pszEscaped,
943 pcchEscaped, dwFlags);
945 if(!pszUrl || !pcchEscaped)
948 if(dwFlags & ~(URL_ESCAPE_SPACES_ONLY |
949 URL_ESCAPE_SEGMENT_ONLY |
950 URL_DONT_ESCAPE_EXTRA_INFO |
952 FIXME("Unimplemented flags: %08x\n", dwFlags);
955 if (dwFlags & URL_ESCAPE_SPACES_ONLY)
956 /* if SPACES_ONLY specified, reset the other controls */
957 dwFlags &= ~(URL_DONT_ESCAPE_EXTRA_INFO |
959 URL_ESCAPE_SEGMENT_ONLY);
962 /* if SPACES_ONLY *not* specified the assume DONT_ESCAPE_EXTRA_INFO */
963 dwFlags |= URL_DONT_ESCAPE_EXTRA_INFO;
967 if(dwFlags & URL_ESCAPE_SEGMENT_ONLY) {
968 int_flags = WINE_URL_ESCAPE_QUESTION | WINE_URL_ESCAPE_HASH | WINE_URL_ESCAPE_SLASH;
970 parsed_url.cbSize = sizeof(parsed_url);
971 if(ParseURLW(pszUrl, &parsed_url) != S_OK)
972 parsed_url.nScheme = URL_SCHEME_INVALID;
974 TRACE("scheme = %d (%s)\n", parsed_url.nScheme, debugstr_wn(parsed_url.pszProtocol, parsed_url.cchProtocol));
976 if(dwFlags & URL_DONT_ESCAPE_EXTRA_INFO)
977 int_flags = WINE_URL_STOP_ON_HASH | WINE_URL_STOP_ON_QUESTION;
979 switch(parsed_url.nScheme) {
980 case URL_SCHEME_FILE:
981 int_flags |= WINE_URL_BASH_AS_SLASH | WINE_URL_COLLAPSE_SLASHES | WINE_URL_ESCAPE_HASH;
982 int_flags &= ~WINE_URL_STOP_ON_HASH;
985 case URL_SCHEME_HTTP:
986 case URL_SCHEME_HTTPS:
987 int_flags |= WINE_URL_BASH_AS_SLASH;
988 if(parsed_url.pszSuffix[0] != '/' && parsed_url.pszSuffix[0] != '\\')
989 int_flags |= WINE_URL_ESCAPE_SLASH;
992 case URL_SCHEME_MAILTO:
993 int_flags |= WINE_URL_ESCAPE_SLASH | WINE_URL_ESCAPE_QUESTION | WINE_URL_ESCAPE_HASH;
994 int_flags &= ~(WINE_URL_STOP_ON_QUESTION | WINE_URL_STOP_ON_HASH);
997 case URL_SCHEME_INVALID:
1000 case URL_SCHEME_FTP:
1002 if(parsed_url.pszSuffix[0] != '/')
1003 int_flags |= WINE_URL_ESCAPE_SLASH;
1008 for(src = pszUrl; *src; ) {
1012 if((int_flags & WINE_URL_COLLAPSE_SLASHES) && src == pszUrl + parsed_url.cchProtocol + 1) {
1013 int localhost_len = sizeof(localhost)/sizeof(WCHAR) - 1;
1014 while(cur == '/' || cur == '\\') {
1018 if(slashes == 2 && !strncmpiW(src, localhost, localhost_len)) { /* file://localhost/ -> file:/// */
1019 if(*(src + localhost_len) == '/' || *(src + localhost_len) == '\\')
1020 src += localhost_len + 1;
1027 next[0] = next[1] = next[2] = '/';
1034 next[0] = next[1] = '/';
1041 if(cur == '#' && (int_flags & WINE_URL_STOP_ON_HASH))
1042 stop_escaping = TRUE;
1044 if(cur == '?' && (int_flags & WINE_URL_STOP_ON_QUESTION))
1045 stop_escaping = TRUE;
1047 if(cur == '\\' && (int_flags & WINE_URL_BASH_AS_SLASH) && !stop_escaping) cur = '/';
1049 if(URL_NeedEscapeW(cur, dwFlags, int_flags) && stop_escaping == FALSE) {
1051 next[1] = hexDigits[(cur >> 4) & 0xf];
1052 next[2] = hexDigits[cur & 0xf];
1061 if(needed + len <= *pcchEscaped) {
1062 memcpy(dst, next, len*sizeof(WCHAR));
1068 if(needed < *pcchEscaped) {
1072 needed++; /* add one for the '\0' */
1075 *pcchEscaped = needed;
1080 /*************************************************************************
1081 * UrlUnescapeA [SHLWAPI.@]
1083 * Converts Url escape sequences back to ordinary characters.
1086 * pszUrl [I/O] Url to convert
1087 * pszUnescaped [O] Destination for converted Url
1088 * pcchUnescaped [I/O] Size of output string
1089 * dwFlags [I] URL_ESCAPE_ Flags from "shlwapi.h"
1092 * Success: S_OK. The converted value is in pszUnescaped, or in pszUrl if
1093 * dwFlags includes URL_ESCAPE_INPLACE.
1094 * Failure: E_POINTER if the converted Url is bigger than pcchUnescaped. In
1095 * this case pcchUnescaped is set to the size required.
1097 * If dwFlags includes URL_DONT_ESCAPE_EXTRA_INFO, the conversion stops at
1098 * the first occurrence of either a '?' or '#' character.
1100 HRESULT WINAPI UrlUnescapeA(
1103 LPDWORD pcchUnescaped,
1110 BOOL stop_unescaping = FALSE;
1112 TRACE("(%s, %p, %p, 0x%08x)\n", debugstr_a(pszUrl), pszUnescaped,
1113 pcchUnescaped, dwFlags);
1115 if (!pszUrl) return E_INVALIDARG;
1117 if(dwFlags & URL_UNESCAPE_INPLACE)
1121 if (!pszUnescaped || !pcchUnescaped) return E_INVALIDARG;
1125 for(src = pszUrl, needed = 0; *src; src++, needed++) {
1126 if(dwFlags & URL_DONT_UNESCAPE_EXTRA_INFO &&
1127 (*src == '#' || *src == '?')) {
1128 stop_unescaping = TRUE;
1130 } else if(*src == '%' && isxdigit(*(src + 1)) && isxdigit(*(src + 2))
1131 && stop_unescaping == FALSE) {
1134 memcpy(buf, src + 1, 2);
1136 ih = strtol(buf, NULL, 16);
1138 src += 2; /* Advance to end of escape */
1142 if(dwFlags & URL_UNESCAPE_INPLACE || needed < *pcchUnescaped)
1146 if(dwFlags & URL_UNESCAPE_INPLACE || needed < *pcchUnescaped) {
1150 needed++; /* add one for the '\0' */
1153 if(!(dwFlags & URL_UNESCAPE_INPLACE))
1154 *pcchUnescaped = needed;
1157 TRACE("result %s\n", (dwFlags & URL_UNESCAPE_INPLACE) ?
1158 debugstr_a(pszUrl) : debugstr_a(pszUnescaped));
1164 /*************************************************************************
1165 * UrlUnescapeW [SHLWAPI.@]
1169 HRESULT WINAPI UrlUnescapeW(
1171 LPWSTR pszUnescaped,
1172 LPDWORD pcchUnescaped,
1179 BOOL stop_unescaping = FALSE;
1181 TRACE("(%s, %p, %p, 0x%08x)\n", debugstr_w(pszUrl), pszUnescaped,
1182 pcchUnescaped, dwFlags);
1184 if(!pszUrl) return E_INVALIDARG;
1186 if(dwFlags & URL_UNESCAPE_INPLACE)
1190 if (!pszUnescaped || !pcchUnescaped) return E_INVALIDARG;
1194 for(src = pszUrl, needed = 0; *src; src++, needed++) {
1195 if(dwFlags & URL_DONT_UNESCAPE_EXTRA_INFO &&
1196 (*src == '#' || *src == '?')) {
1197 stop_unescaping = TRUE;
1199 } else if(*src == '%' && isxdigitW(*(src + 1)) && isxdigitW(*(src + 2))
1200 && stop_unescaping == FALSE) {
1202 WCHAR buf[5] = {'0','x',0};
1203 memcpy(buf + 2, src + 1, 2*sizeof(WCHAR));
1205 StrToIntExW(buf, STIF_SUPPORT_HEX, &ih);
1207 src += 2; /* Advance to end of escape */
1211 if(dwFlags & URL_UNESCAPE_INPLACE || needed < *pcchUnescaped)
1215 if(dwFlags & URL_UNESCAPE_INPLACE || needed < *pcchUnescaped) {
1219 needed++; /* add one for the '\0' */
1222 if(!(dwFlags & URL_UNESCAPE_INPLACE))
1223 *pcchUnescaped = needed;
1226 TRACE("result %s\n", (dwFlags & URL_UNESCAPE_INPLACE) ?
1227 debugstr_w(pszUrl) : debugstr_w(pszUnescaped));
1233 /*************************************************************************
1234 * UrlGetLocationA [SHLWAPI.@]
1236 * Get the location from a Url.
1239 * pszUrl [I] Url to get the location from
1242 * A pointer to the start of the location in pszUrl, or NULL if there is
1246 * - MSDN erroneously states that "The location is the segment of the Url
1247 * starting with a '?' or '#' character". Neither V4 nor V5 of shlwapi.dll
1248 * stop at '?' and always return a NULL in this case.
1249 * - MSDN also erroneously states that "If a file URL has a query string,
1250 * the returned string is the query string". In all tested cases, if the
1251 * Url starts with "fi" then a NULL is returned. V5 gives the following results:
1254 *| NULL file://aa/b/cd#hohoh
1255 *| #hohoh http://aa/b/cd#hohoh
1256 *| NULL fi://aa/b/cd#hohoh
1257 *| #hohoh ff://aa/b/cd#hohoh
1259 LPCSTR WINAPI UrlGetLocationA(
1265 base.cbSize = sizeof(base);
1266 res1 = ParseURLA(pszUrl, &base);
1267 if (res1) return NULL; /* invalid scheme */
1269 /* if scheme is file: then never return pointer */
1270 if (strncmp(base.pszProtocol, "file", min(4,base.cchProtocol)) == 0) return NULL;
1272 /* Look for '#' and return its addr */
1273 return strchr(base.pszSuffix, '#');
1276 /*************************************************************************
1277 * UrlGetLocationW [SHLWAPI.@]
1279 * See UrlGetLocationA.
1281 LPCWSTR WINAPI UrlGetLocationW(
1287 base.cbSize = sizeof(base);
1288 res1 = ParseURLW(pszUrl, &base);
1289 if (res1) return NULL; /* invalid scheme */
1291 /* if scheme is file: then never return pointer */
1292 if (strncmpW(base.pszProtocol, fileW, min(4,base.cchProtocol)) == 0) return NULL;
1294 /* Look for '#' and return its addr */
1295 return strchrW(base.pszSuffix, '#');
1298 /*************************************************************************
1299 * UrlCompareA [SHLWAPI.@]
1304 * pszUrl1 [I] First Url to compare
1305 * pszUrl2 [I] Url to compare to pszUrl1
1306 * fIgnoreSlash [I] TRUE = compare only up to a final slash
1309 * less than zero, zero, or greater than zero indicating pszUrl2 is greater
1310 * than, equal to, or less than pszUrl1 respectively.
1312 INT WINAPI UrlCompareA(
1317 INT ret, len, len1, len2;
1320 return strcmp(pszUrl1, pszUrl2);
1321 len1 = strlen(pszUrl1);
1322 if (pszUrl1[len1-1] == '/') len1--;
1323 len2 = strlen(pszUrl2);
1324 if (pszUrl2[len2-1] == '/') len2--;
1326 return strncmp(pszUrl1, pszUrl2, len1);
1327 len = min(len1, len2);
1328 ret = strncmp(pszUrl1, pszUrl2, len);
1329 if (ret) return ret;
1330 if (len1 > len2) return 1;
1334 /*************************************************************************
1335 * UrlCompareW [SHLWAPI.@]
1339 INT WINAPI UrlCompareW(
1345 size_t len, len1, len2;
1348 return strcmpW(pszUrl1, pszUrl2);
1349 len1 = strlenW(pszUrl1);
1350 if (pszUrl1[len1-1] == '/') len1--;
1351 len2 = strlenW(pszUrl2);
1352 if (pszUrl2[len2-1] == '/') len2--;
1354 return strncmpW(pszUrl1, pszUrl2, len1);
1355 len = min(len1, len2);
1356 ret = strncmpW(pszUrl1, pszUrl2, len);
1357 if (ret) return ret;
1358 if (len1 > len2) return 1;
1362 /*************************************************************************
1363 * HashData [SHLWAPI.@]
1365 * Hash an input block into a variable sized digest.
1368 * lpSrc [I] Input block
1369 * nSrcLen [I] Length of lpSrc
1370 * lpDest [I] Output for hash digest
1371 * nDestLen [I] Length of lpDest
1374 * Success: TRUE. lpDest is filled with the computed hash value.
1375 * Failure: FALSE, if any argument is invalid.
1377 HRESULT WINAPI HashData(const unsigned char *lpSrc, DWORD nSrcLen,
1378 unsigned char *lpDest, DWORD nDestLen)
1380 INT srcCount = nSrcLen - 1, destCount = nDestLen - 1;
1382 if (IsBadReadPtr(lpSrc, nSrcLen) ||
1383 IsBadWritePtr(lpDest, nDestLen))
1384 return E_INVALIDARG;
1386 while (destCount >= 0)
1388 lpDest[destCount] = (destCount & 0xff);
1392 while (srcCount >= 0)
1394 destCount = nDestLen - 1;
1395 while (destCount >= 0)
1397 lpDest[destCount] = HashDataLookup[lpSrc[srcCount] ^ lpDest[destCount]];
1405 /*************************************************************************
1406 * UrlHashA [SHLWAPI.@]
1408 * Produce a Hash from a Url.
1411 * pszUrl [I] Url to hash
1412 * lpDest [O] Destinationh for hash
1413 * nDestLen [I] Length of lpDest
1416 * Success: S_OK. lpDest is filled with the computed hash value.
1417 * Failure: E_INVALIDARG, if any argument is invalid.
1419 HRESULT WINAPI UrlHashA(LPCSTR pszUrl, unsigned char *lpDest, DWORD nDestLen)
1421 if (IsBadStringPtrA(pszUrl, -1) || IsBadWritePtr(lpDest, nDestLen))
1422 return E_INVALIDARG;
1424 HashData((const BYTE*)pszUrl, (int)strlen(pszUrl), lpDest, nDestLen);
1428 /*************************************************************************
1429 * UrlHashW [SHLWAPI.@]
1433 HRESULT WINAPI UrlHashW(LPCWSTR pszUrl, unsigned char *lpDest, DWORD nDestLen)
1435 char szUrl[MAX_PATH];
1437 TRACE("(%s,%p,%d)\n",debugstr_w(pszUrl), lpDest, nDestLen);
1439 if (IsBadStringPtrW(pszUrl, -1) || IsBadWritePtr(lpDest, nDestLen))
1440 return E_INVALIDARG;
1442 /* Win32 hashes the data as an ASCII string, presumably so that both A+W
1443 * return the same digests for the same URL.
1445 WideCharToMultiByte(0, 0, pszUrl, -1, szUrl, MAX_PATH, 0, 0);
1446 HashData((const BYTE*)szUrl, (int)strlen(szUrl), lpDest, nDestLen);
1450 /*************************************************************************
1451 * UrlApplySchemeA [SHLWAPI.@]
1453 * Apply a scheme to a Url.
1456 * pszIn [I] Url to apply scheme to
1457 * pszOut [O] Destination for modified Url
1458 * pcchOut [I/O] Length of pszOut/destination for length of pszOut
1459 * dwFlags [I] URL_ flags from "shlwapi.h"
1462 * Success: S_OK: pszOut contains the modified Url, pcchOut contains its length.
1463 * Failure: An HRESULT error code describing the error.
1465 HRESULT WINAPI UrlApplySchemeA(LPCSTR pszIn, LPSTR pszOut, LPDWORD pcchOut, DWORD dwFlags)
1471 TRACE("(%s, %p, %p:out size %d, 0x%08x)\n", debugstr_a(pszIn),
1472 pszOut, pcchOut, pcchOut ? *pcchOut : 0, dwFlags);
1474 if (!pszIn || !pszOut || !pcchOut) return E_INVALIDARG;
1476 in = HeapAlloc(GetProcessHeap(), 0,
1477 (2*INTERNET_MAX_URL_LENGTH) * sizeof(WCHAR));
1478 out = in + INTERNET_MAX_URL_LENGTH;
1480 MultiByteToWideChar(CP_ACP, 0, pszIn, -1, in, INTERNET_MAX_URL_LENGTH);
1481 len = INTERNET_MAX_URL_LENGTH;
1483 ret = UrlApplySchemeW(in, out, &len, dwFlags);
1485 HeapFree(GetProcessHeap(), 0, in);
1489 len = WideCharToMultiByte(CP_ACP, 0, out, -1, NULL, 0, NULL, NULL);
1490 if (len > *pcchOut) {
1495 WideCharToMultiByte(CP_ACP, 0, out, -1, pszOut, *pcchOut, NULL, NULL);
1500 HeapFree(GetProcessHeap(), 0, in);
1504 static HRESULT URL_GuessScheme(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut)
1509 DWORD value_len, data_len, dwType, i;
1510 WCHAR reg_path[MAX_PATH];
1511 WCHAR value[MAX_PATH], data[MAX_PATH];
1514 MultiByteToWideChar(0, 0,
1515 "Software\\Microsoft\\Windows\\CurrentVersion\\URL\\Prefixes",
1516 -1, reg_path, MAX_PATH);
1517 RegOpenKeyExW(HKEY_LOCAL_MACHINE, reg_path, 0, 1, &newkey);
1519 while(value_len = data_len = MAX_PATH,
1520 RegEnumValueW(newkey, index, value, &value_len,
1521 0, &dwType, (LPVOID)data, &data_len) == 0) {
1522 TRACE("guess %d %s is %s\n",
1523 index, debugstr_w(value), debugstr_w(data));
1526 for(i=0; i<value_len; i++) {
1529 /* remember that TRUE is not-equal */
1530 j = ChrCmpIW(Wxx, Wyy);
1533 if ((i == value_len) && !j) {
1534 if (strlenW(data) + strlenW(pszIn) + 1 > *pcchOut) {
1535 *pcchOut = strlenW(data) + strlenW(pszIn) + 1;
1536 RegCloseKey(newkey);
1539 strcpyW(pszOut, data);
1540 strcatW(pszOut, pszIn);
1541 *pcchOut = strlenW(pszOut);
1542 TRACE("matched and set to %s\n", debugstr_w(pszOut));
1543 RegCloseKey(newkey);
1548 RegCloseKey(newkey);
1552 static HRESULT URL_ApplyDefault(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut)
1555 DWORD data_len, dwType;
1556 WCHAR data[MAX_PATH];
1558 static const WCHAR prefix_keyW[] =
1559 {'S','o','f','t','w','a','r','e',
1560 '\\','M','i','c','r','o','s','o','f','t',
1561 '\\','W','i','n','d','o','w','s',
1562 '\\','C','u','r','r','e','n','t','V','e','r','s','i','o','n',
1564 '\\','D','e','f','a','u','l','t','P','r','e','f','i','x',0};
1566 /* get and prepend default */
1567 RegOpenKeyExW(HKEY_LOCAL_MACHINE, prefix_keyW, 0, 1, &newkey);
1568 data_len = sizeof(data);
1569 RegQueryValueExW(newkey, NULL, 0, &dwType, (LPBYTE)data, &data_len);
1570 RegCloseKey(newkey);
1571 if (strlenW(data) + strlenW(pszIn) + 1 > *pcchOut) {
1572 *pcchOut = strlenW(data) + strlenW(pszIn) + 1;
1575 strcpyW(pszOut, data);
1576 strcatW(pszOut, pszIn);
1577 *pcchOut = strlenW(pszOut);
1578 TRACE("used default %s\n", debugstr_w(pszOut));
1582 /*************************************************************************
1583 * UrlApplySchemeW [SHLWAPI.@]
1585 * See UrlApplySchemeA.
1587 HRESULT WINAPI UrlApplySchemeW(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut, DWORD dwFlags)
1589 PARSEDURLW in_scheme;
1593 TRACE("(%s, %p, %p:out size %d, 0x%08x)\n", debugstr_w(pszIn),
1594 pszOut, pcchOut, pcchOut ? *pcchOut : 0, dwFlags);
1596 if (!pszIn || !pszOut || !pcchOut) return E_INVALIDARG;
1598 if (dwFlags & URL_APPLY_GUESSFILE) {
1599 FIXME("(%s %p %p(%d) 0x%08x): stub URL_APPLY_GUESSFILE not implemented\n",
1600 debugstr_w(pszIn), pszOut, pcchOut, *pcchOut, dwFlags);
1601 strcpyW(pszOut, pszIn);
1602 *pcchOut = strlenW(pszOut);
1606 in_scheme.cbSize = sizeof(in_scheme);
1607 /* See if the base has a scheme */
1608 res1 = ParseURLW(pszIn, &in_scheme);
1610 /* no scheme in input, need to see if we need to guess */
1611 if (dwFlags & URL_APPLY_GUESSSCHEME) {
1612 if ((ret = URL_GuessScheme(pszIn, pszOut, pcchOut)) != E_FAIL)
1617 /* we have a scheme, see if valid (known scheme) */
1618 if (in_scheme.nScheme) {
1619 /* have valid scheme, so just copy and exit */
1620 if (strlenW(pszIn) + 1 > *pcchOut) {
1621 *pcchOut = strlenW(pszIn) + 1;
1624 strcpyW(pszOut, pszIn);
1625 *pcchOut = strlenW(pszOut);
1626 TRACE("valid scheme, returning copy\n");
1631 /* If we are here, then either invalid scheme,
1632 * or no scheme and can't/failed guess.
1634 if ( ( ((res1 == 0) && (dwFlags & URL_APPLY_FORCEAPPLY)) ||
1636 (dwFlags & URL_APPLY_DEFAULT)) {
1637 /* find and apply default scheme */
1638 return URL_ApplyDefault(pszIn, pszOut, pcchOut);
1644 /*************************************************************************
1645 * UrlIsA [SHLWAPI.@]
1647 * Determine if a Url is of a certain class.
1650 * pszUrl [I] Url to check
1651 * Urlis [I] URLIS_ constant from "shlwapi.h"
1654 * TRUE if pszUrl belongs to the class type in Urlis.
1657 BOOL WINAPI UrlIsA(LPCSTR pszUrl, URLIS Urlis)
1663 TRACE("(%s %d)\n", debugstr_a(pszUrl), Urlis);
1668 base.cbSize = sizeof(base);
1669 res1 = ParseURLA(pszUrl, &base);
1670 if (res1) return FALSE; /* invalid scheme */
1671 switch (base.nScheme)
1673 case URL_SCHEME_MAILTO:
1674 case URL_SCHEME_SHELL:
1675 case URL_SCHEME_JAVASCRIPT:
1676 case URL_SCHEME_VBSCRIPT:
1677 case URL_SCHEME_ABOUT:
1683 return !StrCmpNA("file:", pszUrl, 5);
1685 case URLIS_DIRECTORY:
1686 last = pszUrl + strlen(pszUrl) - 1;
1687 return (last >= pszUrl && (*last == '/' || *last == '\\' ));
1690 return PathIsURLA(pszUrl);
1692 case URLIS_NOHISTORY:
1693 case URLIS_APPLIABLE:
1694 case URLIS_HASQUERY:
1696 FIXME("(%s %d): stub\n", debugstr_a(pszUrl), Urlis);
1701 /*************************************************************************
1702 * UrlIsW [SHLWAPI.@]
1706 BOOL WINAPI UrlIsW(LPCWSTR pszUrl, URLIS Urlis)
1708 static const WCHAR stemp[] = { 'f','i','l','e',':',0 };
1713 TRACE("(%s %d)\n", debugstr_w(pszUrl), Urlis);
1718 base.cbSize = sizeof(base);
1719 res1 = ParseURLW(pszUrl, &base);
1720 if (res1) return FALSE; /* invalid scheme */
1721 switch (base.nScheme)
1723 case URL_SCHEME_MAILTO:
1724 case URL_SCHEME_SHELL:
1725 case URL_SCHEME_JAVASCRIPT:
1726 case URL_SCHEME_VBSCRIPT:
1727 case URL_SCHEME_ABOUT:
1733 return !strncmpW(stemp, pszUrl, 5);
1735 case URLIS_DIRECTORY:
1736 last = pszUrl + strlenW(pszUrl) - 1;
1737 return (last >= pszUrl && (*last == '/' || *last == '\\'));
1740 return PathIsURLW(pszUrl);
1742 case URLIS_NOHISTORY:
1743 case URLIS_APPLIABLE:
1744 case URLIS_HASQUERY:
1746 FIXME("(%s %d): stub\n", debugstr_w(pszUrl), Urlis);
1751 /*************************************************************************
1752 * UrlIsNoHistoryA [SHLWAPI.@]
1754 * Determine if a Url should not be stored in the users history list.
1757 * pszUrl [I] Url to check
1760 * TRUE, if pszUrl should be excluded from the history list,
1763 BOOL WINAPI UrlIsNoHistoryA(LPCSTR pszUrl)
1765 return UrlIsA(pszUrl, URLIS_NOHISTORY);
1768 /*************************************************************************
1769 * UrlIsNoHistoryW [SHLWAPI.@]
1771 * See UrlIsNoHistoryA.
1773 BOOL WINAPI UrlIsNoHistoryW(LPCWSTR pszUrl)
1775 return UrlIsW(pszUrl, URLIS_NOHISTORY);
1778 /*************************************************************************
1779 * UrlIsOpaqueA [SHLWAPI.@]
1781 * Determine if a Url is opaque.
1784 * pszUrl [I] Url to check
1787 * TRUE if pszUrl is opaque,
1791 * An opaque Url is one that does not start with "<protocol>://".
1793 BOOL WINAPI UrlIsOpaqueA(LPCSTR pszUrl)
1795 return UrlIsA(pszUrl, URLIS_OPAQUE);
1798 /*************************************************************************
1799 * UrlIsOpaqueW [SHLWAPI.@]
1803 BOOL WINAPI UrlIsOpaqueW(LPCWSTR pszUrl)
1805 return UrlIsW(pszUrl, URLIS_OPAQUE);
1808 /*************************************************************************
1809 * Scans for characters of type "type" and when not matching found,
1810 * returns pointer to it and length in size.
1812 * Characters tested based on RFC 1738
1814 static LPCWSTR URL_ScanID(LPCWSTR start, LPDWORD size, WINE_URL_SCAN_TYPE type)
1816 static DWORD alwayszero = 0;
1825 if ( (islowerW(*start) && isalphaW(*start)) ||
1840 if ( isalphaW(*start) ||
1842 /* user/password only characters */
1847 /* *extra* characters */
1854 /* *safe* characters */
1863 } else if (*start == '%') {
1864 if (isxdigitW(*(start+1)) &&
1865 isxdigitW(*(start+2))) {
1877 if (isdigitW(*start)) {
1888 if (isalnumW(*start) ||
1900 FIXME("unknown type %d\n", type);
1901 return (LPWSTR)&alwayszero;
1903 /* TRACE("scanned %d characters next char %p<%c>\n",
1904 *size, start, *start); */
1908 /*************************************************************************
1909 * Attempt to parse URL into pieces.
1911 static LONG URL_ParseUrl(LPCWSTR pszUrl, WINE_PARSE_URL *pl)
1915 memset(pl, 0, sizeof(WINE_PARSE_URL));
1916 pl->pScheme = pszUrl;
1917 work = URL_ScanID(pl->pScheme, &pl->szScheme, SCHEME);
1918 if (!*work || (*work != ':')) goto ErrorExit;
1920 if ((*work != '/') || (*(work+1) != '/')) goto ErrorExit;
1921 pl->pUserName = work + 2;
1922 work = URL_ScanID(pl->pUserName, &pl->szUserName, USERPASS);
1923 if (*work == ':' ) {
1924 /* parse password */
1926 pl->pPassword = work;
1927 work = URL_ScanID(pl->pPassword, &pl->szPassword, USERPASS);
1929 /* what we just parsed must be the hostname and port
1930 * so reset pointers and clear then let it parse */
1931 pl->szUserName = pl->szPassword = 0;
1932 work = pl->pUserName - 1;
1933 pl->pUserName = pl->pPassword = 0;
1935 } else if (*work == '@') {
1939 } else if (!*work || (*work == '/') || (*work == '.')) {
1940 /* what was parsed was hostname, so reset pointers and let it parse */
1941 pl->szUserName = pl->szPassword = 0;
1942 work = pl->pUserName - 1;
1943 pl->pUserName = pl->pPassword = 0;
1944 } else goto ErrorExit;
1946 /* now start parsing hostname or hostnumber */
1948 pl->pHostName = work;
1949 work = URL_ScanID(pl->pHostName, &pl->szHostName, HOST);
1954 work = URL_ScanID(pl->pPort, &pl->szPort, PORT);
1957 /* see if query string */
1958 pl->pQuery = strchrW(work, '?');
1959 if (pl->pQuery) pl->szQuery = strlenW(pl->pQuery);
1961 TRACE("parse successful: scheme=%p(%d), user=%p(%d), pass=%p(%d), host=%p(%d), port=%p(%d), query=%p(%d)\n",
1962 pl->pScheme, pl->szScheme,
1963 pl->pUserName, pl->szUserName,
1964 pl->pPassword, pl->szPassword,
1965 pl->pHostName, pl->szHostName,
1966 pl->pPort, pl->szPort,
1967 pl->pQuery, pl->szQuery);
1970 FIXME("failed to parse %s\n", debugstr_w(pszUrl));
1971 return E_INVALIDARG;
1974 /*************************************************************************
1975 * UrlGetPartA [SHLWAPI.@]
1977 * Retrieve part of a Url.
1980 * pszIn [I] Url to parse
1981 * pszOut [O] Destination for part of pszIn requested
1982 * pcchOut [I] Size of pszOut
1983 * [O] length of pszOut string EXCLUDING '\0' if S_OK, otherwise
1984 * needed size of pszOut INCLUDING '\0'.
1985 * dwPart [I] URL_PART_ enum from "shlwapi.h"
1986 * dwFlags [I] URL_ flags from "shlwapi.h"
1989 * Success: S_OK. pszOut contains the part requested, pcchOut contains its length.
1990 * Failure: An HRESULT error code describing the error.
1992 HRESULT WINAPI UrlGetPartA(LPCSTR pszIn, LPSTR pszOut, LPDWORD pcchOut,
1993 DWORD dwPart, DWORD dwFlags)
1996 DWORD ret, len, len2;
1998 in = HeapAlloc(GetProcessHeap(), 0,
1999 (2*INTERNET_MAX_URL_LENGTH) * sizeof(WCHAR));
2000 out = in + INTERNET_MAX_URL_LENGTH;
2002 MultiByteToWideChar(0, 0, pszIn, -1, in, INTERNET_MAX_URL_LENGTH);
2004 len = INTERNET_MAX_URL_LENGTH;
2005 ret = UrlGetPartW(in, out, &len, dwPart, dwFlags);
2008 HeapFree(GetProcessHeap(), 0, in);
2012 len2 = WideCharToMultiByte(0, 0, out, len, 0, 0, 0, 0);
2013 if (len2 > *pcchOut) {
2015 HeapFree(GetProcessHeap(), 0, in);
2018 len2 = WideCharToMultiByte(0, 0, out, len+1, pszOut, *pcchOut, 0, 0);
2020 HeapFree(GetProcessHeap(), 0, in);
2024 /*************************************************************************
2025 * UrlGetPartW [SHLWAPI.@]
2029 HRESULT WINAPI UrlGetPartW(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut,
2030 DWORD dwPart, DWORD dwFlags)
2034 DWORD scheme, size, schsize;
2035 LPCWSTR addr, schaddr;
2037 TRACE("(%s %p %p(%d) %08x %08x)\n",
2038 debugstr_w(pszIn), pszOut, pcchOut, *pcchOut, dwPart, dwFlags);
2040 addr = strchrW(pszIn, ':');
2044 scheme = get_scheme_code(pszIn, addr-pszIn);
2046 ret = URL_ParseUrl(pszIn, &pl);
2048 schaddr = pl.pScheme;
2049 schsize = pl.szScheme;
2052 case URL_PART_SCHEME:
2053 if (!pl.szScheme) return E_INVALIDARG;
2058 case URL_PART_HOSTNAME:
2060 case URL_SCHEME_FTP:
2061 case URL_SCHEME_HTTP:
2062 case URL_SCHEME_GOPHER:
2063 case URL_SCHEME_TELNET:
2064 case URL_SCHEME_FILE:
2065 case URL_SCHEME_HTTPS:
2071 if(scheme==URL_SCHEME_FILE && (!pl.szHostName ||
2072 (pl.szHostName==1 && *(pl.pHostName+1)==':'))) {
2079 if (!pl.szHostName) return E_INVALIDARG;
2080 addr = pl.pHostName;
2081 size = pl.szHostName;
2084 case URL_PART_USERNAME:
2085 if (!pl.szUserName) return E_INVALIDARG;
2086 addr = pl.pUserName;
2087 size = pl.szUserName;
2090 case URL_PART_PASSWORD:
2091 if (!pl.szPassword) return E_INVALIDARG;
2092 addr = pl.pPassword;
2093 size = pl.szPassword;
2097 if (!pl.szPort) return E_INVALIDARG;
2102 case URL_PART_QUERY:
2103 if (!pl.szQuery) return E_INVALIDARG;
2109 return E_INVALIDARG;
2112 if (dwFlags == URL_PARTFLAG_KEEPSCHEME) {
2113 if (*pcchOut < schsize + size + 2) {
2114 *pcchOut = schsize + size + 2;
2117 memcpy(pszOut, schaddr, schsize*sizeof(WCHAR));
2118 pszOut[schsize] = ':';
2119 memcpy(pszOut+schsize+1, addr, size*sizeof(WCHAR));
2120 pszOut[schsize+1+size] = 0;
2121 *pcchOut = schsize + 1 + size;
2124 if (*pcchOut < size + 1) {*pcchOut = size+1; return E_POINTER;}
2125 memcpy(pszOut, addr, size*sizeof(WCHAR));
2129 TRACE("len=%d %s\n", *pcchOut, debugstr_w(pszOut));
2130 }else if(dwPart==URL_PART_HOSTNAME && scheme==URL_SCHEME_FILE) {
2140 /*************************************************************************
2141 * PathIsURLA [SHLWAPI.@]
2143 * Check if the given path is a Url.
2146 * lpszPath [I] Path to check.
2149 * TRUE if lpszPath is a Url.
2150 * FALSE if lpszPath is NULL or not a Url.
2152 BOOL WINAPI PathIsURLA(LPCSTR lpstrPath)
2157 TRACE("%s\n", debugstr_a(lpstrPath));
2159 if (!lpstrPath || !*lpstrPath) return FALSE;
2162 base.cbSize = sizeof(base);
2163 hres = ParseURLA(lpstrPath, &base);
2164 return hres == S_OK && (base.nScheme != URL_SCHEME_INVALID);
2167 /*************************************************************************
2168 * PathIsURLW [SHLWAPI.@]
2172 BOOL WINAPI PathIsURLW(LPCWSTR lpstrPath)
2177 TRACE("%s\n", debugstr_w(lpstrPath));
2179 if (!lpstrPath || !*lpstrPath) return FALSE;
2182 base.cbSize = sizeof(base);
2183 hres = ParseURLW(lpstrPath, &base);
2184 return hres == S_OK && (base.nScheme != URL_SCHEME_INVALID);
2187 /*************************************************************************
2188 * UrlCreateFromPathA [SHLWAPI.@]
2190 * See UrlCreateFromPathW
2192 HRESULT WINAPI UrlCreateFromPathA(LPCSTR pszPath, LPSTR pszUrl, LPDWORD pcchUrl, DWORD dwReserved)
2194 WCHAR bufW[INTERNET_MAX_URL_LENGTH];
2196 UNICODE_STRING pathW;
2198 DWORD lenW = sizeof(bufW)/sizeof(WCHAR), lenA;
2200 if(!RtlCreateUnicodeStringFromAsciiz(&pathW, pszPath))
2201 return E_INVALIDARG;
2202 if((ret = UrlCreateFromPathW(pathW.Buffer, urlW, &lenW, dwReserved)) == E_POINTER) {
2203 urlW = HeapAlloc(GetProcessHeap(), 0, lenW * sizeof(WCHAR));
2204 ret = UrlCreateFromPathW(pathW.Buffer, urlW, &lenW, dwReserved);
2206 if(ret == S_OK || ret == S_FALSE) {
2207 RtlUnicodeToMultiByteSize(&lenA, urlW, lenW * sizeof(WCHAR));
2208 if(*pcchUrl > lenA) {
2209 RtlUnicodeToMultiByteN(pszUrl, *pcchUrl - 1, &lenA, urlW, lenW * sizeof(WCHAR));
2213 *pcchUrl = lenA + 1;
2217 if(urlW != bufW) HeapFree(GetProcessHeap(), 0, urlW);
2218 RtlFreeUnicodeString(&pathW);
2222 /*************************************************************************
2223 * UrlCreateFromPathW [SHLWAPI.@]
2225 * Create a Url from a file path.
2228 * pszPath [I] Path to convert
2229 * pszUrl [O] Destination for the converted Url
2230 * pcchUrl [I/O] Length of pszUrl
2231 * dwReserved [I] Reserved, must be 0
2234 * Success: S_OK pszUrl contains the converted path, S_FALSE if the path is already a Url
2235 * Failure: An HRESULT error code.
2237 HRESULT WINAPI UrlCreateFromPathW(LPCWSTR pszPath, LPWSTR pszUrl, LPDWORD pcchUrl, DWORD dwReserved)
2242 WCHAR file_colonW[] = {'f','i','l','e',':',0};
2243 WCHAR three_slashesW[] = {'/','/','/',0};
2244 PARSEDURLW parsed_url;
2246 TRACE("(%s, %p, %p, 0x%08x)\n", debugstr_w(pszPath), pszUrl, pcchUrl, dwReserved);
2248 /* Validate arguments */
2249 if (dwReserved != 0)
2250 return E_INVALIDARG;
2251 if (!pszUrl || !pcchUrl)
2252 return E_INVALIDARG;
2255 parsed_url.cbSize = sizeof(parsed_url);
2256 if(ParseURLW(pszPath, &parsed_url) == S_OK) {
2257 if(parsed_url.nScheme != URL_SCHEME_INVALID && parsed_url.cchProtocol > 1) {
2258 needed = strlenW(pszPath);
2259 if (needed >= *pcchUrl) {
2260 *pcchUrl = needed + 1;
2264 strcpyW(pszUrl, pszPath);
2270 pszNewUrl = HeapAlloc(GetProcessHeap(), 0, (strlenW(pszPath) + 9) * sizeof(WCHAR)); /* "file:///" + pszPath_len + 1 */
2271 strcpyW(pszNewUrl, file_colonW);
2272 if(isalphaW(pszPath[0]) && pszPath[1] == ':')
2273 strcatW(pszNewUrl, three_slashesW);
2274 strcatW(pszNewUrl, pszPath);
2275 ret = UrlEscapeW(pszNewUrl, pszUrl, pcchUrl, URL_ESCAPE_PERCENT);
2277 HeapFree(GetProcessHeap(), 0, pszNewUrl);
2281 /*************************************************************************
2282 * SHAutoComplete [SHLWAPI.@]
2284 * Enable auto-completion for an edit control.
2287 * hwndEdit [I] Handle of control to enable auto-completion for
2288 * dwFlags [I] SHACF_ flags from "shlwapi.h"
2291 * Success: S_OK. Auto-completion is enabled for the control.
2292 * Failure: An HRESULT error code indicating the error.
2294 HRESULT WINAPI SHAutoComplete(HWND hwndEdit, DWORD dwFlags)
2296 FIXME("SHAutoComplete stub\n");
2300 /*************************************************************************
2301 * MLBuildResURLA [SHLWAPI.405]
2303 * Create a Url pointing to a resource in a module.
2306 * lpszLibName [I] Name of the module containing the resource
2307 * hMod [I] Callers module handle
2308 * dwFlags [I] Undocumented flags for loading the module
2309 * lpszRes [I] Resource name
2310 * lpszDest [O] Destination for resulting Url
2311 * dwDestLen [I] Length of lpszDest
2314 * Success: S_OK. lpszDest contains the resource Url.
2315 * Failure: E_INVALIDARG, if any argument is invalid, or
2316 * E_FAIL if dwDestLen is too small.
2318 HRESULT WINAPI MLBuildResURLA(LPCSTR lpszLibName, HMODULE hMod, DWORD dwFlags,
2319 LPCSTR lpszRes, LPSTR lpszDest, DWORD dwDestLen)
2321 WCHAR szLibName[MAX_PATH], szRes[MAX_PATH], szDest[MAX_PATH];
2325 MultiByteToWideChar(CP_ACP, 0, lpszLibName, -1, szLibName, sizeof(szLibName)/sizeof(WCHAR));
2328 MultiByteToWideChar(CP_ACP, 0, lpszRes, -1, szRes, sizeof(szRes)/sizeof(WCHAR));
2330 if (dwDestLen > sizeof(szLibName)/sizeof(WCHAR))
2331 dwDestLen = sizeof(szLibName)/sizeof(WCHAR);
2333 hRet = MLBuildResURLW(lpszLibName ? szLibName : NULL, hMod, dwFlags,
2334 lpszRes ? szRes : NULL, lpszDest ? szDest : NULL, dwDestLen);
2335 if (SUCCEEDED(hRet) && lpszDest)
2336 WideCharToMultiByte(CP_ACP, 0, szDest, -1, lpszDest, dwDestLen, 0, 0);
2341 /*************************************************************************
2342 * MLBuildResURLA [SHLWAPI.406]
2344 * See MLBuildResURLA.
2346 HRESULT WINAPI MLBuildResURLW(LPCWSTR lpszLibName, HMODULE hMod, DWORD dwFlags,
2347 LPCWSTR lpszRes, LPWSTR lpszDest, DWORD dwDestLen)
2349 static const WCHAR szRes[] = { 'r','e','s',':','/','/','\0' };
2350 #define szResLen ((sizeof(szRes) - sizeof(WCHAR))/sizeof(WCHAR))
2351 HRESULT hRet = E_FAIL;
2353 TRACE("(%s,%p,0x%08x,%s,%p,%d)\n", debugstr_w(lpszLibName), hMod, dwFlags,
2354 debugstr_w(lpszRes), lpszDest, dwDestLen);
2356 if (!lpszLibName || !hMod || hMod == INVALID_HANDLE_VALUE || !lpszRes ||
2357 !lpszDest || (dwFlags && dwFlags != 2))
2358 return E_INVALIDARG;
2360 if (dwDestLen >= szResLen + 1)
2362 dwDestLen -= (szResLen + 1);
2363 memcpy(lpszDest, szRes, sizeof(szRes));
2365 hMod = MLLoadLibraryW(lpszLibName, hMod, dwFlags);
2369 WCHAR szBuff[MAX_PATH];
2372 len = GetModuleFileNameW(hMod, szBuff, sizeof(szBuff)/sizeof(WCHAR));
2373 if (len && len < sizeof(szBuff)/sizeof(WCHAR))
2375 DWORD dwPathLen = strlenW(szBuff) + 1;
2377 if (dwDestLen >= dwPathLen)
2381 dwDestLen -= dwPathLen;
2382 memcpy(lpszDest + szResLen, szBuff, dwPathLen * sizeof(WCHAR));
2384 dwResLen = strlenW(lpszRes) + 1;
2385 if (dwDestLen >= dwResLen + 1)
2387 lpszDest[szResLen + dwPathLen + dwResLen] = '/';
2388 memcpy(lpszDest + szResLen + dwPathLen, lpszRes, dwResLen * sizeof(WCHAR));
2393 MLFreeLibrary(hMod);
2399 /***********************************************************************
2400 * UrlFixupW [SHLWAPI.462]
2402 * Checks the scheme part of a URL and attempts to correct misspellings.
2405 * lpszUrl [I] Pointer to the URL to be corrected
2406 * lpszTranslatedUrl [O] Pointer to a buffer to store corrected URL
2407 * dwMaxChars [I] Maximum size of corrected URL
2410 * success: S_OK if URL corrected or already correct
2411 * failure: S_FALSE if unable to correct / COM error code if other error
2414 HRESULT WINAPI UrlFixupW(LPCWSTR url, LPWSTR translatedUrl, DWORD maxChars)
2418 FIXME("(%s,%p,%d) STUB\n", debugstr_w(url), translatedUrl, maxChars);
2423 srcLen = lstrlenW(url);
2425 /* For now just copy the URL directly */
2426 lstrcpynW(translatedUrl, url, (maxChars < srcLen) ? maxChars : srcLen);