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;
52 static const SHL_2_inet_scheme shlwapi_schemes[] = {
53 {URL_SCHEME_FTP, "ftp"},
54 {URL_SCHEME_HTTP, "http"},
55 {URL_SCHEME_GOPHER, "gopher"},
56 {URL_SCHEME_MAILTO, "mailto"},
57 {URL_SCHEME_NEWS, "news"},
58 {URL_SCHEME_NNTP, "nntp"},
59 {URL_SCHEME_TELNET, "telnet"},
60 {URL_SCHEME_WAIS, "wais"},
61 {URL_SCHEME_FILE, "file"},
62 {URL_SCHEME_MK, "mk"},
63 {URL_SCHEME_HTTPS, "https"},
64 {URL_SCHEME_SHELL, "shell"},
65 {URL_SCHEME_SNEWS, "snews"},
66 {URL_SCHEME_LOCAL, "local"},
67 {URL_SCHEME_JAVASCRIPT, "javascript"},
68 {URL_SCHEME_VBSCRIPT, "vbscript"},
69 {URL_SCHEME_ABOUT, "about"},
70 {URL_SCHEME_RES, "res"},
75 LPCWSTR pScheme; /* [out] start of scheme */
76 DWORD szScheme; /* [out] size of scheme (until colon) */
77 LPCWSTR pUserName; /* [out] start of Username */
78 DWORD szUserName; /* [out] size of Username (until ":" or "@") */
79 LPCWSTR pPassword; /* [out] start of Password */
80 DWORD szPassword; /* [out] size of Password (until "@") */
81 LPCWSTR pHostName; /* [out] start of Hostname */
82 DWORD szHostName; /* [out] size of Hostname (until ":" or "/") */
83 LPCWSTR pPort; /* [out] start of Port */
84 DWORD szPort; /* [out] size of Port (until "/" or eos) */
85 LPCWSTR pQuery; /* [out] start of Query */
86 DWORD szQuery; /* [out] size of Query (until eos) */
96 static const CHAR hexDigits[] = "0123456789ABCDEF";
98 static const WCHAR fileW[] = {'f','i','l','e','\0'};
100 static const unsigned char HashDataLookup[256] = {
101 0x01, 0x0E, 0x6E, 0x19, 0x61, 0xAE, 0x84, 0x77, 0x8A, 0xAA, 0x7D, 0x76, 0x1B,
102 0xE9, 0x8C, 0x33, 0x57, 0xC5, 0xB1, 0x6B, 0xEA, 0xA9, 0x38, 0x44, 0x1E, 0x07,
103 0xAD, 0x49, 0xBC, 0x28, 0x24, 0x41, 0x31, 0xD5, 0x68, 0xBE, 0x39, 0xD3, 0x94,
104 0xDF, 0x30, 0x73, 0x0F, 0x02, 0x43, 0xBA, 0xD2, 0x1C, 0x0C, 0xB5, 0x67, 0x46,
105 0x16, 0x3A, 0x4B, 0x4E, 0xB7, 0xA7, 0xEE, 0x9D, 0x7C, 0x93, 0xAC, 0x90, 0xB0,
106 0xA1, 0x8D, 0x56, 0x3C, 0x42, 0x80, 0x53, 0x9C, 0xF1, 0x4F, 0x2E, 0xA8, 0xC6,
107 0x29, 0xFE, 0xB2, 0x55, 0xFD, 0xED, 0xFA, 0x9A, 0x85, 0x58, 0x23, 0xCE, 0x5F,
108 0x74, 0xFC, 0xC0, 0x36, 0xDD, 0x66, 0xDA, 0xFF, 0xF0, 0x52, 0x6A, 0x9E, 0xC9,
109 0x3D, 0x03, 0x59, 0x09, 0x2A, 0x9B, 0x9F, 0x5D, 0xA6, 0x50, 0x32, 0x22, 0xAF,
110 0xC3, 0x64, 0x63, 0x1A, 0x96, 0x10, 0x91, 0x04, 0x21, 0x08, 0xBD, 0x79, 0x40,
111 0x4D, 0x48, 0xD0, 0xF5, 0x82, 0x7A, 0x8F, 0x37, 0x69, 0x86, 0x1D, 0xA4, 0xB9,
112 0xC2, 0xC1, 0xEF, 0x65, 0xF2, 0x05, 0xAB, 0x7E, 0x0B, 0x4A, 0x3B, 0x89, 0xE4,
113 0x6C, 0xBF, 0xE8, 0x8B, 0x06, 0x18, 0x51, 0x14, 0x7F, 0x11, 0x5B, 0x5C, 0xFB,
114 0x97, 0xE1, 0xCF, 0x15, 0x62, 0x71, 0x70, 0x54, 0xE2, 0x12, 0xD6, 0xC7, 0xBB,
115 0x0D, 0x20, 0x5E, 0xDC, 0xE0, 0xD4, 0xF7, 0xCC, 0xC4, 0x2B, 0xF9, 0xEC, 0x2D,
116 0xF4, 0x6F, 0xB6, 0x99, 0x88, 0x81, 0x5A, 0xD9, 0xCA, 0x13, 0xA5, 0xE7, 0x47,
117 0xE6, 0x8E, 0x60, 0xE3, 0x3E, 0xB3, 0xF6, 0x72, 0xA2, 0x35, 0xA0, 0xD7, 0xCD,
118 0xB4, 0x2F, 0x6D, 0x2C, 0x26, 0x1F, 0x95, 0x87, 0x00, 0xD8, 0x34, 0x3F, 0x17,
119 0x25, 0x45, 0x27, 0x75, 0x92, 0xB8, 0xA3, 0xC8, 0xDE, 0xEB, 0xF8, 0xF3, 0xDB,
120 0x0A, 0x98, 0x83, 0x7B, 0xE5, 0xCB, 0x4C, 0x78, 0xD1 };
122 static BOOL URL_JustLocation(LPCWSTR str)
124 while(*str && (*str == L'/')) str++;
126 while (*str && ((*str == L'-') ||
128 isalnumW(*str))) str++;
129 if (*str == L'/') return FALSE;
135 /*************************************************************************
138 * Parse a Url into its constituent parts.
142 * y [O] Undocumented structure holding the parsed information
145 * Success: S_OK. y contains the parsed Url details.
146 * Failure: An HRESULT error code.
148 HRESULT WINAPI ParseURLA(LPCSTR x, PARSEDURLA *y)
151 const SHL_2_inet_scheme *inet_pro;
153 y->nScheme = URL_SCHEME_INVALID;
154 if (y->cbSize != sizeof(*y)) return E_INVALIDARG;
155 /* FIXME: leading white space generates error of 0x80041001 which
158 if (*x <= ' ') return 0x80041001;
164 y->cchProtocol = cnt;
173 /* check for no scheme in string start */
174 /* (apparently schemes *must* be larger than a single character) */
175 if ((*x == '\0') || (y->cchProtocol <= 1)) {
176 y->pszProtocol = NULL;
180 /* found scheme, set length of remainder */
181 y->cchSuffix = lstrlenA(y->pszSuffix);
183 /* see if known scheme and return indicator number */
184 y->nScheme = URL_SCHEME_UNKNOWN;
185 inet_pro = shlwapi_schemes;
186 while (inet_pro->scheme_name) {
187 if (!strncasecmp(inet_pro->scheme_name, y->pszProtocol,
188 min(y->cchProtocol, lstrlenA(inet_pro->scheme_name)))) {
189 y->nScheme = inet_pro->scheme_number;
197 /*************************************************************************
200 * Unicode version of ParseURLA.
202 HRESULT WINAPI ParseURLW(LPCWSTR x, PARSEDURLW *y)
205 const SHL_2_inet_scheme *inet_pro;
209 y->nScheme = URL_SCHEME_INVALID;
210 if (y->cbSize != sizeof(*y)) return E_INVALIDARG;
211 /* FIXME: leading white space generates error of 0x80041001 which
214 if (*x <= L' ') return 0x80041001;
220 y->cchProtocol = cnt;
229 /* check for no scheme in string start */
230 /* (apparently schemes *must* be larger than a single character) */
231 if ((*x == L'\0') || (y->cchProtocol <= 1)) {
232 y->pszProtocol = NULL;
236 /* found scheme, set length of remainder */
237 y->cchSuffix = lstrlenW(y->pszSuffix);
239 /* see if known scheme and return indicator number */
240 len = WideCharToMultiByte(0, 0, y->pszProtocol, y->cchProtocol, 0, 0, 0, 0);
241 cmpstr = HeapAlloc(GetProcessHeap(), 0, len);
242 WideCharToMultiByte(0, 0, y->pszProtocol, y->cchProtocol, cmpstr, len, 0, 0);
243 y->nScheme = URL_SCHEME_UNKNOWN;
244 inet_pro = shlwapi_schemes;
245 while (inet_pro->scheme_name) {
246 if (!strncasecmp(inet_pro->scheme_name, cmpstr,
247 min(len, lstrlenA(inet_pro->scheme_name)))) {
248 y->nScheme = inet_pro->scheme_number;
253 HeapFree(GetProcessHeap(), 0, cmpstr);
257 /*************************************************************************
258 * UrlCanonicalizeA [SHLWAPI.@]
260 * Canonicalize a Url.
263 * pszUrl [I] Url to cCanonicalize
264 * pszCanonicalized [O] Destination for converted Url.
265 * pcchCanonicalized [I/O] Length of pszUrl, destination for length of pszCanonicalized
266 * dwFlags [I] Flags controlling the conversion.
269 * Success: S_OK. The pszCanonicalized contains the converted Url.
270 * Failure: E_POINTER, if *pcchCanonicalized is too small.
272 * MSDN incorrectly describes the flags for this function. They should be:
273 *| URL_DONT_ESCAPE_EXTRA_INFO 0x02000000
274 *| URL_ESCAPE_SPACES_ONLY 0x04000000
275 *| URL_ESCAPE_PERCENT 0x00001000
276 *| URL_ESCAPE_UNSAFE 0x10000000
277 *| URL_UNESCAPE 0x10000000
278 *| URL_DONT_SIMPLIFY 0x08000000
279 *| URL_ESCAPE_SEGMENT_ONLY 0x00002000
281 HRESULT WINAPI UrlCanonicalizeA(LPCSTR pszUrl, LPSTR pszCanonicalized,
282 LPDWORD pcchCanonicalized, DWORD dwFlags)
284 LPWSTR base, canonical;
285 DWORD ret, len, len2;
287 TRACE("(%s %p %p 0x%08lx) using W version\n",
288 debugstr_a(pszUrl), pszCanonicalized,
289 pcchCanonicalized, dwFlags);
291 if(!pszUrl || !pszCanonicalized || !pcchCanonicalized)
294 base = HeapAlloc(GetProcessHeap(), 0,
295 (2*INTERNET_MAX_URL_LENGTH) * sizeof(WCHAR));
296 canonical = base + INTERNET_MAX_URL_LENGTH;
298 MultiByteToWideChar(0, 0, pszUrl, -1, base, INTERNET_MAX_URL_LENGTH);
299 len = INTERNET_MAX_URL_LENGTH;
301 ret = UrlCanonicalizeW(base, canonical, &len, dwFlags);
303 HeapFree(GetProcessHeap(), 0, base);
307 len2 = WideCharToMultiByte(0, 0, canonical, len, 0, 0, 0, 0);
308 if (len2 > *pcchCanonicalized) {
309 *pcchCanonicalized = len;
310 HeapFree(GetProcessHeap(), 0, base);
313 WideCharToMultiByte(0, 0, canonical, len+1, pszCanonicalized,
314 *pcchCanonicalized, 0, 0);
315 *pcchCanonicalized = len2;
316 HeapFree(GetProcessHeap(), 0, base);
320 /*************************************************************************
321 * UrlCanonicalizeW [SHLWAPI.@]
323 * See UrlCanonicalizeA.
325 HRESULT WINAPI UrlCanonicalizeW(LPCWSTR pszUrl, LPWSTR pszCanonicalized,
326 LPDWORD pcchCanonicalized, DWORD dwFlags)
330 LPWSTR lpszUrlCpy, wk1, wk2, mp, mp2, root;
335 static const WCHAR wszFile[] = {'f','i','l','e',':'};
337 TRACE("(%s %p %p 0x%08lx)\n", debugstr_w(pszUrl), pszCanonicalized,
338 pcchCanonicalized, dwFlags);
340 if(!pszUrl || !pszCanonicalized || !pcchCanonicalized)
344 *pszCanonicalized = 0;
348 nByteLen = (lstrlenW(pszUrl) + 1) * sizeof(WCHAR); /* length in bytes */
349 lpszUrlCpy = HeapAlloc(GetProcessHeap(), 0, INTERNET_MAX_URL_LENGTH);
351 if((dwFlags & URL_FILE_USE_PATHURL) && nByteLen >= sizeof(wszFile)
352 && !memcmp(wszFile, pszUrl, sizeof(wszFile)))
358 * 1 have 2[+] alnum 2,3
359 * 2 have scheme (found :) 4,6,3
360 * 3 failed (no location)
362 * 5 have 1[+] alnum 6,3
363 * 6 have location (found /) save root location
366 wk1 = (LPWSTR)pszUrl;
370 if(pszUrl[1] == ':') { /* Assume path */
371 static const WCHAR wszFilePrefix[] = {'f','i','l','e',':','/','/','/'};
373 memcpy(wk2, wszFilePrefix, sizeof(wszFilePrefix));
374 wk2 += sizeof(wszFilePrefix)/sizeof(WCHAR);
381 if (!isalnumW(*wk1)) {state = 3; break;}
383 if (!isalnumW(*wk1)) {state = 3; break;}
389 if (*wk1++ == L':') state = 2;
392 if (*wk1 != L'/') {state = 3; break;}
394 if (*wk1 != L'/') {state = 6; break;}
396 if(*wk1 == '/' && (dwFlags & URL_FILE_USE_PATHURL))
401 nWkLen = strlenW(wk1);
402 memcpy(wk2, wk1, (nWkLen + 1) * sizeof(WCHAR));
408 if(*mp == '/' || *mp == '\\')
414 if (!isalnumW(*wk1) && (*wk1 != L'-') && (*wk1 != L'.') && (*wk1 != ':'))
416 while(isalnumW(*wk1) || (*wk1 == L'-') || (*wk1 == L'.') || (*wk1 == ':'))
421 if (*wk1 != '/' && *wk1 != '\\') {state = 3; break;}
422 while(*wk1 == '/' || *wk1 == '\\') {
429 if(dwFlags & URL_DONT_SIMPLIFY) {
434 /* Now at root location, cannot back up any more. */
435 /* "root" will point at the '/' */
439 mp = strchrW(wk1, '/');
440 mp2 = strchrW(wk1, '\\');
441 if(mp2 && (!mp || mp2 < mp))
444 nWkLen = strlenW(wk1);
445 memcpy(wk2, wk1, (nWkLen + 1) * sizeof(WCHAR));
452 memcpy(wk2, wk1, nLen * sizeof(WCHAR));
460 TRACE("found '/.'\n");
461 if (wk1[1] == '/' || wk1[1] == '\\') {
462 /* case of /./ -> skip the ./ */
465 else if (wk1[1] == '.') {
466 /* found /.. look for next / */
467 TRACE("found '/..'\n");
468 if (wk1[2] == '/' || wk1[2] == '\\' ||wk1[2] == '?'
469 || wk1[2] == '#' || !wk1[2]) {
470 /* case /../ -> need to backup wk2 */
471 TRACE("found '/../'\n");
472 *(wk2-1) = L'\0'; /* set end of string */
473 mp = strrchrW(root, slash);
474 if (mp && (mp >= root)) {
475 /* found valid backup point */
477 if(wk1[2] != '/' && wk1[2] != '\\')
483 /* did not find point, restore '/' */
493 FIXME("how did we get here - state=%d\n", state);
494 HeapFree(GetProcessHeap(), 0, lpszUrlCpy);
498 TRACE("Simplified, orig <%s>, simple <%s>\n",
499 debugstr_w(pszUrl), debugstr_w(lpszUrlCpy));
501 nLen = lstrlenW(lpszUrlCpy);
502 while ((nLen > 0) && ((lpszUrlCpy[nLen-1] == '\r')||(lpszUrlCpy[nLen-1] == '\n')))
503 lpszUrlCpy[--nLen]=0;
505 if(dwFlags & (URL_UNESCAPE | URL_FILE_USE_PATHURL))
506 UrlUnescapeW(lpszUrlCpy, NULL, &nLen, URL_UNESCAPE_INPLACE);
508 if((EscapeFlags = dwFlags & (URL_ESCAPE_UNSAFE |
509 URL_ESCAPE_SPACES_ONLY |
511 URL_DONT_ESCAPE_EXTRA_INFO |
512 URL_ESCAPE_SEGMENT_ONLY ))) {
513 EscapeFlags &= ~URL_ESCAPE_UNSAFE;
514 hr = UrlEscapeW(lpszUrlCpy, pszCanonicalized, pcchCanonicalized,
516 } else { /* No escaping needed, just copy the string */
517 nLen = lstrlenW(lpszUrlCpy);
518 if(nLen < *pcchCanonicalized)
519 memcpy(pszCanonicalized, lpszUrlCpy, (nLen + 1)*sizeof(WCHAR));
524 *pcchCanonicalized = nLen;
527 HeapFree(GetProcessHeap(), 0, lpszUrlCpy);
530 TRACE("result %s\n", debugstr_w(pszCanonicalized));
535 /*************************************************************************
536 * UrlCombineA [SHLWAPI.@]
541 * pszBase [I] Base Url
542 * pszRelative [I] Url to combine with pszBase
543 * pszCombined [O] Destination for combined Url
544 * pcchCombined [O] Destination for length of pszCombined
545 * dwFlags [I] URL_ flags from "shlwapi.h"
548 * Success: S_OK. pszCombined contains the combined Url, pcchCombined
549 * contains its length.
550 * Failure: An HRESULT error code indicating the error.
552 HRESULT WINAPI UrlCombineA(LPCSTR pszBase, LPCSTR pszRelative,
553 LPSTR pszCombined, LPDWORD pcchCombined,
556 LPWSTR base, relative, combined;
557 DWORD ret, len, len2;
559 TRACE("(base %s, Relative %s, Combine size %ld, flags %08lx) using W version\n",
560 debugstr_a(pszBase),debugstr_a(pszRelative),
561 pcchCombined?*pcchCombined:0,dwFlags);
563 if(!pszBase || !pszRelative || !pcchCombined)
566 base = HeapAlloc(GetProcessHeap(), 0,
567 (3*INTERNET_MAX_URL_LENGTH) * sizeof(WCHAR));
568 relative = base + INTERNET_MAX_URL_LENGTH;
569 combined = relative + INTERNET_MAX_URL_LENGTH;
571 MultiByteToWideChar(0, 0, pszBase, -1, base, INTERNET_MAX_URL_LENGTH);
572 MultiByteToWideChar(0, 0, pszRelative, -1, relative, INTERNET_MAX_URL_LENGTH);
575 ret = UrlCombineW(base, relative, pszCombined?combined:NULL, &len, dwFlags);
578 HeapFree(GetProcessHeap(), 0, base);
582 len2 = WideCharToMultiByte(0, 0, combined, len, 0, 0, 0, 0);
583 if (len2 > *pcchCombined) {
584 *pcchCombined = len2;
585 HeapFree(GetProcessHeap(), 0, base);
588 WideCharToMultiByte(0, 0, combined, len+1, pszCombined, (*pcchCombined)+1,
590 *pcchCombined = len2;
591 HeapFree(GetProcessHeap(), 0, base);
595 /*************************************************************************
596 * UrlCombineW [SHLWAPI.@]
600 HRESULT WINAPI UrlCombineW(LPCWSTR pszBase, LPCWSTR pszRelative,
601 LPWSTR pszCombined, LPDWORD pcchCombined,
604 PARSEDURLW base, relative;
605 DWORD myflags, sizeloc = 0;
606 DWORD len, res1, res2, process_case = 0;
607 LPWSTR work, preliminary, mbase, mrelative;
608 static const WCHAR myfilestr[] = {'f','i','l','e',':','/','/','/','\0'};
609 static const WCHAR single_slash[] = {'/','\0'};
612 TRACE("(base %s, Relative %s, Combine size %ld, flags %08lx)\n",
613 debugstr_w(pszBase),debugstr_w(pszRelative),
614 pcchCombined?*pcchCombined:0,dwFlags);
616 if(!pszBase || !pszRelative || !pcchCombined)
619 base.cbSize = sizeof(base);
620 relative.cbSize = sizeof(relative);
622 /* Get space for duplicates of the input and the output */
623 preliminary = HeapAlloc(GetProcessHeap(), 0, (3*INTERNET_MAX_URL_LENGTH) *
625 mbase = preliminary + INTERNET_MAX_URL_LENGTH;
626 mrelative = mbase + INTERNET_MAX_URL_LENGTH;
627 *preliminary = L'\0';
629 /* Canonicalize the base input prior to looking for the scheme */
630 myflags = dwFlags & (URL_DONT_SIMPLIFY | URL_UNESCAPE);
631 len = INTERNET_MAX_URL_LENGTH;
632 ret = UrlCanonicalizeW(pszBase, mbase, &len, myflags);
634 /* Canonicalize the relative input prior to looking for the scheme */
635 len = INTERNET_MAX_URL_LENGTH;
636 ret = UrlCanonicalizeW(pszRelative, mrelative, &len, myflags);
638 /* See if the base has a scheme */
639 res1 = ParseURLW(mbase, &base);
641 /* if pszBase has no scheme, then return pszRelative */
642 TRACE("no scheme detected in Base\n");
647 /* get size of location field (if it exists) */
648 work = (LPWSTR)base.pszSuffix;
650 if (*work++ == L'/') {
651 if (*work++ == L'/') {
652 /* At this point have start of location and
653 * it ends at next '/' or end of string.
655 while(*work && (*work != L'/')) work++;
656 sizeloc = (DWORD)(work - base.pszSuffix);
660 /* Change .sizep2 to not have the last leaf in it,
661 * Note: we need to start after the location (if it exists)
663 work = strrchrW((base.pszSuffix+sizeloc), L'/');
665 len = (DWORD)(work - base.pszSuffix + 1);
666 base.cchSuffix = len;
670 * .pszSuffix points to location (starting with '//')
671 * .cchSuffix length of location (above) and rest less the last
673 * sizeloc length of location (above) up to but not including
677 res2 = ParseURLW(mrelative, &relative);
679 /* no scheme in pszRelative */
680 TRACE("no scheme detected in Relative\n");
681 relative.pszSuffix = mrelative; /* case 3,4,5 depends on this */
682 relative.cchSuffix = strlenW(mrelative);
683 if (*pszRelative == L':') {
684 /* case that is either left alone or uses pszBase */
685 if (dwFlags & URL_PLUGGABLE_PROTOCOL) {
692 if (isalnum(*mrelative) && (*(mrelative + 1) == L':')) {
693 /* case that becomes "file:///" */
694 strcpyW(preliminary, myfilestr);
698 if ((*mrelative == L'/') && (*(mrelative+1) == L'/')) {
699 /* pszRelative has location and rest */
703 if (*mrelative == L'/') {
704 /* case where pszRelative is root to location */
708 process_case = (*base.pszSuffix == L'/') ? 5 : 3;
712 /* handle cases where pszRelative has scheme */
713 if ((base.cchProtocol == relative.cchProtocol) &&
714 (strncmpW(base.pszProtocol, relative.pszProtocol, base.cchProtocol) == 0)) {
716 /* since the schemes are the same */
717 if ((*relative.pszSuffix == L'/') && (*(relative.pszSuffix+1) == L'/')) {
718 /* case where pszRelative replaces location and following */
722 if (*relative.pszSuffix == L'/') {
723 /* case where pszRelative is root to location */
727 /* case where scheme is followed by document path */
731 if ((*relative.pszSuffix == L'/') && (*(relative.pszSuffix+1) == L'/')) {
732 /* case where pszRelative replaces scheme, location,
733 * and following and handles PLUGGABLE
740 } while(FALSE); /* a litte trick to allow easy exit from nested if's */
744 switch (process_case) {
747 * Return pszRelative appended to what ever is in pszCombined,
748 * (which may the string "file:///"
750 strcatW(preliminary, mrelative);
754 * Same as case 1, but if URL_PLUGGABLE_PROTOCOL was specified
755 * and pszRelative starts with "//", then append a "/"
757 strcpyW(preliminary, mrelative);
758 if (!(dwFlags & URL_PLUGGABLE_PROTOCOL) &&
759 URL_JustLocation(relative.pszSuffix))
760 strcatW(preliminary, single_slash);
764 * Return the pszBase scheme with pszRelative. Basically
765 * keeps the scheme and replaces the domain and following.
767 memcpy(preliminary, base.pszProtocol, (base.cchProtocol + 1)*sizeof(WCHAR));
768 work = preliminary + base.cchProtocol + 1;
769 strcpyW(work, relative.pszSuffix);
770 if (!(dwFlags & URL_PLUGGABLE_PROTOCOL) &&
771 URL_JustLocation(relative.pszSuffix))
772 strcatW(work, single_slash);
776 * Return the pszBase scheme and location but everything
777 * after the location is pszRelative. (Replace document
780 memcpy(preliminary, base.pszProtocol, (base.cchProtocol+1+sizeloc)*sizeof(WCHAR));
781 work = preliminary + base.cchProtocol + 1 + sizeloc;
782 if (dwFlags & URL_PLUGGABLE_PROTOCOL)
784 strcpyW(work, relative.pszSuffix);
788 * Return the pszBase without its document (if any) and
789 * append pszRelative after its scheme.
791 memcpy(preliminary, base.pszProtocol,
792 (base.cchProtocol+1+base.cchSuffix)*sizeof(WCHAR));
793 work = preliminary + base.cchProtocol+1+base.cchSuffix - 1;
796 strcpyW(work, relative.pszSuffix);
800 FIXME("How did we get here????? process_case=%ld\n", process_case);
805 /* Reuse mrelative as temp storage as its already allocated and not needed anymore */
806 ret = UrlCanonicalizeW(preliminary, mrelative, pcchCombined, dwFlags);
807 if(SUCCEEDED(ret) && pszCombined) {
808 lstrcpyW(pszCombined, mrelative);
810 TRACE("return-%ld len=%ld, %s\n",
811 process_case, *pcchCombined, debugstr_w(pszCombined));
813 HeapFree(GetProcessHeap(), 0, preliminary);
817 /*************************************************************************
818 * UrlEscapeA [SHLWAPI.@]
821 HRESULT WINAPI UrlEscapeA(
827 WCHAR bufW[INTERNET_MAX_URL_LENGTH];
828 WCHAR *escapedW = bufW;
831 DWORD lenW = sizeof(bufW)/sizeof(WCHAR), lenA;
833 if(!RtlCreateUnicodeStringFromAsciiz(&urlW, pszUrl))
835 if((ret = UrlEscapeW(urlW.Buffer, escapedW, &lenW, dwFlags)) == E_POINTER) {
836 escapedW = HeapAlloc(GetProcessHeap(), 0, lenW * sizeof(WCHAR));
837 ret = UrlEscapeW(urlW.Buffer, escapedW, &lenW, dwFlags);
840 RtlUnicodeToMultiByteSize(&lenA, escapedW, lenW * sizeof(WCHAR));
841 if(*pcchEscaped > lenA) {
842 RtlUnicodeToMultiByteN(pszEscaped, *pcchEscaped - 1, &lenA, escapedW, lenW * sizeof(WCHAR));
843 pszEscaped[lenA] = 0;
846 *pcchEscaped = lenA + 1;
850 if(escapedW != bufW) HeapFree(GetProcessHeap(), 0, escapedW);
851 RtlFreeUnicodeString(&urlW);
855 #define WINE_URL_BASH_AS_SLASH 0x01
856 #define WINE_URL_COLLAPSE_SLASHES 0x02
857 #define WINE_URL_ESCAPE_SLASH 0x04
858 #define WINE_URL_ESCAPE_HASH 0x08
859 #define WINE_URL_ESCAPE_QUESTION 0x10
860 #define WINE_URL_STOP_ON_HASH 0x20
861 #define WINE_URL_STOP_ON_QUESTION 0x40
863 static inline BOOL URL_NeedEscapeW(WCHAR ch, DWORD dwFlags, DWORD int_flags)
869 if(dwFlags & URL_ESCAPE_SPACES_ONLY) {
876 if ((dwFlags & URL_ESCAPE_PERCENT) && (ch == '%'))
879 if (ch <= 31 || ch >= 127)
900 if (int_flags & WINE_URL_ESCAPE_SLASH) return TRUE;
904 if (int_flags & WINE_URL_ESCAPE_QUESTION) return TRUE;
908 if (int_flags & WINE_URL_ESCAPE_HASH) return TRUE;
918 /*************************************************************************
919 * UrlEscapeW [SHLWAPI.@]
921 * Converts unsafe characters in a Url into escape sequences.
924 * pszUrl [I] Url to modify
925 * pszEscaped [O] Destination for modified Url
926 * pcchEscaped [I/O] Length of pszUrl, destination for length of pszEscaped
927 * dwFlags [I] URL_ flags from "shlwapi.h"
930 * Success: S_OK. pszEscaped contains the escaped Url, pcchEscaped
931 * contains its length.
932 * Failure: E_POINTER, if pszEscaped is not large enough. In this case
933 * pcchEscaped is set to the required length.
935 * Converts unsafe characters into their escape sequences.
938 * - By default this function stops converting at the first '?' or
940 * - If dwFlags contains URL_ESCAPE_SPACES_ONLY then only spaces are
941 * converted, but the conversion continues past a '?' or '#'.
942 * - Note that this function did not work well (or at all) in shlwapi version 4.
945 * Only the following flags are implemented:
946 *| URL_ESCAPE_SPACES_ONLY
947 *| URL_DONT_ESCAPE_EXTRA_INFO
948 *| URL_ESCAPE_SEGMENT_ONLY
949 *| URL_ESCAPE_PERCENT
951 HRESULT WINAPI UrlEscapeW(
958 DWORD needed = 0, ret;
959 BOOL stop_escaping = FALSE;
960 WCHAR next[5], *dst = pszEscaped;
962 PARSEDURLW parsed_url;
965 static const WCHAR localhost[] = {'l','o','c','a','l','h','o','s','t',0};
967 TRACE("(%s %p %p 0x%08lx)\n", debugstr_w(pszUrl), pszEscaped,
968 pcchEscaped, dwFlags);
970 if(!pszUrl || !pszEscaped || !pcchEscaped)
973 if(dwFlags & ~(URL_ESCAPE_SPACES_ONLY |
974 URL_ESCAPE_SEGMENT_ONLY |
975 URL_DONT_ESCAPE_EXTRA_INFO |
977 FIXME("Unimplemented flags: %08lx\n", dwFlags);
980 if (dwFlags & URL_ESCAPE_SPACES_ONLY)
981 /* if SPACES_ONLY specified, reset the other controls */
982 dwFlags &= ~(URL_DONT_ESCAPE_EXTRA_INFO |
984 URL_ESCAPE_SEGMENT_ONLY);
987 /* if SPACES_ONLY *not* specified the assume DONT_ESCAPE_EXTRA_INFO */
988 dwFlags |= URL_DONT_ESCAPE_EXTRA_INFO;
992 if(dwFlags & URL_ESCAPE_SEGMENT_ONLY) {
993 int_flags = WINE_URL_ESCAPE_QUESTION | WINE_URL_ESCAPE_HASH | WINE_URL_ESCAPE_SLASH;
995 parsed_url.cbSize = sizeof(parsed_url);
996 if(ParseURLW(pszUrl, &parsed_url) != S_OK)
997 parsed_url.nScheme = URL_SCHEME_INVALID;
999 TRACE("scheme = %d (%s)\n", parsed_url.nScheme, debugstr_wn(parsed_url.pszProtocol, parsed_url.cchProtocol));
1001 if(dwFlags & URL_DONT_ESCAPE_EXTRA_INFO)
1002 int_flags = WINE_URL_STOP_ON_HASH | WINE_URL_STOP_ON_QUESTION;
1004 switch(parsed_url.nScheme) {
1005 case URL_SCHEME_FILE:
1006 int_flags |= WINE_URL_BASH_AS_SLASH | WINE_URL_COLLAPSE_SLASHES | WINE_URL_ESCAPE_HASH;
1007 int_flags &= ~WINE_URL_STOP_ON_HASH;
1010 case URL_SCHEME_HTTP:
1011 case URL_SCHEME_HTTPS:
1012 int_flags |= WINE_URL_BASH_AS_SLASH;
1013 if(parsed_url.pszSuffix[0] != '/' && parsed_url.pszSuffix[0] != '\\')
1014 int_flags |= WINE_URL_ESCAPE_SLASH;
1017 case URL_SCHEME_MAILTO:
1018 int_flags |= WINE_URL_ESCAPE_SLASH | WINE_URL_ESCAPE_QUESTION | WINE_URL_ESCAPE_HASH;
1019 int_flags &= ~(WINE_URL_STOP_ON_QUESTION | WINE_URL_STOP_ON_HASH);
1022 case URL_SCHEME_INVALID:
1025 case URL_SCHEME_FTP:
1027 if(parsed_url.pszSuffix[0] != '/')
1028 int_flags |= WINE_URL_ESCAPE_SLASH;
1033 for(src = pszUrl; *src; ) {
1037 if((int_flags & WINE_URL_COLLAPSE_SLASHES) && src == pszUrl + parsed_url.cchProtocol + 1) {
1038 int localhost_len = sizeof(localhost)/sizeof(WCHAR) - 1;
1039 while(cur == '/' || cur == '\\') {
1043 if(slashes == 2 && !strncmpiW(src, localhost, localhost_len)) { /* file://localhost/ -> file:/// */
1044 if(*(src + localhost_len) == '/' || *(src + localhost_len) == '\\')
1045 src += localhost_len + 1;
1052 next[0] = next[1] = next[2] = '/';
1059 next[0] = next[1] = '/';
1066 if(cur == '#' && (int_flags & WINE_URL_STOP_ON_HASH))
1067 stop_escaping = TRUE;
1069 if(cur == '?' && (int_flags & WINE_URL_STOP_ON_QUESTION))
1070 stop_escaping = TRUE;
1072 if(cur == '\\' && (int_flags & WINE_URL_BASH_AS_SLASH) && !stop_escaping) cur = '/';
1074 if(URL_NeedEscapeW(cur, dwFlags, int_flags) && stop_escaping == FALSE) {
1076 next[1] = hexDigits[(cur >> 4) & 0xf];
1077 next[2] = hexDigits[cur & 0xf];
1086 if(needed + len <= *pcchEscaped) {
1087 memcpy(dst, next, len*sizeof(WCHAR));
1093 if(needed < *pcchEscaped) {
1097 needed++; /* add one for the '\0' */
1100 *pcchEscaped = needed;
1105 /*************************************************************************
1106 * UrlUnescapeA [SHLWAPI.@]
1108 * Converts Url escape sequences back to ordinary characters.
1111 * pszUrl [I/O] Url to convert
1112 * pszUnescaped [O] Destination for converted Url
1113 * pcchUnescaped [I/O] Size of output string
1114 * dwFlags [I] URL_ESCAPE_ Flags from "shlwapi.h"
1117 * Success: S_OK. The converted value is in pszUnescaped, or in pszUrl if
1118 * dwFlags includes URL_ESCAPE_INPLACE.
1119 * Failure: E_POINTER if the converted Url is bigger than pcchUnescaped. In
1120 * this case pcchUnescaped is set to the size required.
1122 * If dwFlags includes URL_DONT_ESCAPE_EXTRA_INFO, the conversion stops at
1123 * the first occurrence of either a '?' or '#' character.
1125 HRESULT WINAPI UrlUnescapeA(
1128 LPDWORD pcchUnescaped,
1135 BOOL stop_unescaping = FALSE;
1137 TRACE("(%s, %p, %p, 0x%08lx)\n", debugstr_a(pszUrl), pszUnescaped,
1138 pcchUnescaped, dwFlags);
1140 if(!pszUrl || (!pszUnescaped && !(dwFlags & URL_UNESCAPE_INPLACE)) || !pcchUnescaped)
1141 return E_INVALIDARG;
1143 if(dwFlags & URL_UNESCAPE_INPLACE)
1148 for(src = pszUrl, needed = 0; *src; src++, needed++) {
1149 if(dwFlags & URL_DONT_UNESCAPE_EXTRA_INFO &&
1150 (*src == '#' || *src == '?')) {
1151 stop_unescaping = TRUE;
1153 } else if(*src == '%' && isxdigit(*(src + 1)) && isxdigit(*(src + 2))
1154 && stop_unescaping == FALSE) {
1157 memcpy(buf, src + 1, 2);
1159 ih = strtol(buf, NULL, 16);
1161 src += 2; /* Advance to end of escape */
1165 if(dwFlags & URL_UNESCAPE_INPLACE || needed < *pcchUnescaped)
1169 if(dwFlags & URL_UNESCAPE_INPLACE || needed < *pcchUnescaped) {
1173 needed++; /* add one for the '\0' */
1176 if(!(dwFlags & URL_UNESCAPE_INPLACE))
1177 *pcchUnescaped = needed;
1180 TRACE("result %s\n", (dwFlags & URL_UNESCAPE_INPLACE) ?
1181 debugstr_a(pszUrl) : debugstr_a(pszUnescaped));
1187 /*************************************************************************
1188 * UrlUnescapeW [SHLWAPI.@]
1192 HRESULT WINAPI UrlUnescapeW(
1194 LPWSTR pszUnescaped,
1195 LPDWORD pcchUnescaped,
1202 BOOL stop_unescaping = FALSE;
1204 TRACE("(%s, %p, %p, 0x%08lx)\n", debugstr_w(pszUrl), pszUnescaped,
1205 pcchUnescaped, dwFlags);
1207 if(!pszUrl || (!pszUnescaped && !(dwFlags & URL_UNESCAPE_INPLACE))|| !pcchUnescaped)
1208 return E_INVALIDARG;
1210 if(dwFlags & URL_UNESCAPE_INPLACE)
1215 for(src = pszUrl, needed = 0; *src; src++, needed++) {
1216 if(dwFlags & URL_DONT_UNESCAPE_EXTRA_INFO &&
1217 (*src == L'#' || *src == L'?')) {
1218 stop_unescaping = TRUE;
1220 } else if(*src == L'%' && isxdigitW(*(src + 1)) && isxdigitW(*(src + 2))
1221 && stop_unescaping == FALSE) {
1223 WCHAR buf[5] = {'0','x',0};
1224 memcpy(buf + 2, src + 1, 2*sizeof(WCHAR));
1226 StrToIntExW(buf, STIF_SUPPORT_HEX, &ih);
1228 src += 2; /* Advance to end of escape */
1232 if(dwFlags & URL_UNESCAPE_INPLACE || needed < *pcchUnescaped)
1236 if(dwFlags & URL_UNESCAPE_INPLACE || needed < *pcchUnescaped) {
1240 needed++; /* add one for the '\0' */
1243 if(!(dwFlags & URL_UNESCAPE_INPLACE))
1244 *pcchUnescaped = needed;
1247 TRACE("result %s\n", (dwFlags & URL_UNESCAPE_INPLACE) ?
1248 debugstr_w(pszUrl) : debugstr_w(pszUnescaped));
1254 /*************************************************************************
1255 * UrlGetLocationA [SHLWAPI.@]
1257 * Get the location from a Url.
1260 * pszUrl [I] Url to get the location from
1263 * A pointer to the start of the location in pszUrl, or NULL if there is
1267 * - MSDN erroneously states that "The location is the segment of the Url
1268 * starting with a '?' or '#' character". Neither V4 nor V5 of shlwapi.dll
1269 * stop at '?' and always return a NULL in this case.
1270 * - MSDN also erroneously states that "If a file URL has a query string,
1271 * the returned string is the query string". In all tested cases, if the
1272 * Url starts with "fi" then a NULL is returned. V5 gives the following results:
1275 *| NULL file://aa/b/cd#hohoh
1276 *| #hohoh http://aa/b/cd#hohoh
1277 *| NULL fi://aa/b/cd#hohoh
1278 *| #hohoh ff://aa/b/cd#hohoh
1280 LPCSTR WINAPI UrlGetLocationA(
1286 base.cbSize = sizeof(base);
1287 res1 = ParseURLA(pszUrl, &base);
1288 if (res1) return NULL; /* invalid scheme */
1290 /* if scheme is file: then never return pointer */
1291 if (strncmp(base.pszProtocol, "file", min(4,base.cchProtocol)) == 0) return NULL;
1293 /* Look for '#' and return its addr */
1294 return strchr(base.pszSuffix, '#');
1297 /*************************************************************************
1298 * UrlGetLocationW [SHLWAPI.@]
1300 * See UrlGetLocationA.
1302 LPCWSTR WINAPI UrlGetLocationW(
1308 base.cbSize = sizeof(base);
1309 res1 = ParseURLW(pszUrl, &base);
1310 if (res1) return NULL; /* invalid scheme */
1312 /* if scheme is file: then never return pointer */
1313 if (strncmpW(base.pszProtocol, fileW, min(4,base.cchProtocol)) == 0) return NULL;
1315 /* Look for '#' and return its addr */
1316 return strchrW(base.pszSuffix, L'#');
1319 /*************************************************************************
1320 * UrlCompareA [SHLWAPI.@]
1325 * pszUrl1 [I] First Url to compare
1326 * pszUrl2 [I] Url to compare to pszUrl1
1327 * fIgnoreSlash [I] TRUE = compare only up to a final slash
1330 * less than zero, zero, or greater than zero indicating pszUrl2 is greater
1331 * than, equal to, or less than pszUrl1 respectively.
1333 INT WINAPI UrlCompareA(
1338 INT ret, len, len1, len2;
1341 return strcmp(pszUrl1, pszUrl2);
1342 len1 = strlen(pszUrl1);
1343 if (pszUrl1[len1-1] == '/') len1--;
1344 len2 = strlen(pszUrl2);
1345 if (pszUrl2[len2-1] == '/') len2--;
1347 return strncmp(pszUrl1, pszUrl2, len1);
1348 len = min(len1, len2);
1349 ret = strncmp(pszUrl1, pszUrl2, len);
1350 if (ret) return ret;
1351 if (len1 > len2) return 1;
1355 /*************************************************************************
1356 * UrlCompareW [SHLWAPI.@]
1360 INT WINAPI UrlCompareW(
1366 size_t len, len1, len2;
1369 return strcmpW(pszUrl1, pszUrl2);
1370 len1 = strlenW(pszUrl1);
1371 if (pszUrl1[len1-1] == '/') len1--;
1372 len2 = strlenW(pszUrl2);
1373 if (pszUrl2[len2-1] == '/') len2--;
1375 return strncmpW(pszUrl1, pszUrl2, len1);
1376 len = min(len1, len2);
1377 ret = strncmpW(pszUrl1, pszUrl2, len);
1378 if (ret) return ret;
1379 if (len1 > len2) return 1;
1383 /*************************************************************************
1384 * HashData [SHLWAPI.@]
1386 * Hash an input block into a variable sized digest.
1389 * lpSrc [I] Input block
1390 * nSrcLen [I] Length of lpSrc
1391 * lpDest [I] Output for hash digest
1392 * nDestLen [I] Length of lpDest
1395 * Success: TRUE. lpDest is filled with the computed hash value.
1396 * Failure: FALSE, if any argument is invalid.
1398 HRESULT WINAPI HashData(const unsigned char *lpSrc, DWORD nSrcLen,
1399 unsigned char *lpDest, DWORD nDestLen)
1401 INT srcCount = nSrcLen - 1, destCount = nDestLen - 1;
1403 if (IsBadReadPtr(lpSrc, nSrcLen) ||
1404 IsBadWritePtr(lpDest, nDestLen))
1405 return E_INVALIDARG;
1407 while (destCount >= 0)
1409 lpDest[destCount] = (destCount & 0xff);
1413 while (srcCount >= 0)
1415 destCount = nDestLen - 1;
1416 while (destCount >= 0)
1418 lpDest[destCount] = HashDataLookup[lpSrc[srcCount] ^ lpDest[destCount]];
1426 /*************************************************************************
1427 * UrlHashA [SHLWAPI.@]
1429 * Produce a Hash from a Url.
1432 * pszUrl [I] Url to hash
1433 * lpDest [O] Destinationh for hash
1434 * nDestLen [I] Length of lpDest
1437 * Success: S_OK. lpDest is filled with the computed hash value.
1438 * Failure: E_INVALIDARG, if any argument is invalid.
1440 HRESULT WINAPI UrlHashA(LPCSTR pszUrl, unsigned char *lpDest, DWORD nDestLen)
1442 if (IsBadStringPtrA(pszUrl, -1) || IsBadWritePtr(lpDest, nDestLen))
1443 return E_INVALIDARG;
1445 HashData((const BYTE*)pszUrl, (int)strlen(pszUrl), lpDest, nDestLen);
1449 /*************************************************************************
1450 * UrlHashW [SHLWAPI.@]
1454 HRESULT WINAPI UrlHashW(LPCWSTR pszUrl, unsigned char *lpDest, DWORD nDestLen)
1456 char szUrl[MAX_PATH];
1458 TRACE("(%s,%p,%ld)\n",debugstr_w(pszUrl), lpDest, nDestLen);
1460 if (IsBadStringPtrW(pszUrl, -1) || IsBadWritePtr(lpDest, nDestLen))
1461 return E_INVALIDARG;
1463 /* Win32 hashes the data as an ASCII string, presumably so that both A+W
1464 * return the same digests for the same URL.
1466 WideCharToMultiByte(0, 0, pszUrl, -1, szUrl, MAX_PATH, 0, 0);
1467 HashData((const BYTE*)szUrl, (int)strlen(szUrl), lpDest, nDestLen);
1471 /*************************************************************************
1472 * UrlApplySchemeA [SHLWAPI.@]
1474 * Apply a scheme to a Url.
1477 * pszIn [I] Url to apply scheme to
1478 * pszOut [O] Destination for modified Url
1479 * pcchOut [I/O] Length of pszOut/destination for length of pszOut
1480 * dwFlags [I] URL_ flags from "shlwapi.h"
1483 * Success: S_OK: pszOut contains the modified Url, pcchOut contains its length.
1484 * Failure: An HRESULT error code describing the error.
1486 HRESULT WINAPI UrlApplySchemeA(LPCSTR pszIn, LPSTR pszOut, LPDWORD pcchOut, DWORD dwFlags)
1489 DWORD ret, len, len2;
1491 TRACE("(in %s, out size %ld, flags %08lx) using W version\n",
1492 debugstr_a(pszIn), *pcchOut, dwFlags);
1494 in = HeapAlloc(GetProcessHeap(), 0,
1495 (2*INTERNET_MAX_URL_LENGTH) * sizeof(WCHAR));
1496 out = in + INTERNET_MAX_URL_LENGTH;
1498 MultiByteToWideChar(0, 0, pszIn, -1, in, INTERNET_MAX_URL_LENGTH);
1499 len = INTERNET_MAX_URL_LENGTH;
1501 ret = UrlApplySchemeW(in, out, &len, dwFlags);
1502 if ((ret != S_OK) && (ret != S_FALSE)) {
1503 HeapFree(GetProcessHeap(), 0, in);
1507 len2 = WideCharToMultiByte(0, 0, out, len+1, 0, 0, 0, 0);
1508 if (len2 > *pcchOut) {
1510 HeapFree(GetProcessHeap(), 0, in);
1513 WideCharToMultiByte(0, 0, out, len+1, pszOut, *pcchOut, 0, 0);
1515 HeapFree(GetProcessHeap(), 0, in);
1519 static HRESULT URL_GuessScheme(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut)
1524 DWORD value_len, data_len, dwType, i;
1525 WCHAR reg_path[MAX_PATH];
1526 WCHAR value[MAX_PATH], data[MAX_PATH];
1529 MultiByteToWideChar(0, 0,
1530 "Software\\Microsoft\\Windows\\CurrentVersion\\URL\\Prefixes",
1531 -1, reg_path, MAX_PATH);
1532 RegOpenKeyExW(HKEY_LOCAL_MACHINE, reg_path, 0, 1, &newkey);
1534 while(value_len = data_len = MAX_PATH,
1535 RegEnumValueW(newkey, index, value, &value_len,
1536 0, &dwType, (LPVOID)data, &data_len) == 0) {
1537 TRACE("guess %d %s is %s\n",
1538 index, debugstr_w(value), debugstr_w(data));
1541 for(i=0; i<value_len; i++) {
1544 /* remember that TRUE is not-equal */
1545 j = ChrCmpIW(Wxx, Wyy);
1548 if ((i == value_len) && !j) {
1549 if (strlenW(data) + strlenW(pszIn) + 1 > *pcchOut) {
1550 *pcchOut = strlenW(data) + strlenW(pszIn) + 1;
1551 RegCloseKey(newkey);
1554 strcpyW(pszOut, data);
1555 strcatW(pszOut, pszIn);
1556 *pcchOut = strlenW(pszOut);
1557 TRACE("matched and set to %s\n", debugstr_w(pszOut));
1558 RegCloseKey(newkey);
1563 RegCloseKey(newkey);
1567 static HRESULT URL_ApplyDefault(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut)
1570 DWORD data_len, dwType;
1571 WCHAR reg_path[MAX_PATH];
1572 WCHAR value[MAX_PATH], data[MAX_PATH];
1574 /* get and prepend default */
1575 MultiByteToWideChar(0, 0,
1576 "Software\\Microsoft\\Windows\\CurrentVersion\\URL\\DefaultPrefix",
1577 -1, reg_path, MAX_PATH);
1578 RegOpenKeyExW(HKEY_LOCAL_MACHINE, reg_path, 0, 1, &newkey);
1579 data_len = MAX_PATH;
1582 RegQueryValueExW(newkey, value, 0, &dwType, (LPBYTE)data, &data_len);
1583 RegCloseKey(newkey);
1584 if (strlenW(data) + strlenW(pszIn) + 1 > *pcchOut) {
1585 *pcchOut = strlenW(data) + strlenW(pszIn) + 1;
1588 strcpyW(pszOut, data);
1589 strcatW(pszOut, pszIn);
1590 *pcchOut = strlenW(pszOut);
1591 TRACE("used default %s\n", debugstr_w(pszOut));
1595 /*************************************************************************
1596 * UrlApplySchemeW [SHLWAPI.@]
1598 * See UrlApplySchemeA.
1600 HRESULT WINAPI UrlApplySchemeW(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut, DWORD dwFlags)
1602 PARSEDURLW in_scheme;
1606 TRACE("(in %s, out size %ld, flags %08lx)\n",
1607 debugstr_w(pszIn), *pcchOut, dwFlags);
1609 if (dwFlags & URL_APPLY_GUESSFILE) {
1610 FIXME("(%s %p %p(%ld) 0x%08lx): stub URL_APPLY_GUESSFILE not implemented\n",
1611 debugstr_w(pszIn), pszOut, pcchOut, *pcchOut, dwFlags);
1612 strcpyW(pszOut, pszIn);
1613 *pcchOut = strlenW(pszOut);
1617 in_scheme.cbSize = sizeof(in_scheme);
1618 /* See if the base has a scheme */
1619 res1 = ParseURLW(pszIn, &in_scheme);
1621 /* no scheme in input, need to see if we need to guess */
1622 if (dwFlags & URL_APPLY_GUESSSCHEME) {
1623 if ((ret = URL_GuessScheme(pszIn, pszOut, pcchOut)) != -1)
1628 /* we have a scheme, see if valid (known scheme) */
1629 if (in_scheme.nScheme) {
1630 /* have valid scheme, so just copy and exit */
1631 if (strlenW(pszIn) + 1 > *pcchOut) {
1632 *pcchOut = strlenW(pszIn) + 1;
1635 strcpyW(pszOut, pszIn);
1636 *pcchOut = strlenW(pszOut);
1637 TRACE("valid scheme, returing copy\n");
1642 /* If we are here, then either invalid scheme,
1643 * or no scheme and can't/failed guess.
1645 if ( ( ((res1 == 0) && (dwFlags & URL_APPLY_FORCEAPPLY)) ||
1647 (dwFlags & URL_APPLY_DEFAULT)) {
1648 /* find and apply default scheme */
1649 return URL_ApplyDefault(pszIn, pszOut, pcchOut);
1652 /* just copy and give proper return code */
1653 if (strlenW(pszIn) + 1 > *pcchOut) {
1654 *pcchOut = strlenW(pszIn) + 1;
1657 strcpyW(pszOut, pszIn);
1658 *pcchOut = strlenW(pszOut);
1659 TRACE("returning copy, left alone\n");
1663 /*************************************************************************
1664 * UrlIsA [SHLWAPI.@]
1666 * Determine if a Url is of a certain class.
1669 * pszUrl [I] Url to check
1670 * Urlis [I] URLIS_ constant from "shlwapi.h"
1673 * TRUE if pszUrl belongs to the class type in Urlis.
1676 BOOL WINAPI UrlIsA(LPCSTR pszUrl, URLIS Urlis)
1682 TRACE("(%s %d)\n", debugstr_a(pszUrl), Urlis);
1687 base.cbSize = sizeof(base);
1688 res1 = ParseURLA(pszUrl, &base);
1689 if (res1) return FALSE; /* invalid scheme */
1690 switch (base.nScheme)
1692 case URL_SCHEME_MAILTO:
1693 case URL_SCHEME_SHELL:
1694 case URL_SCHEME_JAVASCRIPT:
1695 case URL_SCHEME_VBSCRIPT:
1696 case URL_SCHEME_ABOUT:
1702 return !StrCmpNA("file:", pszUrl, 5);
1704 case URLIS_DIRECTORY:
1705 last = pszUrl + strlen(pszUrl) - 1;
1706 return (last >= pszUrl && (*last == '/' || *last == '\\' ));
1709 return PathIsURLA(pszUrl);
1711 case URLIS_NOHISTORY:
1712 case URLIS_APPLIABLE:
1713 case URLIS_HASQUERY:
1715 FIXME("(%s %d): stub\n", debugstr_a(pszUrl), Urlis);
1720 /*************************************************************************
1721 * UrlIsW [SHLWAPI.@]
1725 BOOL WINAPI UrlIsW(LPCWSTR pszUrl, URLIS Urlis)
1727 static const WCHAR stemp[] = { 'f','i','l','e',':',0 };
1732 TRACE("(%s %d)\n", debugstr_w(pszUrl), Urlis);
1737 base.cbSize = sizeof(base);
1738 res1 = ParseURLW(pszUrl, &base);
1739 if (res1) return FALSE; /* invalid scheme */
1740 switch (base.nScheme)
1742 case URL_SCHEME_MAILTO:
1743 case URL_SCHEME_SHELL:
1744 case URL_SCHEME_JAVASCRIPT:
1745 case URL_SCHEME_VBSCRIPT:
1746 case URL_SCHEME_ABOUT:
1752 return !strncmpW(stemp, pszUrl, 5);
1754 case URLIS_DIRECTORY:
1755 last = pszUrl + strlenW(pszUrl) - 1;
1756 return (last >= pszUrl && (*last == '/' || *last == '\\'));
1759 return PathIsURLW(pszUrl);
1761 case URLIS_NOHISTORY:
1762 case URLIS_APPLIABLE:
1763 case URLIS_HASQUERY:
1765 FIXME("(%s %d): stub\n", debugstr_w(pszUrl), Urlis);
1770 /*************************************************************************
1771 * UrlIsNoHistoryA [SHLWAPI.@]
1773 * Determine if a Url should not be stored in the users history list.
1776 * pszUrl [I] Url to check
1779 * TRUE, if pszUrl should be excluded from the history list,
1782 BOOL WINAPI UrlIsNoHistoryA(LPCSTR pszUrl)
1784 return UrlIsA(pszUrl, URLIS_NOHISTORY);
1787 /*************************************************************************
1788 * UrlIsNoHistoryW [SHLWAPI.@]
1790 * See UrlIsNoHistoryA.
1792 BOOL WINAPI UrlIsNoHistoryW(LPCWSTR pszUrl)
1794 return UrlIsW(pszUrl, URLIS_NOHISTORY);
1797 /*************************************************************************
1798 * UrlIsOpaqueA [SHLWAPI.@]
1800 * Determine if a Url is opaque.
1803 * pszUrl [I] Url to check
1806 * TRUE if pszUrl is opaque,
1810 * An opaque Url is one that does not start with "<protocol>://".
1812 BOOL WINAPI UrlIsOpaqueA(LPCSTR pszUrl)
1814 return UrlIsA(pszUrl, URLIS_OPAQUE);
1817 /*************************************************************************
1818 * UrlIsOpaqueW [SHLWAPI.@]
1822 BOOL WINAPI UrlIsOpaqueW(LPCWSTR pszUrl)
1824 return UrlIsW(pszUrl, URLIS_OPAQUE);
1827 /*************************************************************************
1828 * Scans for characters of type "type" and when not matching found,
1829 * returns pointer to it and length in size.
1831 * Characters tested based on RFC 1738
1833 static LPCWSTR URL_ScanID(LPCWSTR start, LPDWORD size, WINE_URL_SCAN_TYPE type)
1835 static DWORD alwayszero = 0;
1844 if ( (islowerW(*start) && isalphaW(*start)) ||
1859 if ( isalphaW(*start) ||
1861 /* user/password only characters */
1866 /* *extra* characters */
1869 (*start == L'\'') ||
1873 /* *safe* characters */
1881 } else if (*start == L'%') {
1882 if (isxdigitW(*(start+1)) &&
1883 isxdigitW(*(start+2))) {
1895 if (isdigitW(*start)) {
1906 if (isalnumW(*start) ||
1908 (*start == L'.') ) {
1917 FIXME("unknown type %d\n", type);
1918 return (LPWSTR)&alwayszero;
1920 /* TRACE("scanned %ld characters next char %p<%c>\n",
1921 *size, start, *start); */
1925 /*************************************************************************
1926 * Attempt to parse URL into pieces.
1928 static LONG URL_ParseUrl(LPCWSTR pszUrl, WINE_PARSE_URL *pl)
1932 memset(pl, 0, sizeof(WINE_PARSE_URL));
1933 pl->pScheme = pszUrl;
1934 work = URL_ScanID(pl->pScheme, &pl->szScheme, SCHEME);
1935 if (!*work || (*work != L':')) goto ErrorExit;
1937 if ((*work != L'/') || (*(work+1) != L'/')) goto ErrorExit;
1938 pl->pUserName = work + 2;
1939 work = URL_ScanID(pl->pUserName, &pl->szUserName, USERPASS);
1940 if (*work == L':' ) {
1941 /* parse password */
1943 pl->pPassword = work;
1944 work = URL_ScanID(pl->pPassword, &pl->szPassword, USERPASS);
1945 if (*work != L'@') {
1946 /* what we just parsed must be the hostname and port
1947 * so reset pointers and clear then let it parse */
1948 pl->szUserName = pl->szPassword = 0;
1949 work = pl->pUserName - 1;
1950 pl->pUserName = pl->pPassword = 0;
1952 } else if (*work == L'@') {
1956 } else if (!*work || (*work == L'/') || (*work == L'.')) {
1957 /* what was parsed was hostname, so reset pointers and let it parse */
1958 pl->szUserName = pl->szPassword = 0;
1959 work = pl->pUserName - 1;
1960 pl->pUserName = pl->pPassword = 0;
1961 } else goto ErrorExit;
1963 /* now start parsing hostname or hostnumber */
1965 pl->pHostName = work;
1966 work = URL_ScanID(pl->pHostName, &pl->szHostName, HOST);
1967 if (*work == L':') {
1971 work = URL_ScanID(pl->pPort, &pl->szPort, PORT);
1973 if (*work == L'/') {
1974 /* see if query string */
1975 pl->pQuery = strchrW(work, L'?');
1976 if (pl->pQuery) pl->szQuery = strlenW(pl->pQuery);
1978 TRACE("parse successful: scheme=%p(%ld), user=%p(%ld), pass=%p(%ld), host=%p(%ld), port=%p(%ld), query=%p(%ld)\n",
1979 pl->pScheme, pl->szScheme,
1980 pl->pUserName, pl->szUserName,
1981 pl->pPassword, pl->szPassword,
1982 pl->pHostName, pl->szHostName,
1983 pl->pPort, pl->szPort,
1984 pl->pQuery, pl->szQuery);
1987 FIXME("failed to parse %s\n", debugstr_w(pszUrl));
1988 return E_INVALIDARG;
1991 /*************************************************************************
1992 * UrlGetPartA [SHLWAPI.@]
1994 * Retrieve part of a Url.
1997 * pszIn [I] Url to parse
1998 * pszOut [O] Destination for part of pszIn requested
1999 * pcchOut [I] Size of pszOut
2000 * [O] length of pszOut string EXLUDING '\0' if S_OK, otherwise
2001 * needed size of pszOut INCLUDING '\0'.
2002 * dwPart [I] URL_PART_ enum from "shlwapi.h"
2003 * dwFlags [I] URL_ flags from "shlwapi.h"
2006 * Success: S_OK. pszOut contains the part requested, pcchOut contains its length.
2007 * Failure: An HRESULT error code describing the error.
2009 HRESULT WINAPI UrlGetPartA(LPCSTR pszIn, LPSTR pszOut, LPDWORD pcchOut,
2010 DWORD dwPart, DWORD dwFlags)
2013 DWORD ret, len, len2;
2015 in = HeapAlloc(GetProcessHeap(), 0,
2016 (2*INTERNET_MAX_URL_LENGTH) * sizeof(WCHAR));
2017 out = in + INTERNET_MAX_URL_LENGTH;
2019 MultiByteToWideChar(0, 0, pszIn, -1, in, INTERNET_MAX_URL_LENGTH);
2021 len = INTERNET_MAX_URL_LENGTH;
2022 ret = UrlGetPartW(in, out, &len, dwPart, dwFlags);
2025 HeapFree(GetProcessHeap(), 0, in);
2029 len2 = WideCharToMultiByte(0, 0, out, len, 0, 0, 0, 0);
2030 if (len2 > *pcchOut) {
2032 HeapFree(GetProcessHeap(), 0, in);
2035 WideCharToMultiByte(0, 0, out, len+1, pszOut, *pcchOut, 0, 0);
2037 HeapFree(GetProcessHeap(), 0, in);
2041 /*************************************************************************
2042 * UrlGetPartW [SHLWAPI.@]
2046 HRESULT WINAPI UrlGetPartW(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut,
2047 DWORD dwPart, DWORD dwFlags)
2051 DWORD size, schsize;
2052 LPCWSTR addr, schaddr;
2054 TRACE("(%s %p %p(%ld) %08lx %08lx)\n",
2055 debugstr_w(pszIn), pszOut, pcchOut, *pcchOut, dwPart, dwFlags);
2057 ret = URL_ParseUrl(pszIn, &pl);
2059 schaddr = pl.pScheme;
2060 schsize = pl.szScheme;
2063 case URL_PART_SCHEME:
2064 if (!pl.szScheme) return E_INVALIDARG;
2069 case URL_PART_HOSTNAME:
2070 if (!pl.szHostName) return E_INVALIDARG;
2071 addr = pl.pHostName;
2072 size = pl.szHostName;
2075 case URL_PART_USERNAME:
2076 if (!pl.szUserName) return E_INVALIDARG;
2077 addr = pl.pUserName;
2078 size = pl.szUserName;
2081 case URL_PART_PASSWORD:
2082 if (!pl.szPassword) return E_INVALIDARG;
2083 addr = pl.pPassword;
2084 size = pl.szPassword;
2088 if (!pl.szPort) return E_INVALIDARG;
2093 case URL_PART_QUERY:
2094 if (!pl.szQuery) return E_INVALIDARG;
2100 return E_INVALIDARG;
2103 if (dwFlags == URL_PARTFLAG_KEEPSCHEME) {
2104 if (*pcchOut < schsize + size + 2) {
2105 *pcchOut = schsize + size + 2;
2108 memcpy(pszOut, schaddr, schsize*sizeof(WCHAR));
2109 pszOut[schsize] = ':';
2110 memcpy(pszOut+schsize+1, addr, size*sizeof(WCHAR));
2111 pszOut[schsize+1+size] = 0;
2112 *pcchOut = schsize + 1 + size;
2115 if (*pcchOut < size + 1) {*pcchOut = size+1; return E_POINTER;}
2116 memcpy(pszOut, addr, size*sizeof(WCHAR));
2120 TRACE("len=%ld %s\n", *pcchOut, debugstr_w(pszOut));
2125 /*************************************************************************
2126 * PathIsURLA [SHLWAPI.@]
2128 * Check if the given path is a Url.
2131 * lpszPath [I] Path to check.
2134 * TRUE if lpszPath is a Url.
2135 * FALSE if lpszPath is NULL or not a Url.
2137 BOOL WINAPI PathIsURLA(LPCSTR lpstrPath)
2142 if (!lpstrPath || !*lpstrPath) return FALSE;
2145 base.cbSize = sizeof(base);
2146 res1 = ParseURLA(lpstrPath, &base);
2147 return (base.nScheme != URL_SCHEME_INVALID);
2150 /*************************************************************************
2151 * PathIsURLW [SHLWAPI.@]
2155 BOOL WINAPI PathIsURLW(LPCWSTR lpstrPath)
2160 if (!lpstrPath || !*lpstrPath) return FALSE;
2163 base.cbSize = sizeof(base);
2164 res1 = ParseURLW(lpstrPath, &base);
2165 return (base.nScheme != URL_SCHEME_INVALID);
2168 /*************************************************************************
2169 * UrlCreateFromPathA [SHLWAPI.@]
2171 * See UrlCreateFromPathW
2173 HRESULT WINAPI UrlCreateFromPathA(LPCSTR pszPath, LPSTR pszUrl, LPDWORD pcchUrl, DWORD dwReserved)
2175 WCHAR bufW[INTERNET_MAX_URL_LENGTH];
2177 UNICODE_STRING pathW;
2179 DWORD lenW = sizeof(bufW)/sizeof(WCHAR), lenA;
2181 if(!RtlCreateUnicodeStringFromAsciiz(&pathW, pszPath))
2182 return E_INVALIDARG;
2183 if((ret = UrlCreateFromPathW(pathW.Buffer, urlW, &lenW, dwReserved)) == E_POINTER) {
2184 urlW = HeapAlloc(GetProcessHeap(), 0, lenW * sizeof(WCHAR));
2185 ret = UrlCreateFromPathW(pathW.Buffer, urlW, &lenW, dwReserved);
2187 if(ret == S_OK || ret == S_FALSE) {
2188 RtlUnicodeToMultiByteSize(&lenA, urlW, lenW * sizeof(WCHAR));
2189 if(*pcchUrl > lenA) {
2190 RtlUnicodeToMultiByteN(pszUrl, *pcchUrl - 1, &lenA, urlW, lenW * sizeof(WCHAR));
2194 *pcchUrl = lenA + 1;
2198 if(urlW != bufW) HeapFree(GetProcessHeap(), 0, urlW);
2199 RtlFreeUnicodeString(&pathW);
2203 /*************************************************************************
2204 * UrlCreateFromPathW [SHLWAPI.@]
2206 * Create a Url from a file path.
2209 * pszPath [I] Path to convert
2210 * pszUrl [O] Destination for the converted Url
2211 * pcchUrl [I/O] Length of pszUrl
2212 * dwReserved [I] Reserved, must be 0
2215 * Success: S_OK pszUrl contains the converted path, S_FALSE if the path is already a Url
2216 * Failure: An HRESULT error code.
2218 HRESULT WINAPI UrlCreateFromPathW(LPCWSTR pszPath, LPWSTR pszUrl, LPDWORD pcchUrl, DWORD dwReserved)
2223 WCHAR file_colonW[] = {'f','i','l','e',':',0};
2224 WCHAR three_slashesW[] = {'/','/','/',0};
2225 PARSEDURLW parsed_url;
2227 TRACE("(%s, %p, %p, 0x%08lx)\n", debugstr_w(pszPath), pszUrl, pcchUrl, dwReserved);
2229 /* Validate arguments */
2230 if (dwReserved != 0)
2231 return E_INVALIDARG;
2232 if (!pszUrl || !pcchUrl)
2233 return E_INVALIDARG;
2236 parsed_url.cbSize = sizeof(parsed_url);
2237 if(ParseURLW(pszPath, &parsed_url) == S_OK) {
2238 if(parsed_url.nScheme != URL_SCHEME_INVALID && parsed_url.cchProtocol > 1) {
2239 needed = strlenW(pszPath);
2240 if (needed >= *pcchUrl) {
2241 *pcchUrl = needed + 1;
2245 strcpyW(pszUrl, pszPath);
2251 pszNewUrl = HeapAlloc(GetProcessHeap(), 0, (strlenW(pszPath) + 9) * sizeof(WCHAR)); /* "file:///" + pszPath_len + 1 */
2252 strcpyW(pszNewUrl, file_colonW);
2253 if(isalphaW(pszPath[0]) && pszPath[1] == ':')
2254 strcatW(pszNewUrl, three_slashesW);
2255 strcatW(pszNewUrl, pszPath);
2256 ret = UrlEscapeW(pszNewUrl, pszUrl, pcchUrl, URL_ESCAPE_PERCENT);
2258 HeapFree(GetProcessHeap(), 0, pszNewUrl);
2262 /*************************************************************************
2263 * SHAutoComplete [SHLWAPI.@]
2265 * Enable auto-completion for an edit control.
2268 * hwndEdit [I] Handle of control to enable auto-completion for
2269 * dwFlags [I] SHACF_ flags from "shlwapi.h"
2272 * Success: S_OK. Auto-completion is enabled for the control.
2273 * Failure: An HRESULT error code indicating the error.
2275 HRESULT WINAPI SHAutoComplete(HWND hwndEdit, DWORD dwFlags)
2277 FIXME("SHAutoComplete stub\n");
2281 /*************************************************************************
2282 * MLBuildResURLA [SHLWAPI.405]
2284 * Create a Url pointing to a resource in a module.
2287 * lpszLibName [I] Name of the module containing the resource
2288 * hMod [I] Callers module handle
2289 * dwFlags [I] Undocumented flags for loading the module
2290 * lpszRes [I] Resource name
2291 * lpszDest [O] Destination for resulting Url
2292 * dwDestLen [I] Length of lpszDest
2295 * Success: S_OK. lpszDest constains the resource Url.
2296 * Failure: E_INVALIDARG, if any argument is invalid, or
2297 * E_FAIL if dwDestLen is too small.
2299 HRESULT WINAPI MLBuildResURLA(LPCSTR lpszLibName, HMODULE hMod, DWORD dwFlags,
2300 LPCSTR lpszRes, LPSTR lpszDest, DWORD dwDestLen)
2302 WCHAR szLibName[MAX_PATH], szRes[MAX_PATH], szDest[MAX_PATH];
2306 MultiByteToWideChar(CP_ACP, 0, lpszLibName, -1, szLibName, sizeof(szLibName)/sizeof(WCHAR));
2309 MultiByteToWideChar(CP_ACP, 0, lpszRes, -1, szRes, sizeof(szRes)/sizeof(WCHAR));
2311 if (dwDestLen > sizeof(szLibName)/sizeof(WCHAR))
2312 dwDestLen = sizeof(szLibName)/sizeof(WCHAR);
2314 hRet = MLBuildResURLW(lpszLibName ? szLibName : NULL, hMod, dwFlags,
2315 lpszRes ? szRes : NULL, lpszDest ? szDest : NULL, dwDestLen);
2316 if (SUCCEEDED(hRet) && lpszDest)
2317 WideCharToMultiByte(CP_ACP, 0, szDest, -1, lpszDest, dwDestLen, 0, 0);
2322 /*************************************************************************
2323 * MLBuildResURLA [SHLWAPI.406]
2325 * See MLBuildResURLA.
2327 HRESULT WINAPI MLBuildResURLW(LPCWSTR lpszLibName, HMODULE hMod, DWORD dwFlags,
2328 LPCWSTR lpszRes, LPWSTR lpszDest, DWORD dwDestLen)
2330 static const WCHAR szRes[] = { 'r','e','s',':','/','/','\0' };
2331 #define szResLen ((sizeof(szRes) - sizeof(WCHAR))/sizeof(WCHAR))
2332 HRESULT hRet = E_FAIL;
2334 TRACE("(%s,%p,0x%08lx,%s,%p,%ld)\n", debugstr_w(lpszLibName), hMod, dwFlags,
2335 debugstr_w(lpszRes), lpszDest, dwDestLen);
2337 if (!lpszLibName || !hMod || hMod == INVALID_HANDLE_VALUE || !lpszRes ||
2338 !lpszDest || (dwFlags && dwFlags != 2))
2339 return E_INVALIDARG;
2341 if (dwDestLen >= szResLen + 1)
2343 dwDestLen -= (szResLen + 1);
2344 memcpy(lpszDest, szRes, sizeof(szRes));
2346 hMod = MLLoadLibraryW(lpszLibName, hMod, dwFlags);
2350 WCHAR szBuff[MAX_PATH];
2353 len = GetModuleFileNameW(hMod, szBuff, sizeof(szBuff)/sizeof(WCHAR));
2354 if (len && len < sizeof(szBuff)/sizeof(WCHAR))
2356 DWORD dwPathLen = strlenW(szBuff) + 1;
2358 if (dwDestLen >= dwPathLen)
2362 dwDestLen -= dwPathLen;
2363 memcpy(lpszDest + szResLen, szBuff, dwPathLen * sizeof(WCHAR));
2365 dwResLen = strlenW(lpszRes) + 1;
2366 if (dwDestLen >= dwResLen + 1)
2368 lpszDest[szResLen + dwPathLen + dwResLen] = '/';
2369 memcpy(lpszDest + szResLen + dwPathLen, lpszRes, dwResLen * sizeof(WCHAR));
2374 MLFreeLibrary(hMod);