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
37 #include "wine/debug.h"
39 HMODULE WINAPI MLLoadLibraryW(LPCWSTR,HMODULE,DWORD);
40 BOOL WINAPI MLFreeLibrary(HMODULE);
41 HRESULT WINAPI MLBuildResURLW(LPCWSTR,HMODULE,DWORD,LPCWSTR,LPWSTR,DWORD);
43 WINE_DEFAULT_DEBUG_CHANNEL(shell);
45 /* The following schemes were identified in the native version of
46 * SHLWAPI.DLL version 5.50
49 URL_SCHEME scheme_number;
50 WCHAR scheme_name[12];
51 } shlwapi_schemes[] = {
52 {URL_SCHEME_FTP, {'f','t','p',0}},
53 {URL_SCHEME_HTTP, {'h','t','t','p',0}},
54 {URL_SCHEME_GOPHER, {'g','o','p','h','e','r',0}},
55 {URL_SCHEME_MAILTO, {'m','a','i','l','t','o',0}},
56 {URL_SCHEME_NEWS, {'n','e','w','s',0}},
57 {URL_SCHEME_NNTP, {'n','n','t','p',0}},
58 {URL_SCHEME_TELNET, {'t','e','l','n','e','t',0}},
59 {URL_SCHEME_WAIS, {'w','a','i','s',0}},
60 {URL_SCHEME_FILE, {'f','i','l','e',0}},
61 {URL_SCHEME_MK, {'m','k',0}},
62 {URL_SCHEME_HTTPS, {'h','t','t','p','s',0}},
63 {URL_SCHEME_SHELL, {'s','h','e','l','l',0}},
64 {URL_SCHEME_SNEWS, {'s','n','e','w','s',0}},
65 {URL_SCHEME_LOCAL, {'l','o','c','a','l',0}},
66 {URL_SCHEME_JAVASCRIPT, {'j','a','v','a','s','c','r','i','p','t',0}},
67 {URL_SCHEME_VBSCRIPT, {'v','b','s','c','r','i','p','t',0}},
68 {URL_SCHEME_ABOUT, {'a','b','o','u','t',0}},
69 {URL_SCHEME_RES, {'r','e','s',0}},
73 LPCWSTR pScheme; /* [out] start of scheme */
74 DWORD szScheme; /* [out] size of scheme (until colon) */
75 LPCWSTR pUserName; /* [out] start of Username */
76 DWORD szUserName; /* [out] size of Username (until ":" or "@") */
77 LPCWSTR pPassword; /* [out] start of Password */
78 DWORD szPassword; /* [out] size of Password (until "@") */
79 LPCWSTR pHostName; /* [out] start of Hostname */
80 DWORD szHostName; /* [out] size of Hostname (until ":" or "/") */
81 LPCWSTR pPort; /* [out] start of Port */
82 DWORD szPort; /* [out] size of Port (until "/" or eos) */
83 LPCWSTR pQuery; /* [out] start of Query */
84 DWORD szQuery; /* [out] size of Query (until eos) */
94 static const CHAR hexDigits[] = "0123456789ABCDEF";
96 static const WCHAR fileW[] = {'f','i','l','e','\0'};
98 static const unsigned char HashDataLookup[256] = {
99 0x01, 0x0E, 0x6E, 0x19, 0x61, 0xAE, 0x84, 0x77, 0x8A, 0xAA, 0x7D, 0x76, 0x1B,
100 0xE9, 0x8C, 0x33, 0x57, 0xC5, 0xB1, 0x6B, 0xEA, 0xA9, 0x38, 0x44, 0x1E, 0x07,
101 0xAD, 0x49, 0xBC, 0x28, 0x24, 0x41, 0x31, 0xD5, 0x68, 0xBE, 0x39, 0xD3, 0x94,
102 0xDF, 0x30, 0x73, 0x0F, 0x02, 0x43, 0xBA, 0xD2, 0x1C, 0x0C, 0xB5, 0x67, 0x46,
103 0x16, 0x3A, 0x4B, 0x4E, 0xB7, 0xA7, 0xEE, 0x9D, 0x7C, 0x93, 0xAC, 0x90, 0xB0,
104 0xA1, 0x8D, 0x56, 0x3C, 0x42, 0x80, 0x53, 0x9C, 0xF1, 0x4F, 0x2E, 0xA8, 0xC6,
105 0x29, 0xFE, 0xB2, 0x55, 0xFD, 0xED, 0xFA, 0x9A, 0x85, 0x58, 0x23, 0xCE, 0x5F,
106 0x74, 0xFC, 0xC0, 0x36, 0xDD, 0x66, 0xDA, 0xFF, 0xF0, 0x52, 0x6A, 0x9E, 0xC9,
107 0x3D, 0x03, 0x59, 0x09, 0x2A, 0x9B, 0x9F, 0x5D, 0xA6, 0x50, 0x32, 0x22, 0xAF,
108 0xC3, 0x64, 0x63, 0x1A, 0x96, 0x10, 0x91, 0x04, 0x21, 0x08, 0xBD, 0x79, 0x40,
109 0x4D, 0x48, 0xD0, 0xF5, 0x82, 0x7A, 0x8F, 0x37, 0x69, 0x86, 0x1D, 0xA4, 0xB9,
110 0xC2, 0xC1, 0xEF, 0x65, 0xF2, 0x05, 0xAB, 0x7E, 0x0B, 0x4A, 0x3B, 0x89, 0xE4,
111 0x6C, 0xBF, 0xE8, 0x8B, 0x06, 0x18, 0x51, 0x14, 0x7F, 0x11, 0x5B, 0x5C, 0xFB,
112 0x97, 0xE1, 0xCF, 0x15, 0x62, 0x71, 0x70, 0x54, 0xE2, 0x12, 0xD6, 0xC7, 0xBB,
113 0x0D, 0x20, 0x5E, 0xDC, 0xE0, 0xD4, 0xF7, 0xCC, 0xC4, 0x2B, 0xF9, 0xEC, 0x2D,
114 0xF4, 0x6F, 0xB6, 0x99, 0x88, 0x81, 0x5A, 0xD9, 0xCA, 0x13, 0xA5, 0xE7, 0x47,
115 0xE6, 0x8E, 0x60, 0xE3, 0x3E, 0xB3, 0xF6, 0x72, 0xA2, 0x35, 0xA0, 0xD7, 0xCD,
116 0xB4, 0x2F, 0x6D, 0x2C, 0x26, 0x1F, 0x95, 0x87, 0x00, 0xD8, 0x34, 0x3F, 0x17,
117 0x25, 0x45, 0x27, 0x75, 0x92, 0xB8, 0xA3, 0xC8, 0xDE, 0xEB, 0xF8, 0xF3, 0xDB,
118 0x0A, 0x98, 0x83, 0x7B, 0xE5, 0xCB, 0x4C, 0x78, 0xD1 };
120 static DWORD get_scheme_code(LPCWSTR scheme, DWORD scheme_len)
124 for(i=0; i < sizeof(shlwapi_schemes)/sizeof(shlwapi_schemes[0]); i++) {
125 if(scheme_len == strlenW(shlwapi_schemes[i].scheme_name)
126 && !memcmp(scheme, shlwapi_schemes[i].scheme_name, scheme_len*sizeof(WCHAR)))
127 return shlwapi_schemes[i].scheme_number;
130 return URL_SCHEME_UNKNOWN;
133 /*************************************************************************
136 * Parse a Url into its constituent parts.
140 * y [O] Undocumented structure holding the parsed information
143 * Success: S_OK. y contains the parsed Url details.
144 * Failure: An HRESULT error code.
146 HRESULT WINAPI ParseURLA(LPCSTR x, PARSEDURLA *y)
148 WCHAR scheme[INTERNET_MAX_SCHEME_LENGTH];
152 TRACE("%s %p\n", debugstr_a(x), y);
154 if(y->cbSize != sizeof(*y))
157 while(*ptr && (isalnum(*ptr) || *ptr == '-'))
160 if (*ptr != ':' || ptr <= x+1) {
161 y->pszProtocol = NULL;
162 return URL_E_INVALID_SYNTAX;
166 y->cchProtocol = ptr-x;
167 y->pszSuffix = ptr+1;
168 y->cchSuffix = strlen(y->pszSuffix);
170 len = MultiByteToWideChar(CP_ACP, 0, x, ptr-x,
171 scheme, sizeof(scheme)/sizeof(WCHAR));
172 y->nScheme = get_scheme_code(scheme, len);
177 /*************************************************************************
180 * Unicode version of ParseURLA.
182 HRESULT WINAPI ParseURLW(LPCWSTR x, PARSEDURLW *y)
184 const WCHAR *ptr = x;
186 TRACE("%s %p\n", debugstr_w(x), y);
188 if(y->cbSize != sizeof(*y))
191 while(*ptr && (isalnumW(*ptr) || *ptr == '-'))
194 if (*ptr != ':' || ptr <= x+1) {
195 y->pszProtocol = NULL;
196 return URL_E_INVALID_SYNTAX;
200 y->cchProtocol = ptr-x;
201 y->pszSuffix = ptr+1;
202 y->cchSuffix = strlenW(y->pszSuffix);
203 y->nScheme = get_scheme_code(x, ptr-x);
208 /*************************************************************************
209 * UrlCanonicalizeA [SHLWAPI.@]
211 * Canonicalize a Url.
214 * pszUrl [I] Url to cCanonicalize
215 * pszCanonicalized [O] Destination for converted Url.
216 * pcchCanonicalized [I/O] Length of pszUrl, destination for length of pszCanonicalized
217 * dwFlags [I] Flags controlling the conversion.
220 * Success: S_OK. The pszCanonicalized contains the converted Url.
221 * Failure: E_POINTER, if *pcchCanonicalized is too small.
223 * MSDN incorrectly describes the flags for this function. They should be:
224 *| URL_DONT_ESCAPE_EXTRA_INFO 0x02000000
225 *| URL_ESCAPE_SPACES_ONLY 0x04000000
226 *| URL_ESCAPE_PERCENT 0x00001000
227 *| URL_ESCAPE_UNSAFE 0x10000000
228 *| URL_UNESCAPE 0x10000000
229 *| URL_DONT_SIMPLIFY 0x08000000
230 *| URL_ESCAPE_SEGMENT_ONLY 0x00002000
232 HRESULT WINAPI UrlCanonicalizeA(LPCSTR pszUrl, LPSTR pszCanonicalized,
233 LPDWORD pcchCanonicalized, DWORD dwFlags)
235 LPWSTR base, canonical;
239 TRACE("(%s, %p, %p, 0x%08x) *pcchCanonicalized: %d\n", debugstr_a(pszUrl), pszCanonicalized,
240 pcchCanonicalized, dwFlags, pcchCanonicalized ? *pcchCanonicalized : -1);
242 if(!pszUrl || !pszCanonicalized || !pcchCanonicalized)
245 base = HeapAlloc(GetProcessHeap(), 0,
246 (2*INTERNET_MAX_URL_LENGTH) * sizeof(WCHAR));
247 canonical = base + INTERNET_MAX_URL_LENGTH;
249 MultiByteToWideChar(0, 0, pszUrl, -1, base, INTERNET_MAX_URL_LENGTH);
250 len = INTERNET_MAX_URL_LENGTH;
252 ret = UrlCanonicalizeW(base, canonical, &len, dwFlags);
254 *pcchCanonicalized = len * 2;
255 HeapFree(GetProcessHeap(), 0, base);
259 len2 = WideCharToMultiByte(0, 0, canonical, -1, 0, 0, 0, 0);
260 if (len2 > *pcchCanonicalized) {
261 *pcchCanonicalized = len2;
262 HeapFree(GetProcessHeap(), 0, base);
265 WideCharToMultiByte(0, 0, canonical, -1, pszCanonicalized, *pcchCanonicalized, 0, 0);
266 *pcchCanonicalized = len;
267 HeapFree(GetProcessHeap(), 0, base);
271 /*************************************************************************
272 * UrlCanonicalizeW [SHLWAPI.@]
274 * See UrlCanonicalizeA.
276 HRESULT WINAPI UrlCanonicalizeW(LPCWSTR pszUrl, LPWSTR pszCanonicalized,
277 LPDWORD pcchCanonicalized, DWORD dwFlags)
282 LPWSTR lpszUrlCpy, wk2, mp, mp2;
284 DWORD nByteLen, nLen, nWkLen;
287 static const WCHAR wszFile[] = {'f','i','l','e',':'};
288 static const WCHAR wszRes[] = {'r','e','s',':'};
289 static const WCHAR wszLocalhost[] = {'l','o','c','a','l','h','o','s','t'};
291 TRACE("(%s, %p, %p, 0x%08x) *pcchCanonicalized: %d\n", debugstr_w(pszUrl), pszCanonicalized,
292 pcchCanonicalized, dwFlags, pcchCanonicalized ? *pcchCanonicalized : -1);
294 if(!pszUrl || !pszCanonicalized || !pcchCanonicalized)
298 *pszCanonicalized = 0;
302 nByteLen = (strlenW(pszUrl) + 1) * sizeof(WCHAR); /* length in bytes */
303 lpszUrlCpy = HeapAlloc(GetProcessHeap(), 0,
304 INTERNET_MAX_URL_LENGTH * sizeof(WCHAR));
306 if((dwFlags & URL_FILE_USE_PATHURL) && nByteLen >= sizeof(wszFile)
307 && !memcmp(wszFile, pszUrl, sizeof(wszFile)))
310 if(nByteLen >= sizeof(wszRes) && !memcmp(wszRes, pszUrl, sizeof(wszRes))) {
311 dwFlags &= ~URL_FILE_USE_PATHURL;
318 * 1 have 2[+] alnum 2,3
319 * 2 have scheme (found :) 4,6,3
320 * 3 failed (no location)
322 * 5 have 1[+] alnum 6,3
323 * 6 have location (found /) save root location
330 if(pszUrl[1] == ':') { /* Assume path */
331 static const WCHAR wszFilePrefix[] = {'f','i','l','e',':','/','/','/'};
333 memcpy(wk2, wszFilePrefix, sizeof(wszFilePrefix));
334 wk2 += sizeof(wszFilePrefix)/sizeof(WCHAR);
335 if (dwFlags & URL_FILE_USE_PATHURL)
341 dwFlags |= URL_ESCAPE_UNSAFE;
348 if (!isalnumW(*wk1)) {state = 3; break;}
350 if (!isalnumW(*wk1)) {state = 3; break;}
356 if (*wk1++ == ':') state = 2;
360 if (*wk1 != '/') {state = 6; break;}
362 if((dwFlags & URL_FILE_USE_PATHURL) && nByteLen >= sizeof(wszLocalhost)
363 && !strncmpW(wszFile, pszUrl, sizeof(wszFile)/sizeof(WCHAR))
364 && !memcmp(wszLocalhost, wk1, sizeof(wszLocalhost))){
365 wk1 += sizeof(wszLocalhost)/sizeof(WCHAR);
366 while(*wk1 == '\\' && (dwFlags & URL_FILE_USE_PATHURL))
369 if(*wk1 == '/' && (dwFlags & URL_FILE_USE_PATHURL))
374 nWkLen = strlenW(wk1);
375 memcpy(wk2, wk1, (nWkLen + 1) * sizeof(WCHAR));
382 if(*mp == '/' || *mp == '\\')
389 if (!isalnumW(*wk1) && (*wk1 != '-') && (*wk1 != '.') && (*wk1 != ':'))
391 while(isalnumW(*wk1) || (*wk1 == '-') || (*wk1 == '.') || (*wk1 == ':'))
402 if (*wk1 != '/' && *wk1 != '\\') {state = 3; break;}
403 while(*wk1 == '/' || *wk1 == '\\') {
413 if(dwFlags & URL_DONT_SIMPLIFY) {
418 /* Now at root location, cannot back up any more. */
419 /* "root" will point at the '/' */
423 mp = strchrW(wk1, '/');
424 mp2 = strchrW(wk1, '\\');
425 if(mp2 && (!mp || mp2 < mp))
428 nWkLen = strlenW(wk1);
429 memcpy(wk2, wk1, (nWkLen + 1) * sizeof(WCHAR));
436 memcpy(wk2, wk1, nLen * sizeof(WCHAR));
447 TRACE("found '/.'\n");
448 if (wk1[1] == '/' || wk1[1] == '\\') {
449 /* case of /./ -> skip the ./ */
452 else if (wk1[1] == '.') {
453 /* found /.. look for next / */
454 TRACE("found '/..'\n");
455 if (wk1[2] == '/' || wk1[2] == '\\' ||wk1[2] == '?'
456 || wk1[2] == '#' || !wk1[2]) {
457 /* case /../ -> need to backup wk2 */
458 TRACE("found '/../'\n");
459 *(wk2-1) = '\0'; /* set end of string */
460 mp = strrchrW(root, '/');
461 mp2 = strrchrW(root, '\\');
462 if(mp2 && (!mp || mp2 < mp))
464 if (mp && (mp >= root)) {
465 /* found valid backup point */
467 if(wk1[2] != '/' && wk1[2] != '\\')
473 /* did not find point, restore '/' */
483 FIXME("how did we get here - state=%d\n", state);
484 HeapFree(GetProcessHeap(), 0, lpszUrlCpy);
488 TRACE("Simplified, orig <%s>, simple <%s>\n",
489 debugstr_w(pszUrl), debugstr_w(lpszUrlCpy));
491 nLen = lstrlenW(lpszUrlCpy);
492 while ((nLen > 0) && ((lpszUrlCpy[nLen-1] <= ' ')))
493 lpszUrlCpy[--nLen]=0;
495 if(dwFlags & (URL_UNESCAPE | URL_FILE_USE_PATHURL))
496 UrlUnescapeW(lpszUrlCpy, NULL, &nLen, URL_UNESCAPE_INPLACE);
498 if((EscapeFlags = dwFlags & (URL_ESCAPE_UNSAFE |
499 URL_ESCAPE_SPACES_ONLY |
501 URL_DONT_ESCAPE_EXTRA_INFO |
502 URL_ESCAPE_SEGMENT_ONLY ))) {
503 EscapeFlags &= ~URL_ESCAPE_UNSAFE;
504 hr = UrlEscapeW(lpszUrlCpy, pszCanonicalized, pcchCanonicalized,
506 } else { /* No escaping needed, just copy the string */
507 nLen = lstrlenW(lpszUrlCpy);
508 if(nLen < *pcchCanonicalized)
509 memcpy(pszCanonicalized, lpszUrlCpy, (nLen + 1)*sizeof(WCHAR));
514 *pcchCanonicalized = nLen;
517 HeapFree(GetProcessHeap(), 0, lpszUrlCpy);
520 TRACE("result %s\n", debugstr_w(pszCanonicalized));
525 /*************************************************************************
526 * UrlCombineA [SHLWAPI.@]
531 * pszBase [I] Base Url
532 * pszRelative [I] Url to combine with pszBase
533 * pszCombined [O] Destination for combined Url
534 * pcchCombined [O] Destination for length of pszCombined
535 * dwFlags [I] URL_ flags from "shlwapi.h"
538 * Success: S_OK. pszCombined contains the combined Url, pcchCombined
539 * contains its length.
540 * Failure: An HRESULT error code indicating the error.
542 HRESULT WINAPI UrlCombineA(LPCSTR pszBase, LPCSTR pszRelative,
543 LPSTR pszCombined, LPDWORD pcchCombined,
546 LPWSTR base, relative, combined;
547 DWORD ret, len, len2;
549 TRACE("(base %s, Relative %s, Combine size %d, flags %08x) using W version\n",
550 debugstr_a(pszBase),debugstr_a(pszRelative),
551 pcchCombined?*pcchCombined:0,dwFlags);
553 if(!pszBase || !pszRelative || !pcchCombined)
556 base = HeapAlloc(GetProcessHeap(), 0,
557 (3*INTERNET_MAX_URL_LENGTH) * sizeof(WCHAR));
558 relative = base + INTERNET_MAX_URL_LENGTH;
559 combined = relative + INTERNET_MAX_URL_LENGTH;
561 MultiByteToWideChar(0, 0, pszBase, -1, base, INTERNET_MAX_URL_LENGTH);
562 MultiByteToWideChar(0, 0, pszRelative, -1, relative, INTERNET_MAX_URL_LENGTH);
565 ret = UrlCombineW(base, relative, pszCombined?combined:NULL, &len, dwFlags);
568 HeapFree(GetProcessHeap(), 0, base);
572 len2 = WideCharToMultiByte(0, 0, combined, len, 0, 0, 0, 0);
573 if (len2 > *pcchCombined) {
574 *pcchCombined = len2;
575 HeapFree(GetProcessHeap(), 0, base);
578 WideCharToMultiByte(0, 0, combined, len+1, pszCombined, (*pcchCombined)+1,
580 *pcchCombined = len2;
581 HeapFree(GetProcessHeap(), 0, base);
585 /*************************************************************************
586 * UrlCombineW [SHLWAPI.@]
590 HRESULT WINAPI UrlCombineW(LPCWSTR pszBase, LPCWSTR pszRelative,
591 LPWSTR pszCombined, LPDWORD pcchCombined,
594 PARSEDURLW base, relative;
595 DWORD myflags, sizeloc = 0;
596 DWORD len, res1, res2, process_case = 0;
597 LPWSTR work, preliminary, mbase, mrelative;
598 static const WCHAR myfilestr[] = {'f','i','l','e',':','/','/','/','\0'};
601 TRACE("(base %s, Relative %s, Combine size %d, flags %08x)\n",
602 debugstr_w(pszBase),debugstr_w(pszRelative),
603 pcchCombined?*pcchCombined:0,dwFlags);
605 if(!pszBase || !pszRelative || !pcchCombined)
608 base.cbSize = sizeof(base);
609 relative.cbSize = sizeof(relative);
611 /* Get space for duplicates of the input and the output */
612 preliminary = HeapAlloc(GetProcessHeap(), 0, (3*INTERNET_MAX_URL_LENGTH) *
614 mbase = preliminary + INTERNET_MAX_URL_LENGTH;
615 mrelative = mbase + INTERNET_MAX_URL_LENGTH;
618 /* Canonicalize the base input prior to looking for the scheme */
619 myflags = dwFlags & (URL_DONT_SIMPLIFY | URL_UNESCAPE);
620 len = INTERNET_MAX_URL_LENGTH;
621 ret = UrlCanonicalizeW(pszBase, mbase, &len, myflags);
623 /* Canonicalize the relative input prior to looking for the scheme */
624 len = INTERNET_MAX_URL_LENGTH;
625 ret = UrlCanonicalizeW(pszRelative, mrelative, &len, myflags);
627 /* See if the base has a scheme */
628 res1 = ParseURLW(mbase, &base);
630 /* if pszBase has no scheme, then return pszRelative */
631 TRACE("no scheme detected in Base\n");
635 BOOL manual_search = FALSE;
637 /* mk is a special case */
638 if(base.nScheme == URL_SCHEME_MK) {
639 static const WCHAR wsz[] = {':',':',0};
641 WCHAR *ptr = strstrW(base.pszSuffix, wsz);
646 delta = ptr-base.pszSuffix;
647 base.cchProtocol += delta;
648 base.pszSuffix += delta;
649 base.cchSuffix -= delta;
652 /* get size of location field (if it exists) */
653 work = (LPWSTR)base.pszSuffix;
655 if (*work++ == '/') {
656 if (*work++ == '/') {
657 /* At this point have start of location and
658 * it ends at next '/' or end of string.
660 while(*work && (*work != '/')) work++;
661 sizeloc = (DWORD)(work - base.pszSuffix);
666 /* If there is a '#' and the characters immediately preceding it are
667 * ".htm[l]", then begin looking for the last leaf starting from
668 * the '#'. Otherwise the '#' is not meaningful and just start
669 * looking from the end. */
670 if ((work = strchrW(base.pszSuffix + sizeloc, '#'))) {
671 const WCHAR htmlW[] = {'.','h','t','m','l',0};
672 const int len_htmlW = 5;
673 const WCHAR htmW[] = {'.','h','t','m',0};
674 const int len_htmW = 4;
676 if (work - base.pszSuffix > len_htmW * sizeof(WCHAR)) {
678 if (strncmpiW(work, htmW, len_htmW) == 0)
679 manual_search = TRUE;
683 if (!manual_search &&
684 work - base.pszSuffix > len_htmlW * sizeof(WCHAR)) {
686 if (strncmpiW(work, htmlW, len_htmlW) == 0)
687 manual_search = TRUE;
693 /* search backwards starting from the current position */
694 while (*work != '/' && work > base.pszSuffix + sizeloc)
696 if (work > base.pszSuffix + sizeloc)
697 base.cchSuffix = work - base.pszSuffix + 1;
699 /* search backwards starting from the end of the string */
700 work = strrchrW((base.pszSuffix+sizeloc), '/');
702 len = (DWORD)(work - base.pszSuffix + 1);
703 base.cchSuffix = len;
709 * .pszSuffix points to location (starting with '//')
710 * .cchSuffix length of location (above) and rest less the last
712 * sizeloc length of location (above) up to but not including
716 res2 = ParseURLW(mrelative, &relative);
718 /* no scheme in pszRelative */
719 TRACE("no scheme detected in Relative\n");
720 relative.pszSuffix = mrelative; /* case 3,4,5 depends on this */
721 relative.cchSuffix = strlenW(mrelative);
722 if (*pszRelative == ':') {
723 /* case that is either left alone or uses pszBase */
724 if (dwFlags & URL_PLUGGABLE_PROTOCOL) {
731 if (isalnum(*mrelative) && (*(mrelative + 1) == ':')) {
732 /* case that becomes "file:///" */
733 strcpyW(preliminary, myfilestr);
737 if ((*mrelative == '/') && (*(mrelative+1) == '/')) {
738 /* pszRelative has location and rest */
742 if (*mrelative == '/') {
743 /* case where pszRelative is root to location */
747 process_case = (*base.pszSuffix == '/' || base.nScheme == URL_SCHEME_MK) ? 5 : 3;
751 /* handle cases where pszRelative has scheme */
752 if ((base.cchProtocol == relative.cchProtocol) &&
753 (strncmpW(base.pszProtocol, relative.pszProtocol, base.cchProtocol) == 0)) {
755 /* since the schemes are the same */
756 if ((*relative.pszSuffix == '/') && (*(relative.pszSuffix+1) == '/')) {
757 /* case where pszRelative replaces location and following */
761 if (*relative.pszSuffix == '/') {
762 /* case where pszRelative is root to location */
766 /* replace either just location if base's location starts with a
767 * slash or otherwise everything */
768 process_case = (*base.pszSuffix == '/') ? 5 : 1;
771 if ((*relative.pszSuffix == '/') && (*(relative.pszSuffix+1) == '/')) {
772 /* case where pszRelative replaces scheme, location,
773 * and following and handles PLUGGABLE
780 } while(FALSE); /* a little trick to allow easy exit from nested if's */
783 switch (process_case) {
786 * Return pszRelative appended to what ever is in pszCombined,
787 * (which may the string "file:///"
789 strcatW(preliminary, mrelative);
792 case 2: /* case where pszRelative replaces scheme, and location */
793 strcpyW(preliminary, mrelative);
797 * Return the pszBase scheme with pszRelative. Basically
798 * keeps the scheme and replaces the domain and following.
800 memcpy(preliminary, base.pszProtocol, (base.cchProtocol + 1)*sizeof(WCHAR));
801 work = preliminary + base.cchProtocol + 1;
802 strcpyW(work, relative.pszSuffix);
806 * Return the pszBase scheme and location but everything
807 * after the location is pszRelative. (Replace document
810 memcpy(preliminary, base.pszProtocol, (base.cchProtocol+1+sizeloc)*sizeof(WCHAR));
811 work = preliminary + base.cchProtocol + 1 + sizeloc;
812 if (dwFlags & URL_PLUGGABLE_PROTOCOL)
814 strcpyW(work, relative.pszSuffix);
818 * Return the pszBase without its document (if any) and
819 * append pszRelative after its scheme.
821 memcpy(preliminary, base.pszProtocol,
822 (base.cchProtocol+1+base.cchSuffix)*sizeof(WCHAR));
823 work = preliminary + base.cchProtocol+1+base.cchSuffix - 1;
826 strcpyW(work, relative.pszSuffix);
830 FIXME("How did we get here????? process_case=%d\n", process_case);
835 /* Reuse mrelative as temp storage as its already allocated and not needed anymore */
836 ret = UrlCanonicalizeW(preliminary, mrelative, pcchCombined, (dwFlags & ~URL_FILE_USE_PATHURL));
837 if(SUCCEEDED(ret) && pszCombined) {
838 lstrcpyW(pszCombined, mrelative);
840 TRACE("return-%d len=%d, %s\n",
841 process_case, *pcchCombined, debugstr_w(pszCombined));
843 HeapFree(GetProcessHeap(), 0, preliminary);
847 /*************************************************************************
848 * UrlEscapeA [SHLWAPI.@]
851 HRESULT WINAPI UrlEscapeA(
857 WCHAR bufW[INTERNET_MAX_URL_LENGTH];
858 WCHAR *escapedW = bufW;
861 DWORD lenW = sizeof(bufW)/sizeof(WCHAR), lenA;
863 if (!pszEscaped || !pcchEscaped || !*pcchEscaped)
866 if(!RtlCreateUnicodeStringFromAsciiz(&urlW, pszUrl))
868 if((ret = UrlEscapeW(urlW.Buffer, escapedW, &lenW, dwFlags)) == E_POINTER) {
869 escapedW = HeapAlloc(GetProcessHeap(), 0, lenW * sizeof(WCHAR));
870 ret = UrlEscapeW(urlW.Buffer, escapedW, &lenW, dwFlags);
873 RtlUnicodeToMultiByteSize(&lenA, escapedW, lenW * sizeof(WCHAR));
874 if(*pcchEscaped > lenA) {
875 RtlUnicodeToMultiByteN(pszEscaped, *pcchEscaped - 1, &lenA, escapedW, lenW * sizeof(WCHAR));
876 pszEscaped[lenA] = 0;
879 *pcchEscaped = lenA + 1;
883 if(escapedW != bufW) HeapFree(GetProcessHeap(), 0, escapedW);
884 RtlFreeUnicodeString(&urlW);
888 #define WINE_URL_BASH_AS_SLASH 0x01
889 #define WINE_URL_COLLAPSE_SLASHES 0x02
890 #define WINE_URL_ESCAPE_SLASH 0x04
891 #define WINE_URL_ESCAPE_HASH 0x08
892 #define WINE_URL_ESCAPE_QUESTION 0x10
893 #define WINE_URL_STOP_ON_HASH 0x20
894 #define WINE_URL_STOP_ON_QUESTION 0x40
896 static inline BOOL URL_NeedEscapeW(WCHAR ch, DWORD dwFlags, DWORD int_flags)
902 if(dwFlags & URL_ESCAPE_SPACES_ONLY) {
909 if ((dwFlags & URL_ESCAPE_PERCENT) && (ch == '%'))
912 if (ch <= 31 || ch >= 127)
933 if (int_flags & WINE_URL_ESCAPE_SLASH) return TRUE;
937 if (int_flags & WINE_URL_ESCAPE_QUESTION) return TRUE;
941 if (int_flags & WINE_URL_ESCAPE_HASH) return TRUE;
951 /*************************************************************************
952 * UrlEscapeW [SHLWAPI.@]
954 * Converts unsafe characters in a Url into escape sequences.
957 * pszUrl [I] Url to modify
958 * pszEscaped [O] Destination for modified Url
959 * pcchEscaped [I/O] Length of pszUrl, destination for length of pszEscaped
960 * dwFlags [I] URL_ flags from "shlwapi.h"
963 * Success: S_OK. pszEscaped contains the escaped Url, pcchEscaped
964 * contains its length.
965 * Failure: E_POINTER, if pszEscaped is not large enough. In this case
966 * pcchEscaped is set to the required length.
968 * Converts unsafe characters into their escape sequences.
971 * - By default this function stops converting at the first '?' or
973 * - If dwFlags contains URL_ESCAPE_SPACES_ONLY then only spaces are
974 * converted, but the conversion continues past a '?' or '#'.
975 * - Note that this function did not work well (or at all) in shlwapi version 4.
978 * Only the following flags are implemented:
979 *| URL_ESCAPE_SPACES_ONLY
980 *| URL_DONT_ESCAPE_EXTRA_INFO
981 *| URL_ESCAPE_SEGMENT_ONLY
982 *| URL_ESCAPE_PERCENT
984 HRESULT WINAPI UrlEscapeW(
991 DWORD needed = 0, ret;
992 BOOL stop_escaping = FALSE;
993 WCHAR next[5], *dst = pszEscaped;
995 PARSEDURLW parsed_url;
998 static const WCHAR localhost[] = {'l','o','c','a','l','h','o','s','t',0};
1000 TRACE("(%s %p %p 0x%08x)\n", debugstr_w(pszUrl), pszEscaped,
1001 pcchEscaped, dwFlags);
1003 if(!pszUrl || !pcchEscaped)
1004 return E_INVALIDARG;
1006 if(dwFlags & ~(URL_ESCAPE_SPACES_ONLY |
1007 URL_ESCAPE_SEGMENT_ONLY |
1008 URL_DONT_ESCAPE_EXTRA_INFO |
1009 URL_ESCAPE_PERCENT))
1010 FIXME("Unimplemented flags: %08x\n", dwFlags);
1013 if (dwFlags & URL_ESCAPE_SPACES_ONLY)
1014 /* if SPACES_ONLY specified, reset the other controls */
1015 dwFlags &= ~(URL_DONT_ESCAPE_EXTRA_INFO |
1016 URL_ESCAPE_PERCENT |
1017 URL_ESCAPE_SEGMENT_ONLY);
1020 /* if SPACES_ONLY *not* specified the assume DONT_ESCAPE_EXTRA_INFO */
1021 dwFlags |= URL_DONT_ESCAPE_EXTRA_INFO;
1025 if(dwFlags & URL_ESCAPE_SEGMENT_ONLY) {
1026 int_flags = WINE_URL_ESCAPE_QUESTION | WINE_URL_ESCAPE_HASH | WINE_URL_ESCAPE_SLASH;
1028 parsed_url.cbSize = sizeof(parsed_url);
1029 if(ParseURLW(pszUrl, &parsed_url) != S_OK)
1030 parsed_url.nScheme = URL_SCHEME_INVALID;
1032 TRACE("scheme = %d (%s)\n", parsed_url.nScheme, debugstr_wn(parsed_url.pszProtocol, parsed_url.cchProtocol));
1034 if(dwFlags & URL_DONT_ESCAPE_EXTRA_INFO)
1035 int_flags = WINE_URL_STOP_ON_HASH | WINE_URL_STOP_ON_QUESTION;
1037 switch(parsed_url.nScheme) {
1038 case URL_SCHEME_FILE:
1039 int_flags |= WINE_URL_BASH_AS_SLASH | WINE_URL_COLLAPSE_SLASHES | WINE_URL_ESCAPE_HASH;
1040 int_flags &= ~WINE_URL_STOP_ON_HASH;
1043 case URL_SCHEME_HTTP:
1044 case URL_SCHEME_HTTPS:
1045 int_flags |= WINE_URL_BASH_AS_SLASH;
1046 if(parsed_url.pszSuffix[0] != '/' && parsed_url.pszSuffix[0] != '\\')
1047 int_flags |= WINE_URL_ESCAPE_SLASH;
1050 case URL_SCHEME_MAILTO:
1051 int_flags |= WINE_URL_ESCAPE_SLASH | WINE_URL_ESCAPE_QUESTION | WINE_URL_ESCAPE_HASH;
1052 int_flags &= ~(WINE_URL_STOP_ON_QUESTION | WINE_URL_STOP_ON_HASH);
1055 case URL_SCHEME_INVALID:
1058 case URL_SCHEME_FTP:
1060 if(parsed_url.pszSuffix[0] != '/')
1061 int_flags |= WINE_URL_ESCAPE_SLASH;
1066 for(src = pszUrl; *src; ) {
1070 if((int_flags & WINE_URL_COLLAPSE_SLASHES) && src == pszUrl + parsed_url.cchProtocol + 1) {
1071 int localhost_len = sizeof(localhost)/sizeof(WCHAR) - 1;
1072 while(cur == '/' || cur == '\\') {
1076 if(slashes == 2 && !strncmpiW(src, localhost, localhost_len)) { /* file://localhost/ -> file:/// */
1077 if(*(src + localhost_len) == '/' || *(src + localhost_len) == '\\')
1078 src += localhost_len + 1;
1085 next[0] = next[1] = next[2] = '/';
1092 next[0] = next[1] = '/';
1099 if(cur == '#' && (int_flags & WINE_URL_STOP_ON_HASH))
1100 stop_escaping = TRUE;
1102 if(cur == '?' && (int_flags & WINE_URL_STOP_ON_QUESTION))
1103 stop_escaping = TRUE;
1105 if(cur == '\\' && (int_flags & WINE_URL_BASH_AS_SLASH) && !stop_escaping) cur = '/';
1107 if(URL_NeedEscapeW(cur, dwFlags, int_flags) && stop_escaping == FALSE) {
1109 next[1] = hexDigits[(cur >> 4) & 0xf];
1110 next[2] = hexDigits[cur & 0xf];
1119 if(needed + len <= *pcchEscaped) {
1120 memcpy(dst, next, len*sizeof(WCHAR));
1126 if(needed < *pcchEscaped) {
1130 needed++; /* add one for the '\0' */
1133 *pcchEscaped = needed;
1138 /*************************************************************************
1139 * UrlUnescapeA [SHLWAPI.@]
1141 * Converts Url escape sequences back to ordinary characters.
1144 * pszUrl [I/O] Url to convert
1145 * pszUnescaped [O] Destination for converted Url
1146 * pcchUnescaped [I/O] Size of output string
1147 * dwFlags [I] URL_ESCAPE_ Flags from "shlwapi.h"
1150 * Success: S_OK. The converted value is in pszUnescaped, or in pszUrl if
1151 * dwFlags includes URL_ESCAPE_INPLACE.
1152 * Failure: E_POINTER if the converted Url is bigger than pcchUnescaped. In
1153 * this case pcchUnescaped is set to the size required.
1155 * If dwFlags includes URL_DONT_ESCAPE_EXTRA_INFO, the conversion stops at
1156 * the first occurrence of either a '?' or '#' character.
1158 HRESULT WINAPI UrlUnescapeA(
1161 LPDWORD pcchUnescaped,
1168 BOOL stop_unescaping = FALSE;
1170 TRACE("(%s, %p, %p, 0x%08x)\n", debugstr_a(pszUrl), pszUnescaped,
1171 pcchUnescaped, dwFlags);
1173 if (!pszUrl) return E_INVALIDARG;
1175 if(dwFlags & URL_UNESCAPE_INPLACE)
1179 if (!pszUnescaped || !pcchUnescaped) return E_INVALIDARG;
1183 for(src = pszUrl, needed = 0; *src; src++, needed++) {
1184 if(dwFlags & URL_DONT_UNESCAPE_EXTRA_INFO &&
1185 (*src == '#' || *src == '?')) {
1186 stop_unescaping = TRUE;
1188 } else if(*src == '%' && isxdigit(*(src + 1)) && isxdigit(*(src + 2))
1189 && stop_unescaping == FALSE) {
1192 memcpy(buf, src + 1, 2);
1194 ih = strtol(buf, NULL, 16);
1196 src += 2; /* Advance to end of escape */
1200 if(dwFlags & URL_UNESCAPE_INPLACE || needed < *pcchUnescaped)
1204 if(dwFlags & URL_UNESCAPE_INPLACE || needed < *pcchUnescaped) {
1208 needed++; /* add one for the '\0' */
1211 if(!(dwFlags & URL_UNESCAPE_INPLACE))
1212 *pcchUnescaped = needed;
1215 TRACE("result %s\n", (dwFlags & URL_UNESCAPE_INPLACE) ?
1216 debugstr_a(pszUrl) : debugstr_a(pszUnescaped));
1222 /*************************************************************************
1223 * UrlUnescapeW [SHLWAPI.@]
1227 HRESULT WINAPI UrlUnescapeW(
1229 LPWSTR pszUnescaped,
1230 LPDWORD pcchUnescaped,
1237 BOOL stop_unescaping = FALSE;
1239 TRACE("(%s, %p, %p, 0x%08x)\n", debugstr_w(pszUrl), pszUnescaped,
1240 pcchUnescaped, dwFlags);
1242 if(!pszUrl) return E_INVALIDARG;
1244 if(dwFlags & URL_UNESCAPE_INPLACE)
1248 if (!pszUnescaped || !pcchUnescaped) return E_INVALIDARG;
1252 for(src = pszUrl, needed = 0; *src; src++, needed++) {
1253 if(dwFlags & URL_DONT_UNESCAPE_EXTRA_INFO &&
1254 (*src == '#' || *src == '?')) {
1255 stop_unescaping = TRUE;
1257 } else if(*src == '%' && isxdigitW(*(src + 1)) && isxdigitW(*(src + 2))
1258 && stop_unescaping == FALSE) {
1260 WCHAR buf[5] = {'0','x',0};
1261 memcpy(buf + 2, src + 1, 2*sizeof(WCHAR));
1263 StrToIntExW(buf, STIF_SUPPORT_HEX, &ih);
1265 src += 2; /* Advance to end of escape */
1269 if(dwFlags & URL_UNESCAPE_INPLACE || needed < *pcchUnescaped)
1273 if(dwFlags & URL_UNESCAPE_INPLACE || needed < *pcchUnescaped) {
1277 needed++; /* add one for the '\0' */
1280 if(!(dwFlags & URL_UNESCAPE_INPLACE))
1281 *pcchUnescaped = needed;
1284 TRACE("result %s\n", (dwFlags & URL_UNESCAPE_INPLACE) ?
1285 debugstr_w(pszUrl) : debugstr_w(pszUnescaped));
1291 /*************************************************************************
1292 * UrlGetLocationA [SHLWAPI.@]
1294 * Get the location from a Url.
1297 * pszUrl [I] Url to get the location from
1300 * A pointer to the start of the location in pszUrl, or NULL if there is
1304 * - MSDN erroneously states that "The location is the segment of the Url
1305 * starting with a '?' or '#' character". Neither V4 nor V5 of shlwapi.dll
1306 * stop at '?' and always return a NULL in this case.
1307 * - MSDN also erroneously states that "If a file URL has a query string,
1308 * the returned string is the query string". In all tested cases, if the
1309 * Url starts with "fi" then a NULL is returned. V5 gives the following results:
1312 *| NULL file://aa/b/cd#hohoh
1313 *| #hohoh http://aa/b/cd#hohoh
1314 *| NULL fi://aa/b/cd#hohoh
1315 *| #hohoh ff://aa/b/cd#hohoh
1317 LPCSTR WINAPI UrlGetLocationA(
1323 base.cbSize = sizeof(base);
1324 res1 = ParseURLA(pszUrl, &base);
1325 if (res1) return NULL; /* invalid scheme */
1327 /* if scheme is file: then never return pointer */
1328 if (strncmp(base.pszProtocol, "file", min(4,base.cchProtocol)) == 0) return NULL;
1330 /* Look for '#' and return its addr */
1331 return strchr(base.pszSuffix, '#');
1334 /*************************************************************************
1335 * UrlGetLocationW [SHLWAPI.@]
1337 * See UrlGetLocationA.
1339 LPCWSTR WINAPI UrlGetLocationW(
1345 base.cbSize = sizeof(base);
1346 res1 = ParseURLW(pszUrl, &base);
1347 if (res1) return NULL; /* invalid scheme */
1349 /* if scheme is file: then never return pointer */
1350 if (strncmpW(base.pszProtocol, fileW, min(4,base.cchProtocol)) == 0) return NULL;
1352 /* Look for '#' and return its addr */
1353 return strchrW(base.pszSuffix, '#');
1356 /*************************************************************************
1357 * UrlCompareA [SHLWAPI.@]
1362 * pszUrl1 [I] First Url to compare
1363 * pszUrl2 [I] Url to compare to pszUrl1
1364 * fIgnoreSlash [I] TRUE = compare only up to a final slash
1367 * less than zero, zero, or greater than zero indicating pszUrl2 is greater
1368 * than, equal to, or less than pszUrl1 respectively.
1370 INT WINAPI UrlCompareA(
1375 INT ret, len, len1, len2;
1378 return strcmp(pszUrl1, pszUrl2);
1379 len1 = strlen(pszUrl1);
1380 if (pszUrl1[len1-1] == '/') len1--;
1381 len2 = strlen(pszUrl2);
1382 if (pszUrl2[len2-1] == '/') len2--;
1384 return strncmp(pszUrl1, pszUrl2, len1);
1385 len = min(len1, len2);
1386 ret = strncmp(pszUrl1, pszUrl2, len);
1387 if (ret) return ret;
1388 if (len1 > len2) return 1;
1392 /*************************************************************************
1393 * UrlCompareW [SHLWAPI.@]
1397 INT WINAPI UrlCompareW(
1403 size_t len, len1, len2;
1406 return strcmpW(pszUrl1, pszUrl2);
1407 len1 = strlenW(pszUrl1);
1408 if (pszUrl1[len1-1] == '/') len1--;
1409 len2 = strlenW(pszUrl2);
1410 if (pszUrl2[len2-1] == '/') len2--;
1412 return strncmpW(pszUrl1, pszUrl2, len1);
1413 len = min(len1, len2);
1414 ret = strncmpW(pszUrl1, pszUrl2, len);
1415 if (ret) return ret;
1416 if (len1 > len2) return 1;
1420 /*************************************************************************
1421 * HashData [SHLWAPI.@]
1423 * Hash an input block into a variable sized digest.
1426 * lpSrc [I] Input block
1427 * nSrcLen [I] Length of lpSrc
1428 * lpDest [I] Output for hash digest
1429 * nDestLen [I] Length of lpDest
1432 * Success: TRUE. lpDest is filled with the computed hash value.
1433 * Failure: FALSE, if any argument is invalid.
1435 HRESULT WINAPI HashData(const unsigned char *lpSrc, DWORD nSrcLen,
1436 unsigned char *lpDest, DWORD nDestLen)
1438 INT srcCount = nSrcLen - 1, destCount = nDestLen - 1;
1440 if (!lpSrc || !lpDest)
1441 return E_INVALIDARG;
1443 while (destCount >= 0)
1445 lpDest[destCount] = (destCount & 0xff);
1449 while (srcCount >= 0)
1451 destCount = nDestLen - 1;
1452 while (destCount >= 0)
1454 lpDest[destCount] = HashDataLookup[lpSrc[srcCount] ^ lpDest[destCount]];
1462 /*************************************************************************
1463 * UrlHashA [SHLWAPI.@]
1465 * Produce a Hash from a Url.
1468 * pszUrl [I] Url to hash
1469 * lpDest [O] Destinationh for hash
1470 * nDestLen [I] Length of lpDest
1473 * Success: S_OK. lpDest is filled with the computed hash value.
1474 * Failure: E_INVALIDARG, if any argument is invalid.
1476 HRESULT WINAPI UrlHashA(LPCSTR pszUrl, unsigned char *lpDest, DWORD nDestLen)
1478 if (IsBadStringPtrA(pszUrl, -1) || IsBadWritePtr(lpDest, nDestLen))
1479 return E_INVALIDARG;
1481 HashData((const BYTE*)pszUrl, (int)strlen(pszUrl), lpDest, nDestLen);
1485 /*************************************************************************
1486 * UrlHashW [SHLWAPI.@]
1490 HRESULT WINAPI UrlHashW(LPCWSTR pszUrl, unsigned char *lpDest, DWORD nDestLen)
1492 char szUrl[MAX_PATH];
1494 TRACE("(%s,%p,%d)\n",debugstr_w(pszUrl), lpDest, nDestLen);
1496 if (IsBadStringPtrW(pszUrl, -1) || IsBadWritePtr(lpDest, nDestLen))
1497 return E_INVALIDARG;
1499 /* Win32 hashes the data as an ASCII string, presumably so that both A+W
1500 * return the same digests for the same URL.
1502 WideCharToMultiByte(0, 0, pszUrl, -1, szUrl, MAX_PATH, 0, 0);
1503 HashData((const BYTE*)szUrl, (int)strlen(szUrl), lpDest, nDestLen);
1507 /*************************************************************************
1508 * UrlApplySchemeA [SHLWAPI.@]
1510 * Apply a scheme to a Url.
1513 * pszIn [I] Url to apply scheme to
1514 * pszOut [O] Destination for modified Url
1515 * pcchOut [I/O] Length of pszOut/destination for length of pszOut
1516 * dwFlags [I] URL_ flags from "shlwapi.h"
1519 * Success: S_OK: pszOut contains the modified Url, pcchOut contains its length.
1520 * Failure: An HRESULT error code describing the error.
1522 HRESULT WINAPI UrlApplySchemeA(LPCSTR pszIn, LPSTR pszOut, LPDWORD pcchOut, DWORD dwFlags)
1528 TRACE("(%s, %p, %p:out size %d, 0x%08x)\n", debugstr_a(pszIn),
1529 pszOut, pcchOut, pcchOut ? *pcchOut : 0, dwFlags);
1531 if (!pszIn || !pszOut || !pcchOut) return E_INVALIDARG;
1533 in = HeapAlloc(GetProcessHeap(), 0,
1534 (2*INTERNET_MAX_URL_LENGTH) * sizeof(WCHAR));
1535 out = in + INTERNET_MAX_URL_LENGTH;
1537 MultiByteToWideChar(CP_ACP, 0, pszIn, -1, in, INTERNET_MAX_URL_LENGTH);
1538 len = INTERNET_MAX_URL_LENGTH;
1540 ret = UrlApplySchemeW(in, out, &len, dwFlags);
1542 HeapFree(GetProcessHeap(), 0, in);
1546 len = WideCharToMultiByte(CP_ACP, 0, out, -1, NULL, 0, NULL, NULL);
1547 if (len > *pcchOut) {
1552 WideCharToMultiByte(CP_ACP, 0, out, -1, pszOut, *pcchOut, NULL, NULL);
1557 HeapFree(GetProcessHeap(), 0, in);
1561 static HRESULT URL_GuessScheme(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut)
1566 DWORD value_len, data_len, dwType, i;
1567 WCHAR reg_path[MAX_PATH];
1568 WCHAR value[MAX_PATH], data[MAX_PATH];
1571 MultiByteToWideChar(0, 0,
1572 "Software\\Microsoft\\Windows\\CurrentVersion\\URL\\Prefixes",
1573 -1, reg_path, MAX_PATH);
1574 RegOpenKeyExW(HKEY_LOCAL_MACHINE, reg_path, 0, 1, &newkey);
1576 while(value_len = data_len = MAX_PATH,
1577 RegEnumValueW(newkey, index, value, &value_len,
1578 0, &dwType, (LPVOID)data, &data_len) == 0) {
1579 TRACE("guess %d %s is %s\n",
1580 index, debugstr_w(value), debugstr_w(data));
1583 for(i=0; i<value_len; i++) {
1586 /* remember that TRUE is not-equal */
1587 j = ChrCmpIW(Wxx, Wyy);
1590 if ((i == value_len) && !j) {
1591 if (strlenW(data) + strlenW(pszIn) + 1 > *pcchOut) {
1592 *pcchOut = strlenW(data) + strlenW(pszIn) + 1;
1593 RegCloseKey(newkey);
1596 strcpyW(pszOut, data);
1597 strcatW(pszOut, pszIn);
1598 *pcchOut = strlenW(pszOut);
1599 TRACE("matched and set to %s\n", debugstr_w(pszOut));
1600 RegCloseKey(newkey);
1605 RegCloseKey(newkey);
1609 static HRESULT URL_ApplyDefault(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut)
1612 DWORD data_len, dwType;
1613 WCHAR data[MAX_PATH];
1615 static const WCHAR prefix_keyW[] =
1616 {'S','o','f','t','w','a','r','e',
1617 '\\','M','i','c','r','o','s','o','f','t',
1618 '\\','W','i','n','d','o','w','s',
1619 '\\','C','u','r','r','e','n','t','V','e','r','s','i','o','n',
1621 '\\','D','e','f','a','u','l','t','P','r','e','f','i','x',0};
1623 /* get and prepend default */
1624 RegOpenKeyExW(HKEY_LOCAL_MACHINE, prefix_keyW, 0, 1, &newkey);
1625 data_len = sizeof(data);
1626 RegQueryValueExW(newkey, NULL, 0, &dwType, (LPBYTE)data, &data_len);
1627 RegCloseKey(newkey);
1628 if (strlenW(data) + strlenW(pszIn) + 1 > *pcchOut) {
1629 *pcchOut = strlenW(data) + strlenW(pszIn) + 1;
1632 strcpyW(pszOut, data);
1633 strcatW(pszOut, pszIn);
1634 *pcchOut = strlenW(pszOut);
1635 TRACE("used default %s\n", debugstr_w(pszOut));
1639 /*************************************************************************
1640 * UrlApplySchemeW [SHLWAPI.@]
1642 * See UrlApplySchemeA.
1644 HRESULT WINAPI UrlApplySchemeW(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut, DWORD dwFlags)
1646 PARSEDURLW in_scheme;
1650 TRACE("(%s, %p, %p:out size %d, 0x%08x)\n", debugstr_w(pszIn),
1651 pszOut, pcchOut, pcchOut ? *pcchOut : 0, dwFlags);
1653 if (!pszIn || !pszOut || !pcchOut) return E_INVALIDARG;
1655 if (dwFlags & URL_APPLY_GUESSFILE) {
1656 FIXME("(%s %p %p(%d) 0x%08x): stub URL_APPLY_GUESSFILE not implemented\n",
1657 debugstr_w(pszIn), pszOut, pcchOut, *pcchOut, dwFlags);
1658 strcpyW(pszOut, pszIn);
1659 *pcchOut = strlenW(pszOut);
1663 in_scheme.cbSize = sizeof(in_scheme);
1664 /* See if the base has a scheme */
1665 res1 = ParseURLW(pszIn, &in_scheme);
1667 /* no scheme in input, need to see if we need to guess */
1668 if (dwFlags & URL_APPLY_GUESSSCHEME) {
1669 if ((ret = URL_GuessScheme(pszIn, pszOut, pcchOut)) != E_FAIL)
1674 /* we have a scheme, see if valid (known scheme) */
1675 if (in_scheme.nScheme) {
1676 /* have valid scheme, so just copy and exit */
1677 if (strlenW(pszIn) + 1 > *pcchOut) {
1678 *pcchOut = strlenW(pszIn) + 1;
1681 strcpyW(pszOut, pszIn);
1682 *pcchOut = strlenW(pszOut);
1683 TRACE("valid scheme, returning copy\n");
1688 /* If we are here, then either invalid scheme,
1689 * or no scheme and can't/failed guess.
1691 if ( ( ((res1 == 0) && (dwFlags & URL_APPLY_FORCEAPPLY)) ||
1693 (dwFlags & URL_APPLY_DEFAULT)) {
1694 /* find and apply default scheme */
1695 return URL_ApplyDefault(pszIn, pszOut, pcchOut);
1701 /*************************************************************************
1702 * UrlIsA [SHLWAPI.@]
1704 * Determine if a Url is of a certain class.
1707 * pszUrl [I] Url to check
1708 * Urlis [I] URLIS_ constant from "shlwapi.h"
1711 * TRUE if pszUrl belongs to the class type in Urlis.
1714 BOOL WINAPI UrlIsA(LPCSTR pszUrl, URLIS Urlis)
1720 TRACE("(%s %d)\n", debugstr_a(pszUrl), Urlis);
1725 base.cbSize = sizeof(base);
1726 res1 = ParseURLA(pszUrl, &base);
1727 if (res1) return FALSE; /* invalid scheme */
1728 switch (base.nScheme)
1730 case URL_SCHEME_MAILTO:
1731 case URL_SCHEME_SHELL:
1732 case URL_SCHEME_JAVASCRIPT:
1733 case URL_SCHEME_VBSCRIPT:
1734 case URL_SCHEME_ABOUT:
1740 return !StrCmpNA("file:", pszUrl, 5);
1742 case URLIS_DIRECTORY:
1743 last = pszUrl + strlen(pszUrl) - 1;
1744 return (last >= pszUrl && (*last == '/' || *last == '\\' ));
1747 return PathIsURLA(pszUrl);
1749 case URLIS_NOHISTORY:
1750 case URLIS_APPLIABLE:
1751 case URLIS_HASQUERY:
1753 FIXME("(%s %d): stub\n", debugstr_a(pszUrl), Urlis);
1758 /*************************************************************************
1759 * UrlIsW [SHLWAPI.@]
1763 BOOL WINAPI UrlIsW(LPCWSTR pszUrl, URLIS Urlis)
1765 static const WCHAR stemp[] = { 'f','i','l','e',':',0 };
1770 TRACE("(%s %d)\n", debugstr_w(pszUrl), Urlis);
1775 base.cbSize = sizeof(base);
1776 res1 = ParseURLW(pszUrl, &base);
1777 if (res1) return FALSE; /* invalid scheme */
1778 switch (base.nScheme)
1780 case URL_SCHEME_MAILTO:
1781 case URL_SCHEME_SHELL:
1782 case URL_SCHEME_JAVASCRIPT:
1783 case URL_SCHEME_VBSCRIPT:
1784 case URL_SCHEME_ABOUT:
1790 return !strncmpW(stemp, pszUrl, 5);
1792 case URLIS_DIRECTORY:
1793 last = pszUrl + strlenW(pszUrl) - 1;
1794 return (last >= pszUrl && (*last == '/' || *last == '\\'));
1797 return PathIsURLW(pszUrl);
1799 case URLIS_NOHISTORY:
1800 case URLIS_APPLIABLE:
1801 case URLIS_HASQUERY:
1803 FIXME("(%s %d): stub\n", debugstr_w(pszUrl), Urlis);
1808 /*************************************************************************
1809 * UrlIsNoHistoryA [SHLWAPI.@]
1811 * Determine if a Url should not be stored in the users history list.
1814 * pszUrl [I] Url to check
1817 * TRUE, if pszUrl should be excluded from the history list,
1820 BOOL WINAPI UrlIsNoHistoryA(LPCSTR pszUrl)
1822 return UrlIsA(pszUrl, URLIS_NOHISTORY);
1825 /*************************************************************************
1826 * UrlIsNoHistoryW [SHLWAPI.@]
1828 * See UrlIsNoHistoryA.
1830 BOOL WINAPI UrlIsNoHistoryW(LPCWSTR pszUrl)
1832 return UrlIsW(pszUrl, URLIS_NOHISTORY);
1835 /*************************************************************************
1836 * UrlIsOpaqueA [SHLWAPI.@]
1838 * Determine if a Url is opaque.
1841 * pszUrl [I] Url to check
1844 * TRUE if pszUrl is opaque,
1848 * An opaque Url is one that does not start with "<protocol>://".
1850 BOOL WINAPI UrlIsOpaqueA(LPCSTR pszUrl)
1852 return UrlIsA(pszUrl, URLIS_OPAQUE);
1855 /*************************************************************************
1856 * UrlIsOpaqueW [SHLWAPI.@]
1860 BOOL WINAPI UrlIsOpaqueW(LPCWSTR pszUrl)
1862 return UrlIsW(pszUrl, URLIS_OPAQUE);
1865 /*************************************************************************
1866 * Scans for characters of type "type" and when not matching found,
1867 * returns pointer to it and length in size.
1869 * Characters tested based on RFC 1738
1871 static LPCWSTR URL_ScanID(LPCWSTR start, LPDWORD size, WINE_URL_SCAN_TYPE type)
1873 static DWORD alwayszero = 0;
1882 if ( (islowerW(*start) && isalphaW(*start)) ||
1897 if ( isalphaW(*start) ||
1899 /* user/password only characters */
1904 /* *extra* characters */
1911 /* *safe* characters */
1920 } else if (*start == '%') {
1921 if (isxdigitW(*(start+1)) &&
1922 isxdigitW(*(start+2))) {
1934 if (isdigitW(*start)) {
1945 if (isalnumW(*start) ||
1957 FIXME("unknown type %d\n", type);
1958 return (LPWSTR)&alwayszero;
1960 /* TRACE("scanned %d characters next char %p<%c>\n",
1961 *size, start, *start); */
1965 /*************************************************************************
1966 * Attempt to parse URL into pieces.
1968 static LONG URL_ParseUrl(LPCWSTR pszUrl, WINE_PARSE_URL *pl)
1972 memset(pl, 0, sizeof(WINE_PARSE_URL));
1973 pl->pScheme = pszUrl;
1974 work = URL_ScanID(pl->pScheme, &pl->szScheme, SCHEME);
1975 if (!*work || (*work != ':')) goto ErrorExit;
1977 if ((*work != '/') || (*(work+1) != '/')) goto SuccessExit;
1978 pl->pUserName = work + 2;
1979 work = URL_ScanID(pl->pUserName, &pl->szUserName, USERPASS);
1980 if (*work == ':' ) {
1981 /* parse password */
1983 pl->pPassword = work;
1984 work = URL_ScanID(pl->pPassword, &pl->szPassword, USERPASS);
1986 /* what we just parsed must be the hostname and port
1987 * so reset pointers and clear then let it parse */
1988 pl->szUserName = pl->szPassword = 0;
1989 work = pl->pUserName - 1;
1990 pl->pUserName = pl->pPassword = 0;
1992 } else if (*work == '@') {
1996 } else if (!*work || (*work == '/') || (*work == '.')) {
1997 /* what was parsed was hostname, so reset pointers and let it parse */
1998 pl->szUserName = pl->szPassword = 0;
1999 work = pl->pUserName - 1;
2000 pl->pUserName = pl->pPassword = 0;
2001 } else goto ErrorExit;
2003 /* now start parsing hostname or hostnumber */
2005 pl->pHostName = work;
2006 work = URL_ScanID(pl->pHostName, &pl->szHostName, HOST);
2011 work = URL_ScanID(pl->pPort, &pl->szPort, PORT);
2014 /* see if query string */
2015 pl->pQuery = strchrW(work, '?');
2016 if (pl->pQuery) pl->szQuery = strlenW(pl->pQuery);
2019 TRACE("parse successful: scheme=%p(%d), user=%p(%d), pass=%p(%d), host=%p(%d), port=%p(%d), query=%p(%d)\n",
2020 pl->pScheme, pl->szScheme,
2021 pl->pUserName, pl->szUserName,
2022 pl->pPassword, pl->szPassword,
2023 pl->pHostName, pl->szHostName,
2024 pl->pPort, pl->szPort,
2025 pl->pQuery, pl->szQuery);
2028 FIXME("failed to parse %s\n", debugstr_w(pszUrl));
2029 return E_INVALIDARG;
2032 /*************************************************************************
2033 * UrlGetPartA [SHLWAPI.@]
2035 * Retrieve part of a Url.
2038 * pszIn [I] Url to parse
2039 * pszOut [O] Destination for part of pszIn requested
2040 * pcchOut [I] Size of pszOut
2041 * [O] length of pszOut string EXCLUDING '\0' if S_OK, otherwise
2042 * needed size of pszOut INCLUDING '\0'.
2043 * dwPart [I] URL_PART_ enum from "shlwapi.h"
2044 * dwFlags [I] URL_ flags from "shlwapi.h"
2047 * Success: S_OK. pszOut contains the part requested, pcchOut contains its length.
2048 * Failure: An HRESULT error code describing the error.
2050 HRESULT WINAPI UrlGetPartA(LPCSTR pszIn, LPSTR pszOut, LPDWORD pcchOut,
2051 DWORD dwPart, DWORD dwFlags)
2054 DWORD ret, len, len2;
2056 if(!pszIn || !pszOut || !pcchOut || *pcchOut <= 0)
2057 return E_INVALIDARG;
2059 in = HeapAlloc(GetProcessHeap(), 0,
2060 (2*INTERNET_MAX_URL_LENGTH) * sizeof(WCHAR));
2061 out = in + INTERNET_MAX_URL_LENGTH;
2063 MultiByteToWideChar(0, 0, pszIn, -1, in, INTERNET_MAX_URL_LENGTH);
2065 len = INTERNET_MAX_URL_LENGTH;
2066 ret = UrlGetPartW(in, out, &len, dwPart, dwFlags);
2069 HeapFree(GetProcessHeap(), 0, in);
2073 len2 = WideCharToMultiByte(0, 0, out, len, 0, 0, 0, 0);
2074 if (len2 > *pcchOut) {
2076 HeapFree(GetProcessHeap(), 0, in);
2079 len2 = WideCharToMultiByte(0, 0, out, len+1, pszOut, *pcchOut, 0, 0);
2081 HeapFree(GetProcessHeap(), 0, in);
2085 /*************************************************************************
2086 * UrlGetPartW [SHLWAPI.@]
2090 HRESULT WINAPI UrlGetPartW(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut,
2091 DWORD dwPart, DWORD dwFlags)
2095 DWORD scheme, size, schsize;
2096 LPCWSTR addr, schaddr;
2098 TRACE("(%s %p %p(%d) %08x %08x)\n",
2099 debugstr_w(pszIn), pszOut, pcchOut, *pcchOut, dwPart, dwFlags);
2101 if(!pszIn || !pszOut || !pcchOut || *pcchOut <= 0)
2102 return E_INVALIDARG;
2106 addr = strchrW(pszIn, ':');
2108 scheme = URL_SCHEME_UNKNOWN;
2110 scheme = get_scheme_code(pszIn, addr-pszIn);
2112 ret = URL_ParseUrl(pszIn, &pl);
2115 case URL_PART_SCHEME:
2116 if (!pl.szScheme || scheme == URL_SCHEME_UNKNOWN) {
2124 case URL_PART_HOSTNAME:
2126 case URL_SCHEME_FTP:
2127 case URL_SCHEME_HTTP:
2128 case URL_SCHEME_GOPHER:
2129 case URL_SCHEME_TELNET:
2130 case URL_SCHEME_FILE:
2131 case URL_SCHEME_HTTPS:
2138 if(scheme==URL_SCHEME_FILE && (!pl.szHostName ||
2139 (pl.szHostName==1 && *(pl.pHostName+1)==':'))) {
2144 if (!pl.szHostName) {
2148 addr = pl.pHostName;
2149 size = pl.szHostName;
2152 case URL_PART_USERNAME:
2153 if (!pl.szUserName) {
2157 addr = pl.pUserName;
2158 size = pl.szUserName;
2161 case URL_PART_PASSWORD:
2162 if (!pl.szPassword) {
2166 addr = pl.pPassword;
2167 size = pl.szPassword;
2179 case URL_PART_QUERY:
2190 return E_INVALIDARG;
2193 if (dwFlags == URL_PARTFLAG_KEEPSCHEME) {
2194 if(!pl.pScheme || !pl.szScheme) {
2198 schaddr = pl.pScheme;
2199 schsize = pl.szScheme;
2200 if (*pcchOut < schsize + size + 2) {
2201 *pcchOut = schsize + size + 2;
2204 memcpy(pszOut, schaddr, schsize*sizeof(WCHAR));
2205 pszOut[schsize] = ':';
2206 memcpy(pszOut+schsize+1, addr, size*sizeof(WCHAR));
2207 pszOut[schsize+1+size] = 0;
2208 *pcchOut = schsize + 1 + size;
2211 if (*pcchOut < size + 1) {*pcchOut = size+1; return E_POINTER;}
2212 memcpy(pszOut, addr, size*sizeof(WCHAR));
2216 TRACE("len=%d %s\n", *pcchOut, debugstr_w(pszOut));
2221 /*************************************************************************
2222 * PathIsURLA [SHLWAPI.@]
2224 * Check if the given path is a Url.
2227 * lpszPath [I] Path to check.
2230 * TRUE if lpszPath is a Url.
2231 * FALSE if lpszPath is NULL or not a Url.
2233 BOOL WINAPI PathIsURLA(LPCSTR lpstrPath)
2238 TRACE("%s\n", debugstr_a(lpstrPath));
2240 if (!lpstrPath || !*lpstrPath) return FALSE;
2243 base.cbSize = sizeof(base);
2244 hres = ParseURLA(lpstrPath, &base);
2245 return hres == S_OK && (base.nScheme != URL_SCHEME_INVALID);
2248 /*************************************************************************
2249 * PathIsURLW [SHLWAPI.@]
2253 BOOL WINAPI PathIsURLW(LPCWSTR lpstrPath)
2258 TRACE("%s\n", debugstr_w(lpstrPath));
2260 if (!lpstrPath || !*lpstrPath) return FALSE;
2263 base.cbSize = sizeof(base);
2264 hres = ParseURLW(lpstrPath, &base);
2265 return hres == S_OK && (base.nScheme != URL_SCHEME_INVALID);
2268 /*************************************************************************
2269 * UrlCreateFromPathA [SHLWAPI.@]
2271 * See UrlCreateFromPathW
2273 HRESULT WINAPI UrlCreateFromPathA(LPCSTR pszPath, LPSTR pszUrl, LPDWORD pcchUrl, DWORD dwReserved)
2275 WCHAR bufW[INTERNET_MAX_URL_LENGTH];
2277 UNICODE_STRING pathW;
2279 DWORD lenW = sizeof(bufW)/sizeof(WCHAR), lenA;
2281 if(!RtlCreateUnicodeStringFromAsciiz(&pathW, pszPath))
2282 return E_INVALIDARG;
2283 if((ret = UrlCreateFromPathW(pathW.Buffer, urlW, &lenW, dwReserved)) == E_POINTER) {
2284 urlW = HeapAlloc(GetProcessHeap(), 0, lenW * sizeof(WCHAR));
2285 ret = UrlCreateFromPathW(pathW.Buffer, urlW, &lenW, dwReserved);
2287 if(ret == S_OK || ret == S_FALSE) {
2288 RtlUnicodeToMultiByteSize(&lenA, urlW, lenW * sizeof(WCHAR));
2289 if(*pcchUrl > lenA) {
2290 RtlUnicodeToMultiByteN(pszUrl, *pcchUrl - 1, &lenA, urlW, lenW * sizeof(WCHAR));
2294 *pcchUrl = lenA + 1;
2298 if(urlW != bufW) HeapFree(GetProcessHeap(), 0, urlW);
2299 RtlFreeUnicodeString(&pathW);
2303 /*************************************************************************
2304 * UrlCreateFromPathW [SHLWAPI.@]
2306 * Create a Url from a file path.
2309 * pszPath [I] Path to convert
2310 * pszUrl [O] Destination for the converted Url
2311 * pcchUrl [I/O] Length of pszUrl
2312 * dwReserved [I] Reserved, must be 0
2315 * Success: S_OK pszUrl contains the converted path, S_FALSE if the path is already a Url
2316 * Failure: An HRESULT error code.
2318 HRESULT WINAPI UrlCreateFromPathW(LPCWSTR pszPath, LPWSTR pszUrl, LPDWORD pcchUrl, DWORD dwReserved)
2323 WCHAR file_colonW[] = {'f','i','l','e',':',0};
2324 WCHAR three_slashesW[] = {'/','/','/',0};
2325 PARSEDURLW parsed_url;
2327 TRACE("(%s, %p, %p, 0x%08x)\n", debugstr_w(pszPath), pszUrl, pcchUrl, dwReserved);
2329 /* Validate arguments */
2330 if (dwReserved != 0)
2331 return E_INVALIDARG;
2332 if (!pszUrl || !pcchUrl)
2333 return E_INVALIDARG;
2336 parsed_url.cbSize = sizeof(parsed_url);
2337 if(ParseURLW(pszPath, &parsed_url) == S_OK) {
2338 if(parsed_url.nScheme != URL_SCHEME_INVALID && parsed_url.cchProtocol > 1) {
2339 needed = strlenW(pszPath);
2340 if (needed >= *pcchUrl) {
2341 *pcchUrl = needed + 1;
2345 strcpyW(pszUrl, pszPath);
2351 pszNewUrl = HeapAlloc(GetProcessHeap(), 0, (strlenW(pszPath) + 9) * sizeof(WCHAR)); /* "file:///" + pszPath_len + 1 */
2352 strcpyW(pszNewUrl, file_colonW);
2353 if(isalphaW(pszPath[0]) && pszPath[1] == ':')
2354 strcatW(pszNewUrl, three_slashesW);
2355 strcatW(pszNewUrl, pszPath);
2356 ret = UrlEscapeW(pszNewUrl, pszUrl, pcchUrl, URL_ESCAPE_PERCENT);
2358 HeapFree(GetProcessHeap(), 0, pszNewUrl);
2362 /*************************************************************************
2363 * SHAutoComplete [SHLWAPI.@]
2365 * Enable auto-completion for an edit control.
2368 * hwndEdit [I] Handle of control to enable auto-completion for
2369 * dwFlags [I] SHACF_ flags from "shlwapi.h"
2372 * Success: S_OK. Auto-completion is enabled for the control.
2373 * Failure: An HRESULT error code indicating the error.
2375 HRESULT WINAPI SHAutoComplete(HWND hwndEdit, DWORD dwFlags)
2381 /*************************************************************************
2382 * MLBuildResURLA [SHLWAPI.405]
2384 * Create a Url pointing to a resource in a module.
2387 * lpszLibName [I] Name of the module containing the resource
2388 * hMod [I] Callers module handle
2389 * dwFlags [I] Undocumented flags for loading the module
2390 * lpszRes [I] Resource name
2391 * lpszDest [O] Destination for resulting Url
2392 * dwDestLen [I] Length of lpszDest
2395 * Success: S_OK. lpszDest contains the resource Url.
2396 * Failure: E_INVALIDARG, if any argument is invalid, or
2397 * E_FAIL if dwDestLen is too small.
2399 HRESULT WINAPI MLBuildResURLA(LPCSTR lpszLibName, HMODULE hMod, DWORD dwFlags,
2400 LPCSTR lpszRes, LPSTR lpszDest, DWORD dwDestLen)
2402 WCHAR szLibName[MAX_PATH], szRes[MAX_PATH], szDest[MAX_PATH];
2406 MultiByteToWideChar(CP_ACP, 0, lpszLibName, -1, szLibName, sizeof(szLibName)/sizeof(WCHAR));
2409 MultiByteToWideChar(CP_ACP, 0, lpszRes, -1, szRes, sizeof(szRes)/sizeof(WCHAR));
2411 if (dwDestLen > sizeof(szLibName)/sizeof(WCHAR))
2412 dwDestLen = sizeof(szLibName)/sizeof(WCHAR);
2414 hRet = MLBuildResURLW(lpszLibName ? szLibName : NULL, hMod, dwFlags,
2415 lpszRes ? szRes : NULL, lpszDest ? szDest : NULL, dwDestLen);
2416 if (SUCCEEDED(hRet) && lpszDest)
2417 WideCharToMultiByte(CP_ACP, 0, szDest, -1, lpszDest, dwDestLen, 0, 0);
2422 /*************************************************************************
2423 * MLBuildResURLA [SHLWAPI.406]
2425 * See MLBuildResURLA.
2427 HRESULT WINAPI MLBuildResURLW(LPCWSTR lpszLibName, HMODULE hMod, DWORD dwFlags,
2428 LPCWSTR lpszRes, LPWSTR lpszDest, DWORD dwDestLen)
2430 static const WCHAR szRes[] = { 'r','e','s',':','/','/','\0' };
2431 #define szResLen ((sizeof(szRes) - sizeof(WCHAR))/sizeof(WCHAR))
2432 HRESULT hRet = E_FAIL;
2434 TRACE("(%s,%p,0x%08x,%s,%p,%d)\n", debugstr_w(lpszLibName), hMod, dwFlags,
2435 debugstr_w(lpszRes), lpszDest, dwDestLen);
2437 if (!lpszLibName || !hMod || hMod == INVALID_HANDLE_VALUE || !lpszRes ||
2438 !lpszDest || (dwFlags && dwFlags != 2))
2439 return E_INVALIDARG;
2441 if (dwDestLen >= szResLen + 1)
2443 dwDestLen -= (szResLen + 1);
2444 memcpy(lpszDest, szRes, sizeof(szRes));
2446 hMod = MLLoadLibraryW(lpszLibName, hMod, dwFlags);
2450 WCHAR szBuff[MAX_PATH];
2453 len = GetModuleFileNameW(hMod, szBuff, sizeof(szBuff)/sizeof(WCHAR));
2454 if (len && len < sizeof(szBuff)/sizeof(WCHAR))
2456 DWORD dwPathLen = strlenW(szBuff) + 1;
2458 if (dwDestLen >= dwPathLen)
2462 dwDestLen -= dwPathLen;
2463 memcpy(lpszDest + szResLen, szBuff, dwPathLen * sizeof(WCHAR));
2465 dwResLen = strlenW(lpszRes) + 1;
2466 if (dwDestLen >= dwResLen + 1)
2468 lpszDest[szResLen + dwPathLen-1] = '/';
2469 memcpy(lpszDest + szResLen + dwPathLen, lpszRes, dwResLen * sizeof(WCHAR));
2474 MLFreeLibrary(hMod);
2480 /***********************************************************************
2481 * UrlFixupW [SHLWAPI.462]
2483 * Checks the scheme part of a URL and attempts to correct misspellings.
2486 * lpszUrl [I] Pointer to the URL to be corrected
2487 * lpszTranslatedUrl [O] Pointer to a buffer to store corrected URL
2488 * dwMaxChars [I] Maximum size of corrected URL
2491 * success: S_OK if URL corrected or already correct
2492 * failure: S_FALSE if unable to correct / COM error code if other error
2495 HRESULT WINAPI UrlFixupW(LPCWSTR url, LPWSTR translatedUrl, DWORD maxChars)
2499 FIXME("(%s,%p,%d) STUB\n", debugstr_w(url), translatedUrl, maxChars);
2504 srcLen = lstrlenW(url) + 1;
2506 /* For now just copy the URL directly */
2507 lstrcpynW(translatedUrl, url, (maxChars < srcLen) ? maxChars : srcLen);