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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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 = (LPSTR)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, root;
334 TRACE("(%s %p %p 0x%08lx)\n", debugstr_w(pszUrl), pszCanonicalized,
335 pcchCanonicalized, dwFlags);
337 if(!pszUrl || !pszCanonicalized || !pcchCanonicalized)
340 nByteLen = (lstrlenW(pszUrl) + 1) * sizeof(WCHAR); /* length in bytes */
341 lpszUrlCpy = HeapAlloc(GetProcessHeap(), 0, nByteLen);
343 if (dwFlags & URL_DONT_SIMPLIFY)
344 memcpy(lpszUrlCpy, pszUrl, nByteLen);
350 * 1 have 2[+] alnum 2,3
351 * 2 have scheme (found :) 4,6,3
352 * 3 failed (no location)
354 * 5 have 1[+] alnum 6,3
355 * 6 have location (found /) save root location
358 wk1 = (LPWSTR)pszUrl;
364 if (!isalnumW(*wk1)) {state = 3; break;}
366 if (!isalnumW(*wk1)) {state = 3; break;}
372 if (*wk1++ == L':') state = 2;
375 if (*wk1 != L'/') {state = 3; break;}
377 if (*wk1 != L'/') {state = 6; break;}
387 if (!isalnumW(*wk1) && (*wk1 != L'-') && (*wk1 != L'.')) {state = 3; break;}
388 while(isalnumW(*wk1) || (*wk1 == L'-') || (*wk1 == L'.')) *wk2++ = *wk1++;
392 if (*wk1 != L'/') {state = 3; break;}
397 /* Now at root location, cannot back up any more. */
398 /* "root" will point at the '/' */
401 TRACE("wk1=%c\n", (CHAR)*wk1);
402 mp = strchrW(wk1, L'/');
410 strncpyW(wk2, wk1, nLen);
414 TRACE("found '/.'\n");
415 if (*(wk1+1) == L'/') {
416 /* case of /./ -> skip the ./ */
419 else if (*(wk1+1) == L'.') {
420 /* found /.. look for next / */
421 TRACE("found '/..'\n");
422 if (*(wk1+2) == L'/' || *(wk1+2) == L'?' || *(wk1+2) == L'#' || *(wk1+2) == 0) {
423 /* case /../ -> need to backup wk2 */
424 TRACE("found '/../'\n");
425 *(wk2-1) = L'\0'; /* set end of string */
426 mp = strrchrW(root, L'/');
427 if (mp && (mp >= root)) {
428 /* found valid backup point */
436 /* did not find point, restore '/' */
446 FIXME("how did we get here - state=%d\n", state);
451 TRACE("Simplified, orig <%s>, simple <%s>\n",
452 debugstr_w(pszUrl), debugstr_w(lpszUrlCpy));
454 nLen = lstrlenW(lpszUrlCpy);
455 while ((nLen > 0) && ((lpszUrlCpy[nLen-1] == '\r')||(lpszUrlCpy[nLen-1] == '\n')))
456 lpszUrlCpy[--nLen]=0;
458 if(dwFlags & URL_UNESCAPE)
459 UrlUnescapeW(lpszUrlCpy, NULL, NULL, URL_UNESCAPE_INPLACE);
461 if((EscapeFlags = dwFlags & (URL_ESCAPE_UNSAFE |
462 URL_ESCAPE_SPACES_ONLY |
464 URL_DONT_ESCAPE_EXTRA_INFO |
465 URL_ESCAPE_SEGMENT_ONLY ))) {
466 EscapeFlags &= ~URL_ESCAPE_UNSAFE;
467 hr = UrlEscapeW(lpszUrlCpy, pszCanonicalized, pcchCanonicalized,
469 } else { /* No escaping needed, just copy the string */
470 nLen = lstrlenW(lpszUrlCpy);
471 if(nLen < *pcchCanonicalized)
472 memcpy(pszCanonicalized, lpszUrlCpy, (nLen + 1)*sizeof(WCHAR));
477 *pcchCanonicalized = nLen;
480 HeapFree(GetProcessHeap(), 0, lpszUrlCpy);
483 TRACE("result %s\n", debugstr_w(pszCanonicalized));
488 /*************************************************************************
489 * UrlCombineA [SHLWAPI.@]
494 * pszBase [I] Base Url
495 * pszRelative [I] Url to combine with pszBase
496 * pszCombined [O] Destination for combined Url
497 * pcchCombined [O] Destination for length of pszCombined
498 * dwFlags [I] URL_ flags from "shlwapi.h"
501 * Success: S_OK. pszCombined contains the combined Url, pcchCombined
502 * contains its length.
503 * Failure: An HRESULT error code indicating the error.
505 HRESULT WINAPI UrlCombineA(LPCSTR pszBase, LPCSTR pszRelative,
506 LPSTR pszCombined, LPDWORD pcchCombined,
509 LPWSTR base, relative, combined;
510 DWORD ret, len, len2;
512 TRACE("(base %s, Relative %s, Combine size %ld, flags %08lx) using W version\n",
513 debugstr_a(pszBase),debugstr_a(pszRelative),
514 pcchCombined?*pcchCombined:0,dwFlags);
516 if(!pszBase || !pszRelative || !pcchCombined)
519 base = (LPWSTR) HeapAlloc(GetProcessHeap(), 0,
520 (3*INTERNET_MAX_URL_LENGTH) * sizeof(WCHAR));
521 relative = base + INTERNET_MAX_URL_LENGTH;
522 combined = relative + INTERNET_MAX_URL_LENGTH;
524 MultiByteToWideChar(0, 0, pszBase, -1, base, INTERNET_MAX_URL_LENGTH);
525 MultiByteToWideChar(0, 0, pszRelative, -1, relative, INTERNET_MAX_URL_LENGTH);
528 ret = UrlCombineW(base, relative, pszCombined?combined:NULL, &len, dwFlags);
531 HeapFree(GetProcessHeap(), 0, base);
535 len2 = WideCharToMultiByte(0, 0, combined, len, 0, 0, 0, 0);
536 if (len2 > *pcchCombined) {
537 *pcchCombined = len2;
538 HeapFree(GetProcessHeap(), 0, base);
541 WideCharToMultiByte(0, 0, combined, len+1, pszCombined, (*pcchCombined)+1,
543 *pcchCombined = len2;
544 HeapFree(GetProcessHeap(), 0, base);
548 /*************************************************************************
549 * UrlCombineW [SHLWAPI.@]
553 HRESULT WINAPI UrlCombineW(LPCWSTR pszBase, LPCWSTR pszRelative,
554 LPWSTR pszCombined, LPDWORD pcchCombined,
557 PARSEDURLW base, relative;
558 DWORD myflags, sizeloc = 0;
559 DWORD len, res1, res2, process_case = 0;
560 LPWSTR work, preliminary, mbase, mrelative;
561 static const WCHAR myfilestr[] = {'f','i','l','e',':','/','/','/','\0'};
562 static const WCHAR single_slash[] = {'/','\0'};
565 TRACE("(base %s, Relative %s, Combine size %ld, flags %08lx)\n",
566 debugstr_w(pszBase),debugstr_w(pszRelative),
567 pcchCombined?*pcchCombined:0,dwFlags);
569 if(!pszBase || !pszRelative || !pcchCombined)
572 base.cbSize = sizeof(base);
573 relative.cbSize = sizeof(relative);
575 /* Get space for duplicates of the input and the output */
576 preliminary = HeapAlloc(GetProcessHeap(), 0, (3*INTERNET_MAX_URL_LENGTH) *
578 mbase = preliminary + INTERNET_MAX_URL_LENGTH;
579 mrelative = mbase + INTERNET_MAX_URL_LENGTH;
580 *preliminary = L'\0';
582 /* Canonicalize the base input prior to looking for the scheme */
583 myflags = dwFlags & (URL_DONT_SIMPLIFY | URL_UNESCAPE);
584 len = INTERNET_MAX_URL_LENGTH;
585 ret = UrlCanonicalizeW(pszBase, mbase, &len, myflags);
587 /* Canonicalize the relative input prior to looking for the scheme */
588 len = INTERNET_MAX_URL_LENGTH;
589 ret = UrlCanonicalizeW(pszRelative, mrelative, &len, myflags);
591 /* See if the base has a scheme */
592 res1 = ParseURLW(mbase, &base);
594 /* if pszBase has no scheme, then return pszRelative */
595 TRACE("no scheme detected in Base\n");
600 /* get size of location field (if it exists) */
601 work = (LPWSTR)base.pszSuffix;
603 if (*work++ == L'/') {
604 if (*work++ == L'/') {
605 /* At this point have start of location and
606 * it ends at next '/' or end of string.
608 while(*work && (*work != L'/')) work++;
609 sizeloc = (DWORD)(work - base.pszSuffix);
613 /* Change .sizep2 to not have the last leaf in it,
614 * Note: we need to start after the location (if it exists)
616 work = strrchrW((base.pszSuffix+sizeloc), L'/');
618 len = (DWORD)(work - base.pszSuffix + 1);
619 base.cchSuffix = len;
623 * .pszSuffix points to location (starting with '//')
624 * .cchSuffix length of location (above) and rest less the last
626 * sizeloc length of location (above) up to but not including
630 res2 = ParseURLW(mrelative, &relative);
632 /* no scheme in pszRelative */
633 TRACE("no scheme detected in Relative\n");
634 relative.pszSuffix = mrelative; /* case 3,4,5 depends on this */
635 relative.cchSuffix = strlenW(mrelative);
636 if (*pszRelative == L':') {
637 /* case that is either left alone or uses pszBase */
638 if (dwFlags & URL_PLUGGABLE_PROTOCOL) {
645 if (isalnum(*mrelative) && (*(mrelative + 1) == L':')) {
646 /* case that becomes "file:///" */
647 strcpyW(preliminary, myfilestr);
651 if ((*mrelative == L'/') && (*(mrelative+1) == L'/')) {
652 /* pszRelative has location and rest */
656 if (*mrelative == L'/') {
657 /* case where pszRelative is root to location */
661 process_case = (*base.pszSuffix == L'/') ? 5 : 3;
665 /* handle cases where pszRelative has scheme */
666 if ((base.cchProtocol == relative.cchProtocol) &&
667 (strncmpW(base.pszProtocol, relative.pszProtocol, base.cchProtocol) == 0)) {
669 /* since the schemes are the same */
670 if ((*relative.pszSuffix == L'/') && (*(relative.pszSuffix+1) == L'/')) {
671 /* case where pszRelative replaces location and following */
675 if (*relative.pszSuffix == L'/') {
676 /* case where pszRelative is root to location */
680 /* case where scheme is followed by document path */
684 if ((*relative.pszSuffix == L'/') && (*(relative.pszSuffix+1) == L'/')) {
685 /* case where pszRelative replaces scheme, location,
686 * and following and handles PLUGGABLE
693 } while(FALSE); /* a litte trick to allow easy exit from nested if's */
697 switch (process_case) {
700 * Return pszRelative appended to what ever is in pszCombined,
701 * (which may the string "file:///"
703 strcatW(preliminary, mrelative);
707 * Same as case 1, but if URL_PLUGGABLE_PROTOCOL was specified
708 * and pszRelative starts with "//", then append a "/"
710 strcpyW(preliminary, mrelative);
711 if (!(dwFlags & URL_PLUGGABLE_PROTOCOL) &&
712 URL_JustLocation(relative.pszSuffix))
713 strcatW(preliminary, single_slash);
717 * Return the pszBase scheme with pszRelative. Basically
718 * keeps the scheme and replaces the domain and following.
720 strncpyW(preliminary, base.pszProtocol, base.cchProtocol + 1);
721 work = preliminary + base.cchProtocol + 1;
722 strcpyW(work, relative.pszSuffix);
723 if (!(dwFlags & URL_PLUGGABLE_PROTOCOL) &&
724 URL_JustLocation(relative.pszSuffix))
725 strcatW(work, single_slash);
729 * Return the pszBase scheme and location but everything
730 * after the location is pszRelative. (Replace document
733 strncpyW(preliminary, base.pszProtocol, base.cchProtocol+1+sizeloc);
734 work = preliminary + base.cchProtocol + 1 + sizeloc;
735 if (dwFlags & URL_PLUGGABLE_PROTOCOL)
737 strcpyW(work, relative.pszSuffix);
741 * Return the pszBase without its document (if any) and
742 * append pszRelative after its scheme.
744 strncpyW(preliminary, base.pszProtocol, base.cchProtocol+1+base.cchSuffix);
745 work = preliminary + base.cchProtocol+1+base.cchSuffix - 1;
748 strcpyW(work, relative.pszSuffix);
752 FIXME("How did we get here????? process_case=%ld\n", process_case);
757 /* Reuse mrelative as temp storage as its already allocated and not needed anymore */
758 ret = UrlCanonicalizeW(preliminary, mrelative, pcchCombined, dwFlags);
759 if(SUCCEEDED(ret) && pszCombined) {
760 lstrcpyW(pszCombined, mrelative);
762 TRACE("return-%ld len=%ld, %s\n",
763 process_case, *pcchCombined, debugstr_w(pszCombined));
765 HeapFree(GetProcessHeap(), 0, preliminary);
769 /*************************************************************************
770 * UrlEscapeA [SHLWAPI.@]
773 HRESULT WINAPI UrlEscapeA(
779 WCHAR bufW[INTERNET_MAX_URL_LENGTH];
780 WCHAR *escapedW = bufW;
783 DWORD lenW = sizeof(bufW)/sizeof(WCHAR), lenA;
785 if(!RtlCreateUnicodeStringFromAsciiz(&urlW, pszUrl))
787 if((ret = UrlEscapeW(urlW.Buffer, escapedW, &lenW, dwFlags)) == E_POINTER) {
788 escapedW = HeapAlloc(GetProcessHeap(), 0, lenW * sizeof(WCHAR));
789 ret = UrlEscapeW(urlW.Buffer, escapedW, &lenW, dwFlags);
792 RtlUnicodeToMultiByteSize(&lenA, escapedW, lenW * sizeof(WCHAR));
793 if(*pcchEscaped > lenA) {
794 RtlUnicodeToMultiByteN(pszEscaped, *pcchEscaped - 1, &lenA, escapedW, lenW * sizeof(WCHAR));
795 pszEscaped[lenA] = 0;
798 *pcchEscaped = lenA + 1;
802 if(escapedW != bufW) HeapFree(GetProcessHeap(), 0, escapedW);
803 RtlFreeUnicodeString(&urlW);
807 #define WINE_URL_BASH_AS_SLASH 0x01
808 #define WINE_URL_COLLAPSE_SLASHES 0x02
809 #define WINE_URL_ESCAPE_SLASH 0x04
810 #define WINE_URL_ESCAPE_HASH 0x08
811 #define WINE_URL_ESCAPE_QUESTION 0x10
812 #define WINE_URL_STOP_ON_HASH 0x20
813 #define WINE_URL_STOP_ON_QUESTION 0x40
815 static inline BOOL URL_NeedEscapeW(WCHAR ch, DWORD dwFlags, DWORD int_flags)
821 if(dwFlags & URL_ESCAPE_SPACES_ONLY) {
828 if ((dwFlags & URL_ESCAPE_PERCENT) && (ch == '%'))
831 if (ch <= 31 || ch >= 127)
852 if (int_flags & WINE_URL_ESCAPE_SLASH) return TRUE;
856 if (int_flags & WINE_URL_ESCAPE_QUESTION) return TRUE;
860 if (int_flags & WINE_URL_ESCAPE_HASH) return TRUE;
870 /*************************************************************************
871 * UrlEscapeW [SHLWAPI.@]
873 * Converts unsafe characters in a Url into escape sequences.
876 * pszUrl [I] Url to modify
877 * pszEscaped [O] Destination for modified Url
878 * pcchEscaped [I/O] Length of pszUrl, destination for length of pszEscaped
879 * dwFlags [I] URL_ flags from "shlwapi.h"
882 * Success: S_OK. pszEscaped contains the escaped Url, pcchEscaped
883 * contains its length.
884 * Failure: E_POINTER, if pszEscaped is not large enough. In this case
885 * pcchEscaped is set to the required length.
887 * Converts unsafe characters into their escape sequences.
890 * - By default this function stops converting at the first '?' or
892 * - If dwFlags contains URL_ESCAPE_SPACES_ONLY then only spaces are
893 * converted, but the conversion continues past a '?' or '#'.
894 * - Note that this function did not work well (or at all) in shlwapi version 4.
897 * Only the following flags are implemented:
898 *| URL_ESCAPE_SPACES_ONLY
899 *| URL_DONT_ESCAPE_EXTRA_INFO
900 *| URL_ESCAPE_SEGMENT_ONLY
901 *| URL_ESCAPE_PERCENT
903 HRESULT WINAPI UrlEscapeW(
910 DWORD needed = 0, ret;
911 BOOL stop_escaping = FALSE;
912 WCHAR next[5], *dst = pszEscaped;
914 PARSEDURLW parsed_url;
917 static const WCHAR localhost[] = {'l','o','c','a','l','h','o','s','t',0};
919 TRACE("(%s %p %p 0x%08lx)\n", debugstr_w(pszUrl), pszEscaped,
920 pcchEscaped, dwFlags);
922 if(!pszUrl || !pszEscaped || !pcchEscaped)
925 if(dwFlags & ~(URL_ESCAPE_SPACES_ONLY |
926 URL_ESCAPE_SEGMENT_ONLY |
927 URL_DONT_ESCAPE_EXTRA_INFO |
929 FIXME("Unimplemented flags: %08lx\n", dwFlags);
932 if (dwFlags & URL_ESCAPE_SPACES_ONLY)
933 /* if SPACES_ONLY specified, reset the other controls */
934 dwFlags &= ~(URL_DONT_ESCAPE_EXTRA_INFO |
936 URL_ESCAPE_SEGMENT_ONLY);
939 /* if SPACES_ONLY *not* specified the assume DONT_ESCAPE_EXTRA_INFO */
940 dwFlags |= URL_DONT_ESCAPE_EXTRA_INFO;
944 if(dwFlags & URL_ESCAPE_SEGMENT_ONLY) {
945 int_flags = WINE_URL_ESCAPE_QUESTION | WINE_URL_ESCAPE_HASH | WINE_URL_ESCAPE_SLASH;
947 parsed_url.cbSize = sizeof(parsed_url);
948 if(ParseURLW(pszUrl, &parsed_url) != S_OK)
949 parsed_url.nScheme = URL_SCHEME_INVALID;
951 TRACE("scheme = %d (%s)\n", parsed_url.nScheme, debugstr_wn(parsed_url.pszProtocol, parsed_url.cchProtocol));
953 if(dwFlags & URL_DONT_ESCAPE_EXTRA_INFO)
954 int_flags = WINE_URL_STOP_ON_HASH | WINE_URL_STOP_ON_QUESTION;
956 switch(parsed_url.nScheme) {
957 case URL_SCHEME_FILE:
958 int_flags |= WINE_URL_BASH_AS_SLASH | WINE_URL_COLLAPSE_SLASHES | WINE_URL_ESCAPE_HASH;
959 int_flags &= ~WINE_URL_STOP_ON_HASH;
962 case URL_SCHEME_HTTP:
963 case URL_SCHEME_HTTPS:
964 int_flags |= WINE_URL_BASH_AS_SLASH;
965 if(parsed_url.pszSuffix[0] != '/' && parsed_url.pszSuffix[0] != '\\')
966 int_flags |= WINE_URL_ESCAPE_SLASH;
969 case URL_SCHEME_MAILTO:
970 int_flags |= WINE_URL_ESCAPE_SLASH | WINE_URL_ESCAPE_QUESTION | WINE_URL_ESCAPE_HASH;
971 int_flags &= ~(WINE_URL_STOP_ON_QUESTION | WINE_URL_STOP_ON_HASH);
974 case URL_SCHEME_INVALID:
979 if(parsed_url.pszSuffix[0] != '/')
980 int_flags |= WINE_URL_ESCAPE_SLASH;
985 for(src = pszUrl; *src; ) {
989 if((int_flags & WINE_URL_COLLAPSE_SLASHES) && src == pszUrl + parsed_url.cchProtocol + 1) {
990 int localhost_len = sizeof(localhost)/sizeof(WCHAR) - 1;
991 while(cur == '/' || cur == '\\') {
995 if(slashes == 2 && !strncmpiW(src, localhost, localhost_len)) { /* file://localhost/ -> file:/// */
996 if(*(src + localhost_len) == '/' || *(src + localhost_len) == '\\')
997 src += localhost_len + 1;
1004 next[0] = next[1] = next[2] = '/';
1011 next[0] = next[1] = '/';
1018 if(cur == '#' && (int_flags & WINE_URL_STOP_ON_HASH))
1019 stop_escaping = TRUE;
1021 if(cur == '?' && (int_flags & WINE_URL_STOP_ON_QUESTION))
1022 stop_escaping = TRUE;
1024 if(cur == '\\' && (int_flags & WINE_URL_BASH_AS_SLASH) && !stop_escaping) cur = '/';
1026 if(URL_NeedEscapeW(cur, dwFlags, int_flags) && stop_escaping == FALSE) {
1028 next[1] = hexDigits[(cur >> 4) & 0xf];
1029 next[2] = hexDigits[cur & 0xf];
1038 if(needed + len <= *pcchEscaped) {
1039 memcpy(dst, next, len*sizeof(WCHAR));
1045 if(needed < *pcchEscaped) {
1049 needed++; /* add one for the '\0' */
1052 *pcchEscaped = needed;
1057 /*************************************************************************
1058 * UrlUnescapeA [SHLWAPI.@]
1060 * Converts Url escape sequences back to ordinary characters.
1063 * pszUrl [I/O] Url to convert
1064 * pszUnescaped [O] Destination for converted Url
1065 * pcchUnescaped [I/O] Size of output string
1066 * dwFlags [I] URL_ESCAPE_ Flags from "shlwapi.h"
1069 * Success: S_OK. The converted value is in pszUnescaped, or in pszUrl if
1070 * dwFlags includes URL_ESCAPE_INPLACE.
1071 * Failure: E_POINTER if the converted Url is bigger than pcchUnescaped. In
1072 * this case pcchUnescaped is set to the size required.
1074 * If dwFlags includes URL_DONT_ESCAPE_EXTRA_INFO, the conversion stops at
1075 * the first occurrence of either a '?' or '#' character.
1077 HRESULT WINAPI UrlUnescapeA(
1080 LPDWORD pcchUnescaped,
1087 BOOL stop_unescaping = FALSE;
1089 TRACE("(%s, %p, %p, 0x%08lx)\n", debugstr_a(pszUrl), pszUnescaped,
1090 pcchUnescaped, dwFlags);
1092 if(!pszUrl || !pszUnescaped || !pcchUnescaped)
1093 return E_INVALIDARG;
1095 if(dwFlags & URL_UNESCAPE_INPLACE)
1100 for(src = pszUrl, needed = 0; *src; src++, needed++) {
1101 if(dwFlags & URL_DONT_UNESCAPE_EXTRA_INFO &&
1102 (*src == '#' || *src == '?')) {
1103 stop_unescaping = TRUE;
1105 } else if(*src == '%' && isxdigit(*(src + 1)) && isxdigit(*(src + 2))
1106 && stop_unescaping == FALSE) {
1109 memcpy(buf, src + 1, 2);
1111 ih = strtol(buf, NULL, 16);
1113 src += 2; /* Advance to end of escape */
1117 if(dwFlags & URL_UNESCAPE_INPLACE || needed < *pcchUnescaped)
1121 if(dwFlags & URL_UNESCAPE_INPLACE || needed < *pcchUnescaped) {
1125 needed++; /* add one for the '\0' */
1128 if(!(dwFlags & URL_UNESCAPE_INPLACE))
1129 *pcchUnescaped = needed;
1132 TRACE("result %s\n", (dwFlags & URL_UNESCAPE_INPLACE) ?
1133 debugstr_a(pszUrl) : debugstr_a(pszUnescaped));
1139 /*************************************************************************
1140 * UrlUnescapeW [SHLWAPI.@]
1144 HRESULT WINAPI UrlUnescapeW(
1146 LPWSTR pszUnescaped,
1147 LPDWORD pcchUnescaped,
1154 BOOL stop_unescaping = FALSE;
1156 TRACE("(%s, %p, %p, 0x%08lx)\n", debugstr_w(pszUrl), pszUnescaped,
1157 pcchUnescaped, dwFlags);
1159 if(!pszUrl || !pszUnescaped || !pcchUnescaped)
1160 return E_INVALIDARG;
1162 if(dwFlags & URL_UNESCAPE_INPLACE)
1167 for(src = pszUrl, needed = 0; *src; src++, needed++) {
1168 if(dwFlags & URL_DONT_UNESCAPE_EXTRA_INFO &&
1169 (*src == L'#' || *src == L'?')) {
1170 stop_unescaping = TRUE;
1172 } else if(*src == L'%' && isxdigitW(*(src + 1)) && isxdigitW(*(src + 2))
1173 && stop_unescaping == FALSE) {
1175 WCHAR buf[5] = {'0','x',0};
1176 memcpy(buf + 2, src + 1, 2*sizeof(WCHAR));
1178 StrToIntExW(buf, STIF_SUPPORT_HEX, &ih);
1180 src += 2; /* Advance to end of escape */
1184 if(dwFlags & URL_UNESCAPE_INPLACE || needed < *pcchUnescaped)
1188 if(dwFlags & URL_UNESCAPE_INPLACE || needed < *pcchUnescaped) {
1192 needed++; /* add one for the '\0' */
1195 if(!(dwFlags & URL_UNESCAPE_INPLACE))
1196 *pcchUnescaped = needed;
1199 TRACE("result %s\n", (dwFlags & URL_UNESCAPE_INPLACE) ?
1200 debugstr_w(pszUrl) : debugstr_w(pszUnescaped));
1206 /*************************************************************************
1207 * UrlGetLocationA [SHLWAPI.@]
1209 * Get the location from a Url.
1212 * pszUrl [I] Url to get the location from
1215 * A pointer to the start of the location in pszUrl, or NULL if there is
1219 * - MSDN erroneously states that "The location is the segment of the Url
1220 * starting with a '?' or '#' character". Neither V4 nor V5 of shlwapi.dll
1221 * stop at '?' and always return a NULL in this case.
1222 * - MSDN also erroneously states that "If a file URL has a query string,
1223 * the returned string is the query string". In all tested cases, if the
1224 * Url starts with "fi" then a NULL is returned. V5 gives the following results:
1227 *| NULL file://aa/b/cd#hohoh
1228 *| #hohoh http://aa/b/cd#hohoh
1229 *| NULL fi://aa/b/cd#hohoh
1230 *| #hohoh ff://aa/b/cd#hohoh
1232 LPCSTR WINAPI UrlGetLocationA(
1238 base.cbSize = sizeof(base);
1239 res1 = ParseURLA(pszUrl, &base);
1240 if (res1) return NULL; /* invalid scheme */
1242 /* if scheme is file: then never return pointer */
1243 if (strncmp(base.pszProtocol, "file", min(4,base.cchProtocol)) == 0) return NULL;
1245 /* Look for '#' and return its addr */
1246 return strchr(base.pszSuffix, '#');
1249 /*************************************************************************
1250 * UrlGetLocationW [SHLWAPI.@]
1252 * See UrlGetLocationA.
1254 LPCWSTR WINAPI UrlGetLocationW(
1260 base.cbSize = sizeof(base);
1261 res1 = ParseURLW(pszUrl, &base);
1262 if (res1) return NULL; /* invalid scheme */
1264 /* if scheme is file: then never return pointer */
1265 if (strncmpW(base.pszProtocol, fileW, min(4,base.cchProtocol)) == 0) return NULL;
1267 /* Look for '#' and return its addr */
1268 return strchrW(base.pszSuffix, L'#');
1271 /*************************************************************************
1272 * UrlCompareA [SHLWAPI.@]
1277 * pszUrl1 [I] First Url to compare
1278 * pszUrl2 [I] Url to compare to pszUrl1
1279 * fIgnoreSlash [I] TRUE = compare only up to a final slash
1282 * less than zero, zero, or greater than zero indicating pszUrl2 is greater
1283 * than, equal to, or less than pszUrl1 respectively.
1285 INT WINAPI UrlCompareA(
1290 INT ret, len, len1, len2;
1293 return strcmp(pszUrl1, pszUrl2);
1294 len1 = strlen(pszUrl1);
1295 if (pszUrl1[len1-1] == '/') len1--;
1296 len2 = strlen(pszUrl2);
1297 if (pszUrl2[len2-1] == '/') len2--;
1299 return strncmp(pszUrl1, pszUrl2, len1);
1300 len = min(len1, len2);
1301 ret = strncmp(pszUrl1, pszUrl2, len);
1302 if (ret) return ret;
1303 if (len1 > len2) return 1;
1307 /*************************************************************************
1308 * UrlCompareW [SHLWAPI.@]
1312 INT WINAPI UrlCompareW(
1318 size_t len, len1, len2;
1321 return strcmpW(pszUrl1, pszUrl2);
1322 len1 = strlenW(pszUrl1);
1323 if (pszUrl1[len1-1] == '/') len1--;
1324 len2 = strlenW(pszUrl2);
1325 if (pszUrl2[len2-1] == '/') len2--;
1327 return strncmpW(pszUrl1, pszUrl2, len1);
1328 len = min(len1, len2);
1329 ret = strncmpW(pszUrl1, pszUrl2, len);
1330 if (ret) return ret;
1331 if (len1 > len2) return 1;
1335 /*************************************************************************
1336 * HashData [SHLWAPI.@]
1338 * Hash an input block into a variable sized digest.
1341 * lpSrc [I] Input block
1342 * nSrcLen [I] Length of lpSrc
1343 * lpDest [I] Output for hash digest
1344 * nDestLen [I] Length of lpDest
1347 * Success: TRUE. lpDest is filled with the computed hash value.
1348 * Failure: FALSE, if any argument is invalid.
1350 HRESULT WINAPI HashData(const unsigned char *lpSrc, DWORD nSrcLen,
1351 unsigned char *lpDest, DWORD nDestLen)
1353 INT srcCount = nSrcLen - 1, destCount = nDestLen - 1;
1355 if (IsBadReadPtr(lpSrc, nSrcLen) ||
1356 IsBadWritePtr(lpDest, nDestLen))
1357 return E_INVALIDARG;
1359 while (destCount >= 0)
1361 lpDest[destCount] = (destCount & 0xff);
1365 while (srcCount >= 0)
1367 destCount = nDestLen - 1;
1368 while (destCount >= 0)
1370 lpDest[destCount] = HashDataLookup[lpSrc[srcCount] ^ lpDest[destCount]];
1378 /*************************************************************************
1379 * UrlHashA [SHLWAPI.@]
1381 * Produce a Hash from a Url.
1384 * pszUrl [I] Url to hash
1385 * lpDest [O] Destinationh for hash
1386 * nDestLen [I] Length of lpDest
1389 * Success: S_OK. lpDest is filled with the computed hash value.
1390 * Failure: E_INVALIDARG, if any argument is invalid.
1392 HRESULT WINAPI UrlHashA(LPCSTR pszUrl, unsigned char *lpDest, DWORD nDestLen)
1394 if (IsBadStringPtrA(pszUrl, -1) || IsBadWritePtr(lpDest, nDestLen))
1395 return E_INVALIDARG;
1397 HashData((PBYTE)pszUrl, (int)strlen(pszUrl), lpDest, nDestLen);
1401 /*************************************************************************
1402 * UrlHashW [SHLWAPI.@]
1406 HRESULT WINAPI UrlHashW(LPCWSTR pszUrl, unsigned char *lpDest, DWORD nDestLen)
1408 char szUrl[MAX_PATH];
1410 TRACE("(%s,%p,%ld)\n",debugstr_w(pszUrl), lpDest, nDestLen);
1412 if (IsBadStringPtrW(pszUrl, -1) || IsBadWritePtr(lpDest, nDestLen))
1413 return E_INVALIDARG;
1415 /* Win32 hashes the data as an ASCII string, presumably so that both A+W
1416 * return the same digests for the same URL.
1418 WideCharToMultiByte(0, 0, pszUrl, -1, szUrl, MAX_PATH, 0, 0);
1419 HashData((PBYTE)szUrl, (int)strlen(szUrl), lpDest, nDestLen);
1423 /*************************************************************************
1424 * UrlApplySchemeA [SHLWAPI.@]
1426 * Apply a scheme to a Url.
1429 * pszIn [I] Url to apply scheme to
1430 * pszOut [O] Destination for modified Url
1431 * pcchOut [I/O] Length of pszOut/destination for length of pszOut
1432 * dwFlags [I] URL_ flags from "shlwapi.h"
1435 * Success: S_OK: pszOut contains the modified Url, pcchOut contains its length.
1436 * Failure: An HRESULT error code describing the error.
1438 HRESULT WINAPI UrlApplySchemeA(LPCSTR pszIn, LPSTR pszOut, LPDWORD pcchOut, DWORD dwFlags)
1441 DWORD ret, len, len2;
1443 TRACE("(in %s, out size %ld, flags %08lx) using W version\n",
1444 debugstr_a(pszIn), *pcchOut, dwFlags);
1446 in = HeapAlloc(GetProcessHeap(), 0,
1447 (2*INTERNET_MAX_URL_LENGTH) * sizeof(WCHAR));
1448 out = in + INTERNET_MAX_URL_LENGTH;
1450 MultiByteToWideChar(0, 0, pszIn, -1, in, INTERNET_MAX_URL_LENGTH);
1451 len = INTERNET_MAX_URL_LENGTH;
1453 ret = UrlApplySchemeW(in, out, &len, dwFlags);
1454 if ((ret != S_OK) && (ret != S_FALSE)) {
1455 HeapFree(GetProcessHeap(), 0, in);
1459 len2 = WideCharToMultiByte(0, 0, out, len+1, 0, 0, 0, 0);
1460 if (len2 > *pcchOut) {
1462 HeapFree(GetProcessHeap(), 0, in);
1465 WideCharToMultiByte(0, 0, out, len+1, pszOut, *pcchOut, 0, 0);
1467 HeapFree(GetProcessHeap(), 0, in);
1471 static HRESULT URL_GuessScheme(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut)
1476 DWORD value_len, data_len, dwType, i;
1477 WCHAR reg_path[MAX_PATH];
1478 WCHAR value[MAX_PATH], data[MAX_PATH];
1481 MultiByteToWideChar(0, 0,
1482 "Software\\Microsoft\\Windows\\CurrentVersion\\URL\\Prefixes",
1483 -1, reg_path, MAX_PATH);
1484 RegOpenKeyExW(HKEY_LOCAL_MACHINE, reg_path, 0, 1, &newkey);
1486 while(value_len = data_len = MAX_PATH,
1487 RegEnumValueW(newkey, index, value, &value_len,
1488 0, &dwType, (LPVOID)data, &data_len) == 0) {
1489 TRACE("guess %d %s is %s\n",
1490 index, debugstr_w(value), debugstr_w(data));
1493 for(i=0; i<value_len; i++) {
1496 /* remember that TRUE is not-equal */
1497 j = ChrCmpIW(Wxx, Wyy);
1500 if ((i == value_len) && !j) {
1501 if (strlenW(data) + strlenW(pszIn) + 1 > *pcchOut) {
1502 *pcchOut = strlenW(data) + strlenW(pszIn) + 1;
1503 RegCloseKey(newkey);
1506 strcpyW(pszOut, data);
1507 strcatW(pszOut, pszIn);
1508 *pcchOut = strlenW(pszOut);
1509 TRACE("matched and set to %s\n", debugstr_w(pszOut));
1510 RegCloseKey(newkey);
1515 RegCloseKey(newkey);
1519 static HRESULT URL_ApplyDefault(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut)
1522 DWORD data_len, dwType;
1523 WCHAR reg_path[MAX_PATH];
1524 WCHAR value[MAX_PATH], data[MAX_PATH];
1526 /* get and prepend default */
1527 MultiByteToWideChar(0, 0,
1528 "Software\\Microsoft\\Windows\\CurrentVersion\\URL\\DefaultPrefix",
1529 -1, reg_path, MAX_PATH);
1530 RegOpenKeyExW(HKEY_LOCAL_MACHINE, reg_path, 0, 1, &newkey);
1531 data_len = MAX_PATH;
1534 RegQueryValueExW(newkey, value, 0, &dwType, (LPBYTE)data, &data_len);
1535 RegCloseKey(newkey);
1536 if (strlenW(data) + strlenW(pszIn) + 1 > *pcchOut) {
1537 *pcchOut = strlenW(data) + strlenW(pszIn) + 1;
1540 strcpyW(pszOut, data);
1541 strcatW(pszOut, pszIn);
1542 *pcchOut = strlenW(pszOut);
1543 TRACE("used default %s\n", debugstr_w(pszOut));
1547 /*************************************************************************
1548 * UrlApplySchemeW [SHLWAPI.@]
1550 * See UrlApplySchemeA.
1552 HRESULT WINAPI UrlApplySchemeW(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut, DWORD dwFlags)
1554 PARSEDURLW in_scheme;
1558 TRACE("(in %s, out size %ld, flags %08lx)\n",
1559 debugstr_w(pszIn), *pcchOut, dwFlags);
1561 if (dwFlags & URL_APPLY_GUESSFILE) {
1562 FIXME("(%s %p %p(%ld) 0x%08lx): stub URL_APPLY_GUESSFILE not implemented\n",
1563 debugstr_w(pszIn), pszOut, pcchOut, *pcchOut, dwFlags);
1564 strcpyW(pszOut, pszIn);
1565 *pcchOut = strlenW(pszOut);
1569 in_scheme.cbSize = sizeof(in_scheme);
1570 /* See if the base has a scheme */
1571 res1 = ParseURLW(pszIn, &in_scheme);
1573 /* no scheme in input, need to see if we need to guess */
1574 if (dwFlags & URL_APPLY_GUESSSCHEME) {
1575 if ((ret = URL_GuessScheme(pszIn, pszOut, pcchOut)) != -1)
1580 /* we have a scheme, see if valid (known scheme) */
1581 if (in_scheme.nScheme) {
1582 /* have valid scheme, so just copy and exit */
1583 if (strlenW(pszIn) + 1 > *pcchOut) {
1584 *pcchOut = strlenW(pszIn) + 1;
1587 strcpyW(pszOut, pszIn);
1588 *pcchOut = strlenW(pszOut);
1589 TRACE("valid scheme, returing copy\n");
1594 /* If we are here, then either invalid scheme,
1595 * or no scheme and can't/failed guess.
1597 if ( ( ((res1 == 0) && (dwFlags & URL_APPLY_FORCEAPPLY)) ||
1599 (dwFlags & URL_APPLY_DEFAULT)) {
1600 /* find and apply default scheme */
1601 return URL_ApplyDefault(pszIn, pszOut, pcchOut);
1604 /* just copy and give proper return code */
1605 if (strlenW(pszIn) + 1 > *pcchOut) {
1606 *pcchOut = strlenW(pszIn) + 1;
1609 strcpyW(pszOut, pszIn);
1610 *pcchOut = strlenW(pszOut);
1611 TRACE("returning copy, left alone\n");
1615 /*************************************************************************
1616 * UrlIsA [SHLWAPI.@]
1618 * Determine if a Url is of a certain class.
1621 * pszUrl [I] Url to check
1622 * Urlis [I] URLIS_ constant from "shlwapi.h"
1625 * TRUE if pszUrl belongs to the class type in Urlis.
1628 BOOL WINAPI UrlIsA(LPCSTR pszUrl, URLIS Urlis)
1637 base.cbSize = sizeof(base);
1638 res1 = ParseURLA(pszUrl, &base);
1639 if (res1) return FALSE; /* invalid scheme */
1640 if ((*base.pszSuffix == '/') && (*(base.pszSuffix+1) == '/'))
1641 /* has scheme followed by 2 '/' */
1646 return !StrCmpNA("file://", pszUrl, 7);
1648 case URLIS_DIRECTORY:
1649 last = pszUrl + strlen(pszUrl) - 1;
1650 return (last >= pszUrl && (*last == '/' || *last == '\\' ));
1653 case URLIS_NOHISTORY:
1654 case URLIS_APPLIABLE:
1655 case URLIS_HASQUERY:
1657 FIXME("(%s %d): stub\n", debugstr_a(pszUrl), Urlis);
1662 /*************************************************************************
1663 * UrlIsW [SHLWAPI.@]
1667 BOOL WINAPI UrlIsW(LPCWSTR pszUrl, URLIS Urlis)
1669 static const WCHAR stemp[] = { 'f','i','l','e',':','/','/',0 };
1677 base.cbSize = sizeof(base);
1678 res1 = ParseURLW(pszUrl, &base);
1679 if (res1) return FALSE; /* invalid scheme */
1680 if ((*base.pszSuffix == '/') && (*(base.pszSuffix+1) == '/'))
1681 /* has scheme followed by 2 '/' */
1686 return !strncmpW(stemp, pszUrl, 7);
1688 case URLIS_DIRECTORY:
1689 last = pszUrl + strlenW(pszUrl) - 1;
1690 return (last >= pszUrl && (*last == '/' || *last == '\\'));
1693 case URLIS_NOHISTORY:
1694 case URLIS_APPLIABLE:
1695 case URLIS_HASQUERY:
1697 FIXME("(%s %d): stub\n", debugstr_w(pszUrl), Urlis);
1702 /*************************************************************************
1703 * UrlIsNoHistoryA [SHLWAPI.@]
1705 * Determine if a Url should not be stored in the users history list.
1708 * pszUrl [I] Url to check
1711 * TRUE, if pszUrl should be excluded from the history list,
1714 BOOL WINAPI UrlIsNoHistoryA(LPCSTR pszUrl)
1716 return UrlIsA(pszUrl, URLIS_NOHISTORY);
1719 /*************************************************************************
1720 * UrlIsNoHistoryW [SHLWAPI.@]
1722 * See UrlIsNoHistoryA.
1724 BOOL WINAPI UrlIsNoHistoryW(LPCWSTR pszUrl)
1726 return UrlIsW(pszUrl, URLIS_NOHISTORY);
1729 /*************************************************************************
1730 * UrlIsOpaqueA [SHLWAPI.@]
1732 * Determine if a Url is opaque.
1735 * pszUrl [I] Url to check
1738 * TRUE if pszUrl is opaque,
1742 * An opaque Url is one that does not start with "<protocol>://".
1744 BOOL WINAPI UrlIsOpaqueA(LPCSTR pszUrl)
1746 return UrlIsA(pszUrl, URLIS_OPAQUE);
1749 /*************************************************************************
1750 * UrlIsOpaqueW [SHLWAPI.@]
1754 BOOL WINAPI UrlIsOpaqueW(LPCWSTR pszUrl)
1756 return UrlIsW(pszUrl, URLIS_OPAQUE);
1759 /*************************************************************************
1760 * Scans for characters of type "type" and when not matching found,
1761 * returns pointer to it and length in size.
1763 * Characters tested based on RFC 1738
1765 static LPCWSTR URL_ScanID(LPCWSTR start, LPDWORD size, WINE_URL_SCAN_TYPE type)
1767 static DWORD alwayszero = 0;
1776 if ( (islowerW(*start) && isalphaW(*start)) ||
1791 if ( isalphaW(*start) ||
1793 /* user/password only characters */
1798 /* *extra* characters */
1801 (*start == L'\'') ||
1805 /* *safe* characters */
1813 } else if (*start == L'%') {
1814 if (isxdigitW(*(start+1)) &&
1815 isxdigitW(*(start+2))) {
1827 if (isdigitW(*start)) {
1838 if (isalnumW(*start) ||
1840 (*start == L'.') ) {
1849 FIXME("unknown type %d\n", type);
1850 return (LPWSTR)&alwayszero;
1852 /* TRACE("scanned %ld characters next char %p<%c>\n",
1853 *size, start, *start); */
1857 /*************************************************************************
1858 * Attempt to parse URL into pieces.
1860 static LONG URL_ParseUrl(LPCWSTR pszUrl, WINE_PARSE_URL *pl)
1864 memset(pl, 0, sizeof(WINE_PARSE_URL));
1865 pl->pScheme = pszUrl;
1866 work = URL_ScanID(pl->pScheme, &pl->szScheme, SCHEME);
1867 if (!*work || (*work != L':')) goto ErrorExit;
1869 if ((*work != L'/') || (*(work+1) != L'/')) goto ErrorExit;
1870 pl->pUserName = work + 2;
1871 work = URL_ScanID(pl->pUserName, &pl->szUserName, USERPASS);
1872 if (*work == L':' ) {
1873 /* parse password */
1875 pl->pPassword = work;
1876 work = URL_ScanID(pl->pPassword, &pl->szPassword, USERPASS);
1877 if (*work != L'@') {
1878 /* what we just parsed must be the hostname and port
1879 * so reset pointers and clear then let it parse */
1880 pl->szUserName = pl->szPassword = 0;
1881 work = pl->pUserName - 1;
1882 pl->pUserName = pl->pPassword = 0;
1884 } else if (*work == L'@') {
1888 } else if (!*work || (*work == L'/') || (*work == L'.')) {
1889 /* what was parsed was hostname, so reset pointers and let it parse */
1890 pl->szUserName = pl->szPassword = 0;
1891 work = pl->pUserName - 1;
1892 pl->pUserName = pl->pPassword = 0;
1893 } else goto ErrorExit;
1895 /* now start parsing hostname or hostnumber */
1897 pl->pHostName = work;
1898 work = URL_ScanID(pl->pHostName, &pl->szHostName, HOST);
1899 if (*work == L':') {
1903 work = URL_ScanID(pl->pPort, &pl->szPort, PORT);
1905 if (*work == L'/') {
1906 /* see if query string */
1907 pl->pQuery = strchrW(work, L'?');
1908 if (pl->pQuery) pl->szQuery = strlenW(pl->pQuery);
1910 TRACE("parse successful: scheme=%p(%ld), user=%p(%ld), pass=%p(%ld), host=%p(%ld), port=%p(%ld), query=%p(%ld)\n",
1911 pl->pScheme, pl->szScheme,
1912 pl->pUserName, pl->szUserName,
1913 pl->pPassword, pl->szPassword,
1914 pl->pHostName, pl->szHostName,
1915 pl->pPort, pl->szPort,
1916 pl->pQuery, pl->szQuery);
1919 FIXME("failed to parse %s\n", debugstr_w(pszUrl));
1920 return E_INVALIDARG;
1923 /*************************************************************************
1924 * UrlGetPartA [SHLWAPI.@]
1926 * Retrieve part of a Url.
1929 * pszIn [I] Url to parse
1930 * pszOut [O] Destination for part of pszIn requested
1931 * pcchOut [I/O] Length of pszOut/destination for length of pszOut
1932 * dwPart [I] URL_PART_ enum from "shlwapi.h"
1933 * dwFlags [I] URL_ flags from "shlwapi.h"
1936 * Success: S_OK. pszOut contains the part requested, pcchOut contains its length.
1937 * Failure: An HRESULT error code describing the error.
1939 HRESULT WINAPI UrlGetPartA(LPCSTR pszIn, LPSTR pszOut, LPDWORD pcchOut,
1940 DWORD dwPart, DWORD dwFlags)
1943 DWORD ret, len, len2;
1945 in = HeapAlloc(GetProcessHeap(), 0,
1946 (2*INTERNET_MAX_URL_LENGTH) * sizeof(WCHAR));
1947 out = in + INTERNET_MAX_URL_LENGTH;
1949 MultiByteToWideChar(0, 0, pszIn, -1, in, INTERNET_MAX_URL_LENGTH);
1951 len = INTERNET_MAX_URL_LENGTH;
1952 ret = UrlGetPartW(in, out, &len, dwPart, dwFlags);
1955 HeapFree(GetProcessHeap(), 0, in);
1959 len2 = WideCharToMultiByte(0, 0, out, len, 0, 0, 0, 0);
1960 if (len2 > *pcchOut) {
1962 HeapFree(GetProcessHeap(), 0, in);
1965 WideCharToMultiByte(0, 0, out, len+1, pszOut, *pcchOut, 0, 0);
1967 HeapFree(GetProcessHeap(), 0, in);
1971 /*************************************************************************
1972 * UrlGetPartW [SHLWAPI.@]
1976 HRESULT WINAPI UrlGetPartW(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut,
1977 DWORD dwPart, DWORD dwFlags)
1981 DWORD size, schsize;
1982 LPCWSTR addr, schaddr;
1985 TRACE("(%s %p %p(%ld) %08lx %08lx)\n",
1986 debugstr_w(pszIn), pszOut, pcchOut, *pcchOut, dwPart, dwFlags);
1988 ret = URL_ParseUrl(pszIn, &pl);
1990 schaddr = pl.pScheme;
1991 schsize = pl.szScheme;
1994 case URL_PART_SCHEME:
1995 if (!pl.szScheme) return E_INVALIDARG;
2000 case URL_PART_HOSTNAME:
2001 if (!pl.szHostName) return E_INVALIDARG;
2002 addr = pl.pHostName;
2003 size = pl.szHostName;
2006 case URL_PART_USERNAME:
2007 if (!pl.szUserName) return E_INVALIDARG;
2008 addr = pl.pUserName;
2009 size = pl.szUserName;
2012 case URL_PART_PASSWORD:
2013 if (!pl.szPassword) return E_INVALIDARG;
2014 addr = pl.pPassword;
2015 size = pl.szPassword;
2019 if (!pl.szPort) return E_INVALIDARG;
2024 case URL_PART_QUERY:
2025 if (!pl.szQuery) return E_INVALIDARG;
2031 return E_INVALIDARG;
2034 if (dwFlags == URL_PARTFLAG_KEEPSCHEME) {
2035 if (*pcchOut < size + schsize + 2) {
2036 *pcchOut = size + schsize + 2;
2039 strncpyW(pszOut, schaddr, schsize);
2040 work = pszOut + schsize;
2042 strncpyW(work+1, addr, size);
2043 *pcchOut = size + schsize + 1;
2048 if (*pcchOut < size + 1) {*pcchOut = size+1; return E_POINTER;}
2049 strncpyW(pszOut, addr, size);
2051 work = pszOut + size;
2054 TRACE("len=%ld %s\n", *pcchOut, debugstr_w(pszOut));
2059 /*************************************************************************
2060 * PathIsURLA [SHLWAPI.@]
2062 * Check if the given path is a Url.
2065 * lpszPath [I] Path to check.
2068 * TRUE if lpszPath is a Url.
2069 * FALSE if lpszPath is NULL or not a Url.
2071 BOOL WINAPI PathIsURLA(LPCSTR lpstrPath)
2076 if (!lpstrPath || !*lpstrPath) return FALSE;
2079 base.cbSize = sizeof(base);
2080 res1 = ParseURLA(lpstrPath, &base);
2081 return (base.nScheme != URL_SCHEME_INVALID);
2084 /*************************************************************************
2085 * PathIsURLW [SHLWAPI.@]
2089 BOOL WINAPI PathIsURLW(LPCWSTR lpstrPath)
2094 if (!lpstrPath || !*lpstrPath) return FALSE;
2097 base.cbSize = sizeof(base);
2098 res1 = ParseURLW(lpstrPath, &base);
2099 return (base.nScheme != URL_SCHEME_INVALID);
2102 /*************************************************************************
2103 * UrlCreateFromPathA [SHLWAPI.@]
2105 * Create a Url from a file path.
2108 * pszPath [I] Path to convert
2109 * pszUrl [O] Destination for the converted Url
2110 * pcchUrl [I/O] Length of pszUrl
2111 * dwReserved [I] Reserved, must be 0
2114 * Success: S_OK. pszUrl contains the converted path.
2115 * Failure: An HRESULT error code.
2117 HRESULT WINAPI UrlCreateFromPathA(LPCSTR pszPath, LPSTR pszUrl, LPDWORD pcchUrl, DWORD dwReserved)
2119 DWORD nCharBeforeColon = 0;
2121 DWORD dwChRequired = 0;
2122 LPSTR pszNewUrl = NULL;
2123 LPCSTR pszConstPointer = NULL;
2124 LPSTR pszPointer = NULL;
2128 TRACE("(%s, %p, %p, 0x%08lx)\n", debugstr_a(pszPath), pszUrl, pcchUrl, dwReserved);
2130 /* Validate arguments */
2131 if (dwReserved != 0)
2133 FIXME("dwReserved should be 0: 0x%08lx\n", dwReserved);
2134 return E_INVALIDARG;
2136 if (!pszUrl || !pcchUrl || !pszUrl)
2138 ERR("Invalid argument\n");
2139 return E_INVALIDARG;
2142 for (pszConstPointer = pszPath; *pszConstPointer; pszConstPointer++)
2144 if (isalpha(*pszConstPointer) || isdigit(*pszConstPointer) ||
2145 *pszConstPointer == '.' || *pszConstPointer == '-')
2149 if (*pszConstPointer == ':') /* then already in URL format, so copy */
2151 dwChRequired = lstrlenA(pszPath);
2152 if (dwChRequired > *pcchUrl)
2154 *pcchUrl = dwChRequired;
2159 *pcchUrl = dwChRequired;
2160 StrCpyA(pszUrl, pszPath);
2164 /* then must need converting to file: format */
2166 /* Strip off leading slashes */
2167 while (*pszPath == '\\' || *pszPath == '/')
2173 dwChRequired = *pcchUrl; /* UrlEscape will fill this in with the correct amount */
2174 TRACE("pszUrl: %s\n", debugstr_a(pszPath));
2175 pszNewUrl = HeapAlloc(GetProcessHeap(), 0, dwChRequired + 1);
2176 ret = UrlEscapeA(pszPath, pszNewUrl, &dwChRequired, URL_ESCAPE_PERCENT);
2177 TRACE("ret: 0x%08lx, pszUrl: %s\n", ret, debugstr_a(pszNewUrl));
2178 TRACE("%ld\n", dwChRequired);
2179 if (ret != E_POINTER && FAILED(ret))
2181 dwChRequired += 5; /* "file:" */
2182 if ((lstrlenA(pszUrl) > 1) && isalpha(pszUrl[0]) && (pszUrl[1] == ':'))
2184 dwChRequired += 3; /* "///" */
2190 case 0: /* no slashes */
2192 case 2: /* two slashes */
2199 default: /* three slashes */
2204 if (dwChRequired > *pcchUrl)
2206 *pcchUrl = dwChRequired; /* Return number of chars required (not including termination) */
2207 StrCpyA(pszUrl, "file:");
2208 pszPointer = pszUrl + lstrlenA(pszUrl);
2209 for (i=0; i < nSlashes; i++)
2214 StrCpyA(pszPointer, pszNewUrl);
2215 TRACE("<- %s\n", debugstr_a(pszUrl));
2219 /*************************************************************************
2220 * UrlCreateFromPathW [SHLWAPI.@]
2222 * See UrlCreateFromPathA.
2224 HRESULT WINAPI UrlCreateFromPathW(LPCWSTR pszPath, LPWSTR pszUrl, LPDWORD pcchUrl, DWORD dwReserved)
2226 DWORD nCharBeforeColon = 0;
2228 DWORD dwChRequired = 0;
2229 LPWSTR pszNewUrl = NULL;
2230 LPCWSTR pszConstPointer = NULL;
2231 LPWSTR pszPointer = NULL;
2235 TRACE("(%s, %p, %p, 0x%08lx)\n", debugstr_w(pszPath), pszUrl, pcchUrl, dwReserved);
2237 /* Validate arguments */
2238 if (dwReserved != 0)
2239 return E_INVALIDARG;
2240 if (!pszUrl || !pcchUrl || !pszUrl)
2241 return E_INVALIDARG;
2243 for (pszConstPointer = pszPath; *pszConstPointer; pszConstPointer++)
2245 if (isalphaW(*pszConstPointer) || isdigitW(*pszConstPointer) ||
2246 *pszConstPointer == '.' || *pszConstPointer == '-')
2250 if (*pszConstPointer == ':') /* then already in URL format, so copy */
2252 dwChRequired = lstrlenW(pszPath);
2253 *pcchUrl = dwChRequired;
2254 if (dwChRequired > *pcchUrl)
2258 StrCpyW(pszUrl, pszPath);
2262 /* then must need converting to file: format */
2264 /* Strip off leading slashes */
2265 while (*pszPath == '\\' || *pszPath == '/')
2271 dwChRequired = *pcchUrl; /* UrlEscape will fill this in with the correct amount */
2272 ret = UrlEscapeW(pszPath, pszUrl, &dwChRequired, URL_ESCAPE_PERCENT);
2273 if (ret != E_POINTER && FAILED(ret))
2275 dwChRequired += 5; /* "file:" */
2276 if ((lstrlenW(pszUrl) > 1) && isalphaW(pszUrl[0]) && (pszUrl[1] == ':'))
2278 dwChRequired += 3; /* "///" */
2284 case 0: /* no slashes */
2286 case 2: /* two slashes */
2293 default: /* three slashes */
2298 *pcchUrl = dwChRequired; /* Return number of chars required (not including termination) */
2299 if (dwChRequired > *pcchUrl)
2301 pszNewUrl = HeapAlloc(GetProcessHeap(), 0, (dwChRequired + 1) * sizeof(WCHAR));
2302 StrCpyW(pszNewUrl, fileW);
2303 pszPointer = pszNewUrl + 4;
2306 for (i=0; i < nSlashes; i++)
2311 StrCpyW(pszPointer, pszPath);
2312 StrCpyW(pszUrl, pszNewUrl);
2316 /*************************************************************************
2317 * SHAutoComplete [SHLWAPI.@]
2319 * Enable auto-completion for an edit control.
2322 * hwndEdit [I] Handle of control to enable auto-completion for
2323 * dwFlags [I] SHACF_ flags from "shlwapi.h"
2326 * Success: S_OK. Auto-completion is enabled for the control.
2327 * Failure: An HRESULT error code indicating the error.
2329 HRESULT WINAPI SHAutoComplete(HWND hwndEdit, DWORD dwFlags)
2331 FIXME("SHAutoComplete stub\n");
2335 /*************************************************************************
2336 * MLBuildResURLA [SHLWAPI.405]
2338 * Create a Url pointing to a resource in a module.
2341 * lpszLibName [I] Name of the module containing the resource
2342 * hMod [I] Callers module handle
2343 * dwFlags [I] Undocumented flags for loading the module
2344 * lpszRes [I] Resource name
2345 * lpszDest [O] Destination for resulting Url
2346 * dwDestLen [I] Length of lpszDest
2349 * Success: S_OK. lpszDest constains the resource Url.
2350 * Failure: E_INVALIDARG, if any argument is invalid, or
2351 * E_FAIL if dwDestLen is too small.
2353 HRESULT WINAPI MLBuildResURLA(LPCSTR lpszLibName, HMODULE hMod, DWORD dwFlags,
2354 LPCSTR lpszRes, LPSTR lpszDest, DWORD dwDestLen)
2356 WCHAR szLibName[MAX_PATH], szRes[MAX_PATH], szDest[MAX_PATH];
2360 MultiByteToWideChar(CP_ACP, 0, lpszLibName, -1, szLibName, sizeof(szLibName)/sizeof(WCHAR));
2363 MultiByteToWideChar(CP_ACP, 0, lpszRes, -1, szRes, sizeof(szRes)/sizeof(WCHAR));
2365 if (dwDestLen > sizeof(szLibName)/sizeof(WCHAR))
2366 dwDestLen = sizeof(szLibName)/sizeof(WCHAR);
2368 hRet = MLBuildResURLW(lpszLibName ? szLibName : NULL, hMod, dwFlags,
2369 lpszRes ? szRes : NULL, lpszDest ? szDest : NULL, dwDestLen);
2370 if (SUCCEEDED(hRet) && lpszDest)
2371 WideCharToMultiByte(CP_ACP, 0, szDest, -1, lpszDest, dwDestLen, 0, 0);
2376 /*************************************************************************
2377 * MLBuildResURLA [SHLWAPI.406]
2379 * See MLBuildResURLA.
2381 HRESULT WINAPI MLBuildResURLW(LPCWSTR lpszLibName, HMODULE hMod, DWORD dwFlags,
2382 LPCWSTR lpszRes, LPWSTR lpszDest, DWORD dwDestLen)
2384 static const WCHAR szRes[] = { 'r','e','s',':','/','/','\0' };
2385 #define szResLen ((sizeof(szRes) - sizeof(WCHAR))/sizeof(WCHAR))
2386 HRESULT hRet = E_FAIL;
2388 TRACE("(%s,%p,0x%08lx,%s,%p,%ld)\n", debugstr_w(lpszLibName), hMod, dwFlags,
2389 debugstr_w(lpszRes), lpszDest, dwDestLen);
2391 if (!lpszLibName || !hMod || hMod == INVALID_HANDLE_VALUE || !lpszRes ||
2392 !lpszDest || (dwFlags && dwFlags != 2))
2393 return E_INVALIDARG;
2395 if (dwDestLen >= szResLen + 1)
2397 dwDestLen -= (szResLen + 1);
2398 memcpy(lpszDest, szRes, sizeof(szRes));
2400 hMod = MLLoadLibraryW(lpszLibName, hMod, dwFlags);
2404 WCHAR szBuff[MAX_PATH];
2407 len = GetModuleFileNameW(hMod, szBuff, sizeof(szBuff)/sizeof(WCHAR));
2408 if (len && len < sizeof(szBuff)/sizeof(WCHAR))
2410 DWORD dwPathLen = strlenW(szBuff) + 1;
2412 if (dwDestLen >= dwPathLen)
2416 dwDestLen -= dwPathLen;
2417 memcpy(lpszDest + szResLen, szBuff, dwPathLen * sizeof(WCHAR));
2419 dwResLen = strlenW(lpszRes) + 1;
2420 if (dwDestLen >= dwResLen + 1)
2422 lpszDest[szResLen + dwPathLen + dwResLen] = '/';
2423 memcpy(lpszDest + szResLen + dwPathLen, lpszRes, dwResLen * sizeof(WCHAR));
2428 MLFreeLibrary(hMod);