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"
33 #define NO_SHLWAPI_STREAM
35 #include "wine/debug.h"
37 HMODULE WINAPI MLLoadLibraryW(LPCWSTR,HMODULE,DWORD);
38 BOOL WINAPI MLFreeLibrary(HMODULE);
39 HRESULT WINAPI MLBuildResURLW(LPCWSTR,HMODULE,DWORD,LPCWSTR,LPWSTR,DWORD);
41 WINE_DEFAULT_DEBUG_CHANNEL(shell);
43 /* The following schemes were identified in the native version of
44 * SHLWAPI.DLL version 5.50
47 URL_SCHEME_INVALID = -1,
48 URL_SCHEME_UNKNOWN = 0,
63 URL_SCHEME_JAVASCRIPT,
71 URL_SCHEME scheme_number;
75 static const SHL_2_inet_scheme shlwapi_schemes[] = {
76 {URL_SCHEME_FTP, "ftp"},
77 {URL_SCHEME_HTTP, "http"},
78 {URL_SCHEME_GOPHER, "gopher"},
79 {URL_SCHEME_MAILTO, "mailto"},
80 {URL_SCHEME_NEWS, "news"},
81 {URL_SCHEME_NNTP, "nntp"},
82 {URL_SCHEME_TELNET, "telnet"},
83 {URL_SCHEME_WAIS, "wais"},
84 {URL_SCHEME_FILE, "file"},
85 {URL_SCHEME_MK, "mk"},
86 {URL_SCHEME_HTTPS, "https"},
87 {URL_SCHEME_SHELL, "shell"},
88 {URL_SCHEME_SNEWS, "snews"},
89 {URL_SCHEME_LOCAL, "local"},
90 {URL_SCHEME_JAVASCRIPT, "javascript"},
91 {URL_SCHEME_VBSCRIPT, "vbscript"},
92 {URL_SCHEME_ABOUT, "about"},
93 {URL_SCHEME_RES, "res"},
98 LPCWSTR pScheme; /* [out] start of scheme */
99 DWORD szScheme; /* [out] size of scheme (until colon) */
100 LPCWSTR pUserName; /* [out] start of Username */
101 DWORD szUserName; /* [out] size of Username (until ":" or "@") */
102 LPCWSTR pPassword; /* [out] start of Password */
103 DWORD szPassword; /* [out] size of Password (until "@") */
104 LPCWSTR pHostName; /* [out] start of Hostname */
105 DWORD szHostName; /* [out] size of Hostname (until ":" or "/") */
106 LPCWSTR pPort; /* [out] start of Port */
107 DWORD szPort; /* [out] size of Port (until "/" or eos) */
108 LPCWSTR pQuery; /* [out] start of Query */
109 DWORD szQuery; /* [out] size of Query (until eos) */
117 } WINE_URL_SCAN_TYPE;
120 INT size; /* [in] (always 0x18) */
121 LPCSTR ap1; /* [out] start of scheme */
122 INT sizep1; /* [out] size of scheme (until colon) */
123 LPCSTR ap2; /* [out] pointer following first colon */
124 INT sizep2; /* [out] size of remainder */
125 INT fcncde; /* [out] function match of p1 (0 if unknown) */
129 INT size; /* [in] (always 0x18) */
130 LPCWSTR ap1; /* [out] start of scheme */
131 INT sizep1; /* [out] size of scheme (until colon) */
132 LPCWSTR ap2; /* [out] pointer following first colon */
133 INT sizep2; /* [out] size of remainder */
134 INT fcncde; /* [out] function match of p1 (0 if unknown) */
137 static const CHAR hexDigits[] = "0123456789ABCDEF";
139 static const WCHAR fileW[] = {'f','i','l','e','\0'};
141 static const unsigned char HashDataLookup[256] = {
142 0x01, 0x0E, 0x6E, 0x19, 0x61, 0xAE, 0x84, 0x77, 0x8A, 0xAA, 0x7D, 0x76, 0x1B,
143 0xE9, 0x8C, 0x33, 0x57, 0xC5, 0xB1, 0x6B, 0xEA, 0xA9, 0x38, 0x44, 0x1E, 0x07,
144 0xAD, 0x49, 0xBC, 0x28, 0x24, 0x41, 0x31, 0xD5, 0x68, 0xBE, 0x39, 0xD3, 0x94,
145 0xDF, 0x30, 0x73, 0x0F, 0x02, 0x43, 0xBA, 0xD2, 0x1C, 0x0C, 0xB5, 0x67, 0x46,
146 0x16, 0x3A, 0x4B, 0x4E, 0xB7, 0xA7, 0xEE, 0x9D, 0x7C, 0x93, 0xAC, 0x90, 0xB0,
147 0xA1, 0x8D, 0x56, 0x3C, 0x42, 0x80, 0x53, 0x9C, 0xF1, 0x4F, 0x2E, 0xA8, 0xC6,
148 0x29, 0xFE, 0xB2, 0x55, 0xFD, 0xED, 0xFA, 0x9A, 0x85, 0x58, 0x23, 0xCE, 0x5F,
149 0x74, 0xFC, 0xC0, 0x36, 0xDD, 0x66, 0xDA, 0xFF, 0xF0, 0x52, 0x6A, 0x9E, 0xC9,
150 0x3D, 0x03, 0x59, 0x09, 0x2A, 0x9B, 0x9F, 0x5D, 0xA6, 0x50, 0x32, 0x22, 0xAF,
151 0xC3, 0x64, 0x63, 0x1A, 0x96, 0x10, 0x91, 0x04, 0x21, 0x08, 0xBD, 0x79, 0x40,
152 0x4D, 0x48, 0xD0, 0xF5, 0x82, 0x7A, 0x8F, 0x37, 0x69, 0x86, 0x1D, 0xA4, 0xB9,
153 0xC2, 0xC1, 0xEF, 0x65, 0xF2, 0x05, 0xAB, 0x7E, 0x0B, 0x4A, 0x3B, 0x89, 0xE4,
154 0x6C, 0xBF, 0xE8, 0x8B, 0x06, 0x18, 0x51, 0x14, 0x7F, 0x11, 0x5B, 0x5C, 0xFB,
155 0x97, 0xE1, 0xCF, 0x15, 0x62, 0x71, 0x70, 0x54, 0xE2, 0x12, 0xD6, 0xC7, 0xBB,
156 0x0D, 0x20, 0x5E, 0xDC, 0xE0, 0xD4, 0xF7, 0xCC, 0xC4, 0x2B, 0xF9, 0xEC, 0x2D,
157 0xF4, 0x6F, 0xB6, 0x99, 0x88, 0x81, 0x5A, 0xD9, 0xCA, 0x13, 0xA5, 0xE7, 0x47,
158 0xE6, 0x8E, 0x60, 0xE3, 0x3E, 0xB3, 0xF6, 0x72, 0xA2, 0x35, 0xA0, 0xD7, 0xCD,
159 0xB4, 0x2F, 0x6D, 0x2C, 0x26, 0x1F, 0x95, 0x87, 0x00, 0xD8, 0x34, 0x3F, 0x17,
160 0x25, 0x45, 0x27, 0x75, 0x92, 0xB8, 0xA3, 0xC8, 0xDE, 0xEB, 0xF8, 0xF3, 0xDB,
161 0x0A, 0x98, 0x83, 0x7B, 0xE5, 0xCB, 0x4C, 0x78, 0xD1 };
163 static inline BOOL URL_NeedEscapeA(CHAR ch, DWORD dwFlags)
169 if(dwFlags & URL_ESCAPE_SPACES_ONLY) {
176 if ((dwFlags & URL_ESCAPE_PERCENT) && (ch == '%'))
179 if (ch <= 31 || ch >= 127)
201 if (dwFlags & URL_ESCAPE_SEGMENT_ONLY) return TRUE;
208 static inline BOOL URL_NeedEscapeW(WCHAR ch, DWORD dwFlags)
214 if(dwFlags & URL_ESCAPE_SPACES_ONLY) {
221 if ((dwFlags & URL_ESCAPE_PERCENT) && (ch == L'%'))
224 if (ch <= 31 || ch >= 127)
246 if (dwFlags & URL_ESCAPE_SEGMENT_ONLY) return TRUE;
253 static BOOL URL_JustLocation(LPCWSTR str)
255 while(*str && (*str == L'/')) str++;
257 while (*str && ((*str == L'-') ||
259 isalnumW(*str))) str++;
260 if (*str == L'/') return FALSE;
266 /*************************************************************************
269 * Parse a Url into its constituent parts.
273 * y [O] Undocumented structure holding the parsed information
276 * Success: S_OK. y contains the parsed Url details.
277 * Failure: An HRESULT error code.
279 DWORD WINAPI ParseURLA(LPCSTR x, UNKNOWN_SHLWAPI_1 *y)
282 const SHL_2_inet_scheme *inet_pro;
284 y->fcncde = URL_SCHEME_INVALID;
285 if (y->size != 0x18) return E_INVALIDARG;
286 /* FIXME: leading white space generates error of 0x80041001 which
289 if (*x <= ' ') return 0x80041001;
304 /* check for no scheme in string start */
305 /* (apparently schemes *must* be larger than a single character) */
306 if ((*x == '\0') || (y->sizep1 <= 1)) {
311 /* found scheme, set length of remainder */
312 y->sizep2 = lstrlenA(y->ap2);
314 /* see if known scheme and return indicator number */
315 y->fcncde = URL_SCHEME_UNKNOWN;
316 inet_pro = shlwapi_schemes;
317 while (inet_pro->scheme_name) {
318 if (!strncasecmp(inet_pro->scheme_name, y->ap1,
319 min(y->sizep1, lstrlenA(inet_pro->scheme_name)))) {
320 y->fcncde = inet_pro->scheme_number;
328 /*************************************************************************
331 * Unicode version of ParseURLA.
333 DWORD WINAPI ParseURLW(LPCWSTR x, UNKNOWN_SHLWAPI_2 *y)
336 const SHL_2_inet_scheme *inet_pro;
340 y->fcncde = URL_SCHEME_INVALID;
341 if (y->size != 0x18) return E_INVALIDARG;
342 /* FIXME: leading white space generates error of 0x80041001 which
345 if (*x <= L' ') return 0x80041001;
360 /* check for no scheme in string start */
361 /* (apparently schemes *must* be larger than a single character) */
362 if ((*x == L'\0') || (y->sizep1 <= 1)) {
367 /* found scheme, set length of remainder */
368 y->sizep2 = lstrlenW(y->ap2);
370 /* see if known scheme and return indicator number */
371 len = WideCharToMultiByte(0, 0, y->ap1, y->sizep1, 0, 0, 0, 0);
372 cmpstr = (LPSTR)HeapAlloc(GetProcessHeap(), 0, len+1);
373 WideCharToMultiByte(0, 0, y->ap1, y->sizep1, cmpstr, len+1, 0, 0);
374 y->fcncde = URL_SCHEME_UNKNOWN;
375 inet_pro = shlwapi_schemes;
376 while (inet_pro->scheme_name) {
377 if (!strncasecmp(inet_pro->scheme_name, cmpstr,
378 min(len, lstrlenA(inet_pro->scheme_name)))) {
379 y->fcncde = inet_pro->scheme_number;
384 HeapFree(GetProcessHeap(), 0, cmpstr);
388 /*************************************************************************
389 * UrlCanonicalizeA [SHLWAPI.@]
391 * Canonicalize a Url.
394 * pszUrl [I] Url to cCanonicalize
395 * pszCanonicalized [O] Destination for converted Url.
396 * pcchCanonicalized [I/O] Length of pszUrl, destination for length of pszCanonicalized
397 * dwFlags [I] Flags controlling the conversion.
400 * Success: S_OK. The pszCanonicalized contains the converted Url.
401 * Failure: E_POINTER, if *pcchCanonicalized is too small.
403 * MSDN incorrectly describes the flags for this function. They should be:
404 *| URL_DONT_ESCAPE_EXTRA_INFO 0x02000000
405 *| URL_ESCAPE_SPACES_ONLY 0x04000000
406 *| URL_ESCAPE_PERCENT 0x00001000
407 *| URL_ESCAPE_UNSAFE 0x10000000
408 *| URL_UNESCAPE 0x10000000
409 *| URL_DONT_SIMPLIFY 0x08000000
410 *| URL_ESCAPE_SEGMENT_ONLY 0x00002000
412 HRESULT WINAPI UrlCanonicalizeA(LPCSTR pszUrl, LPSTR pszCanonicalized,
413 LPDWORD pcchCanonicalized, DWORD dwFlags)
415 LPWSTR base, canonical;
416 DWORD ret, len, len2;
418 TRACE("(%s %p %p 0x%08lx) using W version\n",
419 debugstr_a(pszUrl), pszCanonicalized,
420 pcchCanonicalized, dwFlags);
422 if(!pszUrl || !pszCanonicalized || !pcchCanonicalized)
425 base = (LPWSTR) HeapAlloc(GetProcessHeap(), 0,
426 (2*INTERNET_MAX_URL_LENGTH) * sizeof(WCHAR));
427 canonical = base + INTERNET_MAX_URL_LENGTH;
429 MultiByteToWideChar(0, 0, pszUrl, -1, base, INTERNET_MAX_URL_LENGTH);
430 len = INTERNET_MAX_URL_LENGTH;
432 ret = UrlCanonicalizeW(base, canonical, &len, dwFlags);
434 HeapFree(GetProcessHeap(), 0, base);
438 len2 = WideCharToMultiByte(0, 0, canonical, len, 0, 0, 0, 0);
439 if (len2 > *pcchCanonicalized) {
440 *pcchCanonicalized = len;
441 HeapFree(GetProcessHeap(), 0, base);
444 WideCharToMultiByte(0, 0, canonical, len+1, pszCanonicalized,
445 *pcchCanonicalized, 0, 0);
446 *pcchCanonicalized = len2;
447 HeapFree(GetProcessHeap(), 0, base);
451 /*************************************************************************
452 * UrlCanonicalizeW [SHLWAPI.@]
454 * See UrlCanonicalizeA.
456 HRESULT WINAPI UrlCanonicalizeW(LPCWSTR pszUrl, LPWSTR pszCanonicalized,
457 LPDWORD pcchCanonicalized, DWORD dwFlags)
461 LPWSTR lpszUrlCpy, wk1, wk2, mp, root;
462 INT nLen, nByteLen, state;
464 TRACE("(%s %p %p 0x%08lx)\n", debugstr_w(pszUrl), pszCanonicalized,
465 pcchCanonicalized, dwFlags);
467 if(!pszUrl || !pszCanonicalized || !pcchCanonicalized)
470 nByteLen = (lstrlenW(pszUrl) + 1) * sizeof(WCHAR); /* length in bytes */
471 lpszUrlCpy = HeapAlloc(GetProcessHeap(), 0, nByteLen);
473 if (dwFlags & URL_DONT_SIMPLIFY)
474 memcpy(lpszUrlCpy, pszUrl, nByteLen);
480 * 1 have 2[+] alnum 2,3
481 * 2 have scheme (found :) 4,6,3
482 * 3 failed (no location)
484 * 5 have 1[+] alnum 6,3
485 * 6 have location (found /) save root location
488 wk1 = (LPWSTR)pszUrl;
494 if (!isalnumW(*wk1)) {state = 3; break;}
496 if (!isalnumW(*wk1)) {state = 3; break;}
502 if (*wk1++ == L':') state = 2;
505 if (*wk1 != L'/') {state = 3; break;}
507 if (*wk1 != L'/') {state = 6; break;}
517 if (!isalnumW(*wk1) && (*wk1 != L'-') && (*wk1 != L'.')) {state = 3; break;}
518 while(isalnumW(*wk1) || (*wk1 == L'-') || (*wk1 == L'.')) *wk2++ = *wk1++;
522 if (*wk1 != L'/') {state = 3; break;}
527 /* Now at root location, cannot back up any more. */
528 /* "root" will point at the '/' */
531 TRACE("wk1=%c\n", (CHAR)*wk1);
532 mp = strchrW(wk1, L'/');
540 strncpyW(wk2, wk1, nLen);
544 TRACE("found '/.'\n");
545 if (*(wk1+1) == L'/') {
546 /* case of /./ -> skip the ./ */
549 else if (*(wk1+1) == L'.') {
550 /* found /.. look for next / */
551 TRACE("found '/..'\n");
552 if (*(wk1+2) == L'/' || *(wk1+2) == L'?' || *(wk1+2) == L'#' || *(wk1+2) == 0) {
553 /* case /../ -> need to backup wk2 */
554 TRACE("found '/../'\n");
555 *(wk2-1) = L'\0'; /* set end of string */
556 mp = strrchrW(root, L'/');
557 if (mp && (mp >= root)) {
558 /* found valid backup point */
566 /* did not find point, restore '/' */
576 FIXME("how did we get here - state=%d\n", state);
581 TRACE("Simplified, orig <%s>, simple <%s>\n",
582 debugstr_w(pszUrl), debugstr_w(lpszUrlCpy));
584 nLen = lstrlenW(lpszUrlCpy);
585 while ((nLen > 0) && ((lpszUrlCpy[nLen-1] == '\r')||(lpszUrlCpy[nLen-1] == '\n')))
586 lpszUrlCpy[--nLen]=0;
588 if(dwFlags & URL_UNESCAPE)
589 UrlUnescapeW(lpszUrlCpy, NULL, NULL, URL_UNESCAPE_INPLACE);
591 if((EscapeFlags = dwFlags & (URL_ESCAPE_UNSAFE |
592 URL_ESCAPE_SPACES_ONLY |
594 URL_DONT_ESCAPE_EXTRA_INFO |
595 URL_ESCAPE_SEGMENT_ONLY ))) {
596 EscapeFlags &= ~URL_ESCAPE_UNSAFE;
597 hr = UrlEscapeW(lpszUrlCpy, pszCanonicalized, pcchCanonicalized,
599 } else { /* No escaping needed, just copy the string */
600 nLen = lstrlenW(lpszUrlCpy);
601 if(nLen < *pcchCanonicalized)
602 memcpy(pszCanonicalized, lpszUrlCpy, (nLen + 1)*sizeof(WCHAR));
607 *pcchCanonicalized = nLen;
610 HeapFree(GetProcessHeap(), 0, lpszUrlCpy);
613 TRACE("result %s\n", debugstr_w(pszCanonicalized));
618 /*************************************************************************
619 * UrlCombineA [SHLWAPI.@]
624 * pszBase [I] Base Url
625 * pszRelative [I] Url to combine with pszBase
626 * pszCombined [O] Destination for combined Url
627 * pcchCombined [O] Destination for length of pszCombined
628 * dwFlags [I] URL_ flags from "shlwapi.h"
631 * Success: S_OK. pszCombined contains the combined Url, pcchCombined
632 * contains its length.
633 * Failure: An HRESULT error code indicating the error.
635 HRESULT WINAPI UrlCombineA(LPCSTR pszBase, LPCSTR pszRelative,
636 LPSTR pszCombined, LPDWORD pcchCombined,
639 LPWSTR base, relative, combined;
640 DWORD ret, len, len2;
642 TRACE("(base %s, Relative %s, Combine size %ld, flags %08lx) using W version\n",
643 debugstr_a(pszBase),debugstr_a(pszRelative),
644 pcchCombined?*pcchCombined:0,dwFlags);
646 if(!pszBase || !pszRelative || !pszCombined || !pcchCombined)
649 base = (LPWSTR) HeapAlloc(GetProcessHeap(), 0,
650 (3*INTERNET_MAX_URL_LENGTH) * sizeof(WCHAR));
651 relative = base + INTERNET_MAX_URL_LENGTH;
652 combined = relative + INTERNET_MAX_URL_LENGTH;
654 MultiByteToWideChar(0, 0, pszBase, -1, base, INTERNET_MAX_URL_LENGTH);
655 MultiByteToWideChar(0, 0, pszRelative, -1, relative, INTERNET_MAX_URL_LENGTH);
656 len = INTERNET_MAX_URL_LENGTH;
658 ret = UrlCombineW(base, relative, combined, &len, dwFlags);
660 HeapFree(GetProcessHeap(), 0, base);
664 len2 = WideCharToMultiByte(0, 0, combined, len, 0, 0, 0, 0);
665 if (len2 > *pcchCombined) {
666 *pcchCombined = len2;
667 HeapFree(GetProcessHeap(), 0, base);
670 WideCharToMultiByte(0, 0, combined, len+1, pszCombined, *pcchCombined,
672 *pcchCombined = len2;
673 HeapFree(GetProcessHeap(), 0, base);
677 /*************************************************************************
678 * UrlCombineW [SHLWAPI.@]
682 HRESULT WINAPI UrlCombineW(LPCWSTR pszBase, LPCWSTR pszRelative,
683 LPWSTR pszCombined, LPDWORD pcchCombined,
686 UNKNOWN_SHLWAPI_2 base, relative;
687 DWORD myflags, sizeloc = 0;
688 DWORD len, res1, res2, process_case = 0;
689 LPWSTR work, preliminary, mbase, mrelative;
690 WCHAR myfilestr[] = {'f','i','l','e',':','/','/','/','\0'};
691 WCHAR single_slash[] = {'/','\0'};
694 TRACE("(base %s, Relative %s, Combine size %ld, flags %08lx)\n",
695 debugstr_w(pszBase),debugstr_w(pszRelative),
696 pcchCombined?*pcchCombined:0,dwFlags);
698 if(!pszBase || !pszRelative || !pszCombined || !pcchCombined)
704 /* Get space for duplicates of the input and the output */
705 preliminary = HeapAlloc(GetProcessHeap(), 0, (3*INTERNET_MAX_URL_LENGTH) *
707 mbase = preliminary + INTERNET_MAX_URL_LENGTH;
708 mrelative = mbase + INTERNET_MAX_URL_LENGTH;
709 *preliminary = L'\0';
711 /* Canonicalize the base input prior to looking for the scheme */
712 myflags = dwFlags & (URL_DONT_SIMPLIFY | URL_UNESCAPE);
713 len = INTERNET_MAX_URL_LENGTH;
714 ret = UrlCanonicalizeW(pszBase, mbase, &len, myflags);
716 /* Canonicalize the relative input prior to looking for the scheme */
717 len = INTERNET_MAX_URL_LENGTH;
718 ret = UrlCanonicalizeW(pszRelative, mrelative, &len, myflags);
720 /* See if the base has a scheme */
721 res1 = ParseURLW(mbase, &base);
723 /* if pszBase has no scheme, then return pszRelative */
724 TRACE("no scheme detected in Base\n");
729 /* get size of location field (if it exists) */
730 work = (LPWSTR)base.ap2;
732 if (*work++ == L'/') {
733 if (*work++ == L'/') {
734 /* At this point have start of location and
735 * it ends at next '/' or end of string.
737 while(*work && (*work != L'/')) work++;
738 sizeloc = (DWORD)(work - base.ap2);
742 /* Change .sizep2 to not have the last leaf in it,
743 * Note: we need to start after the location (if it exists)
745 work = strrchrW((base.ap2+sizeloc), L'/');
747 len = (DWORD)(work - base.ap2 + 1);
752 * .ap2 points to location (starting with '//')
753 * .sizep2 length of location (above) and rest less the last
755 * sizeloc length of location (above) up to but not including
759 res2 = ParseURLW(mrelative, &relative);
761 /* no scheme in pszRelative */
762 TRACE("no scheme detected in Relative\n");
763 relative.ap2 = mrelative; /* case 3,4,5 depends on this */
764 relative.sizep2 = strlenW(mrelative);
765 if (*pszRelative == L':') {
766 /* case that is either left alone or uses pszBase */
767 if (dwFlags & URL_PLUGGABLE_PROTOCOL) {
774 if (isalnum(*mrelative) && (*(mrelative + 1) == L':')) {
775 /* case that becomes "file:///" */
776 strcpyW(preliminary, myfilestr);
780 if ((*mrelative == L'/') && (*(mrelative+1) == L'/')) {
781 /* pszRelative has location and rest */
785 if (*mrelative == L'/') {
786 /* case where pszRelative is root to location */
790 process_case = (*base.ap2 == L'/') ? 5 : 3;
794 /* handle cases where pszRelative has scheme */
795 if ((base.sizep1 == relative.sizep1) &&
796 (strncmpW(base.ap1, relative.ap1, base.sizep1) == 0)) {
798 /* since the schemes are the same */
799 if ((*relative.ap2 == L'/') && (*(relative.ap2+1) == L'/')) {
800 /* case where pszRelative replaces location and following */
804 if (*relative.ap2 == L'/') {
805 /* case where pszRelative is root to location */
809 /* case where scheme is followed by document path */
813 if ((*relative.ap2 == L'/') && (*(relative.ap2+1) == L'/')) {
814 /* case where pszRelative replaces scheme, location,
815 * and following and handles PLUGGABLE
822 } while(FALSE); /* a litte trick to allow easy exit from nested if's */
826 switch (process_case) {
829 * Return pszRelative appended to what ever is in pszCombined,
830 * (which may the string "file:///"
832 len = strlenW(mrelative) + strlenW(preliminary);
833 if (len+1 > *pcchCombined) {
838 strcatW(preliminary, mrelative);
842 * Same as case 1, but if URL_PLUGGABLE_PROTOCOL was specified
843 * and pszRelative starts with "//", then append a "/"
845 len = strlenW(mrelative) + 1;
846 if (len+1 > *pcchCombined) {
851 strcpyW(preliminary, mrelative);
852 if (!(dwFlags & URL_PLUGGABLE_PROTOCOL) &&
853 URL_JustLocation(relative.ap2))
854 strcatW(preliminary, single_slash);
858 * Return the pszBase scheme with pszRelative. Basicly
859 * keeps the scheme and replaces the domain and following.
861 len = base.sizep1 + 1 + relative.sizep2 + 1;
862 if (len+1 > *pcchCombined) {
867 strncpyW(preliminary, base.ap1, base.sizep1 + 1);
868 work = preliminary + base.sizep1 + 1;
869 strcpyW(work, relative.ap2);
870 if (!(dwFlags & URL_PLUGGABLE_PROTOCOL) &&
871 URL_JustLocation(relative.ap2))
872 strcatW(work, single_slash);
876 * Return the pszBase scheme and location but everything
877 * after the location is pszRelative. (Replace document
880 len = base.sizep1 + 1 + sizeloc + relative.sizep2 + 1;
881 if (len+1 > *pcchCombined) {
886 strncpyW(preliminary, base.ap1, base.sizep1+1+sizeloc);
887 work = preliminary + base.sizep1 + 1 + sizeloc;
888 if (dwFlags & URL_PLUGGABLE_PROTOCOL)
890 strcpyW(work, relative.ap2);
894 * Return the pszBase without its document (if any) and
895 * append pszRelative after its scheme.
897 len = base.sizep1 + 1 + base.sizep2 + relative.sizep2;
898 if (len+1 > *pcchCombined) {
903 strncpyW(preliminary, base.ap1, base.sizep1+1+base.sizep2);
904 work = preliminary + base.sizep1+1+base.sizep2 - 1;
907 strcpyW(work, relative.ap2);
911 FIXME("How did we get here????? process_case=%ld\n", process_case);
917 * Now that the combining is done, process the escape options if
918 * necessary, otherwise just copy the string.
920 myflags = dwFlags & (URL_ESCAPE_PERCENT |
921 URL_ESCAPE_SPACES_ONLY |
922 URL_DONT_ESCAPE_EXTRA_INFO |
923 URL_ESCAPE_SEGMENT_ONLY);
925 ret = UrlEscapeW(preliminary, pszCombined,
926 pcchCombined, myflags);
928 len = (strlenW(preliminary) + 1) * sizeof(WCHAR);
929 memcpy(pszCombined, preliminary, len);
930 *pcchCombined = strlenW(preliminary);
932 TRACE("return-%ld len=%ld, %s\n",
933 process_case, *pcchCombined, debugstr_w(pszCombined));
935 HeapFree(GetProcessHeap(), 0, preliminary);
939 /*************************************************************************
940 * UrlEscapeA [SHLWAPI.@]
942 * Converts unsafe characters in a Url into escape sequences.
945 * pszUrl [I] Url to modify
946 * pszEscaped [O] Destination for modified Url
947 * pcchEscaped [I/O] Length of pszUrl, destination for length of pszEscaped
948 * dwFlags [I] URL_ flags from "shlwapi.h"
951 * Success: S_OK. pszEscaped contains the escaped Url, pcchEscaped
952 * contains its length.
953 * Failure: E_POINTER, if pszEscaped is not large enough. In this case
954 * pcchEscaped is set to the required length.
956 * Converts unsafe characters into their escape sequences.
959 * - By default this function stops converting at the first '?' or
960 * '#' character (MSDN does not document this).
961 * - If dwFlags contains URL_ESCAPE_SPACES_ONLY then only spaces are
962 * converted, but the conversion continues past a '?' or '#'.
963 * - Note that this function did not work well (or at all) in shlwapi version 4.
966 * Only the following flags are implemented:
967 *| URL_ESCAPE_SPACES_ONLY
968 *| URL_DONT_ESCAPE_EXTRA_INFO
969 *| URL_ESCAPE_SEGMENT_ONLY
970 *| URL_ESCAPE_PERCENT
972 HRESULT WINAPI UrlEscapeA(
979 DWORD needed = 0, ret;
980 BOOL stop_escaping = FALSE;
981 char next[3], *dst = pszEscaped;
984 TRACE("(%s %p %lx 0x%08lx)\n", debugstr_a(pszUrl), pszEscaped,
985 pcchEscaped?*pcchEscaped:0, dwFlags);
987 if(!pszUrl || !pszEscaped || !pcchEscaped)
990 if(dwFlags & ~(URL_ESCAPE_SPACES_ONLY |
991 URL_ESCAPE_SEGMENT_ONLY |
992 URL_DONT_ESCAPE_EXTRA_INFO |
994 FIXME("Unimplemented flags: %08lx\n", dwFlags);
997 if (dwFlags & URL_ESCAPE_SPACES_ONLY)
998 /* if SPACES_ONLY specified, reset the other controls */
999 dwFlags &= ~(URL_DONT_ESCAPE_EXTRA_INFO |
1000 URL_ESCAPE_PERCENT |
1001 URL_ESCAPE_SEGMENT_ONLY);
1004 /* if SPACES_ONLY *not* specified then assume DONT_ESCAPE_EXTRA_INFO */
1005 dwFlags |= URL_DONT_ESCAPE_EXTRA_INFO;
1007 for(src = pszUrl; *src; src++) {
1008 if(!(dwFlags & URL_ESCAPE_SEGMENT_ONLY) &&
1009 (dwFlags & URL_DONT_ESCAPE_EXTRA_INFO) &&
1010 (*src == '#' || *src == '?'))
1011 stop_escaping = TRUE;
1013 if(URL_NeedEscapeA(*src, dwFlags) && stop_escaping == FALSE) {
1014 /* TRACE("escaping %c\n", *src); */
1016 next[1] = hexDigits[(*src >> 4) & 0xf];
1017 next[2] = hexDigits[*src & 0xf];
1020 /* TRACE("passing %c\n", *src); */
1025 if(needed + len <= *pcchEscaped) {
1026 memcpy(dst, next, len);
1032 if(needed < *pcchEscaped) {
1036 needed++; /* add one for the '\0' */
1039 *pcchEscaped = needed;
1043 /*************************************************************************
1044 * UrlEscapeW [SHLWAPI.@]
1048 HRESULT WINAPI UrlEscapeW(
1051 LPDWORD pcchEscaped,
1055 DWORD needed = 0, ret;
1056 BOOL stop_escaping = FALSE;
1057 WCHAR next[5], *dst = pszEscaped;
1060 TRACE("(%s %p %p 0x%08lx)\n", debugstr_w(pszUrl), pszEscaped,
1061 pcchEscaped, dwFlags);
1063 if(!pszUrl || !pszEscaped || !pcchEscaped)
1064 return E_INVALIDARG;
1066 if(dwFlags & ~(URL_ESCAPE_SPACES_ONLY |
1067 URL_ESCAPE_SEGMENT_ONLY |
1068 URL_DONT_ESCAPE_EXTRA_INFO |
1069 URL_ESCAPE_PERCENT))
1070 FIXME("Unimplemented flags: %08lx\n", dwFlags);
1073 if (dwFlags & URL_ESCAPE_SPACES_ONLY)
1074 /* if SPACES_ONLY specified, reset the other controls */
1075 dwFlags &= ~(URL_DONT_ESCAPE_EXTRA_INFO |
1076 URL_ESCAPE_PERCENT |
1077 URL_ESCAPE_SEGMENT_ONLY);
1080 /* if SPACES_ONLY *not* specified the assume DONT_ESCAPE_EXTRA_INFO */
1081 dwFlags |= URL_DONT_ESCAPE_EXTRA_INFO;
1083 for(src = pszUrl; *src; src++) {
1085 * if(!(dwFlags & URL_ESCAPE_SPACES_ONLY) &&
1086 * (*src == L'#' || *src == L'?'))
1087 * stop_escaping = TRUE;
1089 if(!(dwFlags & URL_ESCAPE_SEGMENT_ONLY) &&
1090 (dwFlags & URL_DONT_ESCAPE_EXTRA_INFO) &&
1091 (*src == L'#' || *src == L'?'))
1092 stop_escaping = TRUE;
1094 if(URL_NeedEscapeW(*src, dwFlags) && stop_escaping == FALSE) {
1095 /* TRACE("escaping %c\n", *src); */
1098 * I would have assumed that the W form would escape
1099 * the character with 4 hex digits (or even 8),
1100 * however, experiments show that native shlwapi escapes
1101 * with only 2 hex digits.
1102 * next[1] = hexDigits[(*src >> 12) & 0xf];
1103 * next[2] = hexDigits[(*src >> 8) & 0xf];
1104 * next[3] = hexDigits[(*src >> 4) & 0xf];
1105 * next[4] = hexDigits[*src & 0xf];
1108 next[1] = hexDigits[(*src >> 4) & 0xf];
1109 next[2] = hexDigits[*src & 0xf];
1112 /* TRACE("passing %c\n", *src); */
1117 if(needed + len <= *pcchEscaped) {
1118 memcpy(dst, next, len*sizeof(WCHAR));
1124 if(needed < *pcchEscaped) {
1128 needed++; /* add one for the '\0' */
1131 *pcchEscaped = needed;
1136 /*************************************************************************
1137 * UrlUnescapeA [SHLWAPI.@]
1139 * Converts Url escape sequences back to ordinary characters.
1142 * pszUrl [I/O] Url to convert
1143 * pszUnescaped [O] Destination for converted Url
1144 * pcchUnescaped [I/O] Size of output string
1145 * dwFlags [I] URL_ESCAPE_ Flags from "shlwapi.h"
1148 * Success: S_OK. The converted value is in pszUnescaped, or in pszUrl if
1149 * dwFlags includes URL_ESCAPE_INPLACE.
1150 * Failure: E_POINTER if the converted Url is bigger than pcchUnescaped. In
1151 * this case pcchUnescaped is set to the size required.
1153 * If dwFlags includes URL_DONT_ESCAPE_EXTRA_INFO, the conversion stops at
1154 * the first occurrence of either a '?' or '#' character.
1156 HRESULT WINAPI UrlUnescapeA(
1159 LPDWORD pcchUnescaped,
1166 BOOL stop_unescaping = FALSE;
1168 TRACE("(%s, %p, %p, 0x%08lx)\n", debugstr_a(pszUrl), pszUnescaped,
1169 pcchUnescaped, dwFlags);
1171 if(!pszUrl || !pszUnescaped || !pcchUnescaped)
1172 return E_INVALIDARG;
1174 if(dwFlags & URL_UNESCAPE_INPLACE)
1179 for(src = pszUrl, needed = 0; *src; src++, needed++) {
1180 if(dwFlags & URL_DONT_UNESCAPE_EXTRA_INFO &&
1181 (*src == '#' || *src == '?')) {
1182 stop_unescaping = TRUE;
1184 } else if(*src == '%' && isxdigit(*(src + 1)) && isxdigit(*(src + 2))
1185 && stop_unescaping == FALSE) {
1188 memcpy(buf, src + 1, 2);
1190 ih = strtol(buf, NULL, 16);
1192 src += 2; /* Advance to end of escape */
1196 if(dwFlags & URL_UNESCAPE_INPLACE || needed < *pcchUnescaped)
1200 if(dwFlags & URL_UNESCAPE_INPLACE || needed < *pcchUnescaped) {
1204 needed++; /* add one for the '\0' */
1207 if(!(dwFlags & URL_UNESCAPE_INPLACE))
1208 *pcchUnescaped = needed;
1211 TRACE("result %s\n", (dwFlags & URL_UNESCAPE_INPLACE) ?
1212 debugstr_a(pszUrl) : debugstr_a(pszUnescaped));
1218 /*************************************************************************
1219 * UrlUnescapeW [SHLWAPI.@]
1223 HRESULT WINAPI UrlUnescapeW(
1225 LPWSTR pszUnescaped,
1226 LPDWORD pcchUnescaped,
1233 BOOL stop_unescaping = FALSE;
1235 TRACE("(%s, %p, %p, 0x%08lx)\n", debugstr_w(pszUrl), pszUnescaped,
1236 pcchUnescaped, dwFlags);
1238 if(!pszUrl || !pszUnescaped || !pcchUnescaped)
1239 return E_INVALIDARG;
1241 if(dwFlags & URL_UNESCAPE_INPLACE)
1246 for(src = pszUrl, needed = 0; *src; src++, needed++) {
1247 if(dwFlags & URL_DONT_UNESCAPE_EXTRA_INFO &&
1248 (*src == L'#' || *src == L'?')) {
1249 stop_unescaping = TRUE;
1251 } else if(*src == L'%' && isxdigitW(*(src + 1)) && isxdigitW(*(src + 2))
1252 && stop_unescaping == FALSE) {
1255 memcpy(buf, src + 1, 2*sizeof(WCHAR));
1257 ih = StrToIntW(buf);
1259 src += 2; /* Advance to end of escape */
1263 if(dwFlags & URL_UNESCAPE_INPLACE || needed < *pcchUnescaped)
1267 if(dwFlags & URL_UNESCAPE_INPLACE || needed < *pcchUnescaped) {
1271 needed++; /* add one for the '\0' */
1274 if(!(dwFlags & URL_UNESCAPE_INPLACE))
1275 *pcchUnescaped = needed;
1278 TRACE("result %s\n", (dwFlags & URL_UNESCAPE_INPLACE) ?
1279 debugstr_w(pszUrl) : debugstr_w(pszUnescaped));
1285 /*************************************************************************
1286 * UrlGetLocationA [SHLWAPI.@]
1288 * Get the location from a Url.
1291 * pszUrl [I] Url to get the location from
1294 * A pointer to the start of the location in pszUrl, or NULL if there is
1298 * - MSDN erroneously states that "The location is the segment of the Url
1299 * starting with a '?' or '#' character". Neither V4 nor V5 of shlwapi.dll
1300 * stop at '?' and always return a NULL in this case.
1301 * - MSDN also erroneously states that "If a file URL has a query string,
1302 * the returned string is the query string". In all tested cases, if the
1303 * Url starts with "fi" then a NULL is returned. V5 gives the following results:
1306 *| NULL file://aa/b/cd#hohoh
1307 *| #hohoh http://aa/b/cd#hohoh
1308 *| NULL fi://aa/b/cd#hohoh
1309 *| #hohoh ff://aa/b/cd#hohoh
1311 LPCSTR WINAPI UrlGetLocationA(
1314 UNKNOWN_SHLWAPI_1 base;
1318 res1 = ParseURLA(pszUrl, &base);
1319 if (res1) return NULL; /* invalid scheme */
1321 /* if scheme is file: then never return pointer */
1322 if (strncmp(base.ap1, "file", min(4,base.sizep1)) == 0) return NULL;
1324 /* Look for '#' and return its addr */
1325 return strchr(base.ap2, '#');
1328 /*************************************************************************
1329 * UrlGetLocationW [SHLWAPI.@]
1331 * See UrlGetLocationA.
1333 LPCWSTR WINAPI UrlGetLocationW(
1336 UNKNOWN_SHLWAPI_2 base;
1340 res1 = ParseURLW(pszUrl, &base);
1341 if (res1) return NULL; /* invalid scheme */
1343 /* if scheme is file: then never return pointer */
1344 if (strncmpW(base.ap1, fileW, min(4,base.sizep1)) == 0) return NULL;
1346 /* Look for '#' and return its addr */
1347 return strchrW(base.ap2, L'#');
1350 /*************************************************************************
1351 * UrlCompareA [SHLWAPI.@]
1356 * pszUrl1 [I] First Url to compare
1357 * pszUrl2 [I] Url to compare to pszUrl1
1358 * fIgnoreSlash [I] TRUE = compare only up to a final slash
1361 * less than zero, zero, or greater than zero indicating pszUrl2 is greater
1362 * than, equal to, or less than pszUrl1 respectively.
1364 INT WINAPI UrlCompareA(
1369 INT ret, len, len1, len2;
1372 return strcmp(pszUrl1, pszUrl2);
1373 len1 = strlen(pszUrl1);
1374 if (pszUrl1[len1-1] == '/') len1--;
1375 len2 = strlen(pszUrl2);
1376 if (pszUrl2[len2-1] == '/') len2--;
1378 return strncmp(pszUrl1, pszUrl2, len1);
1379 len = min(len1, len2);
1380 ret = strncmp(pszUrl1, pszUrl2, len);
1381 if (ret) return ret;
1382 if (len1 > len2) return 1;
1386 /*************************************************************************
1387 * UrlCompareW [SHLWAPI.@]
1391 INT WINAPI UrlCompareW(
1397 size_t len, len1, len2;
1400 return strcmpW(pszUrl1, pszUrl2);
1401 len1 = strlenW(pszUrl1);
1402 if (pszUrl1[len1-1] == '/') len1--;
1403 len2 = strlenW(pszUrl2);
1404 if (pszUrl2[len2-1] == '/') len2--;
1406 return strncmpW(pszUrl1, pszUrl2, len1);
1407 len = min(len1, len2);
1408 ret = strncmpW(pszUrl1, pszUrl2, len);
1409 if (ret) return ret;
1410 if (len1 > len2) return 1;
1414 /*************************************************************************
1415 * HashData [SHLWAPI.@]
1417 * Hash an input block into a variable sized digest.
1420 * lpSrc [I] Input block
1421 * nSrcLen [I] Length of lpSrc
1422 * lpDest [I] Output for hash digest
1423 * nDestLen [I] Length of lpDest
1426 * Success: TRUE. lpDest is filled with the computed hash value.
1427 * Failure: FALSE, if any argument is invalid.
1429 HRESULT WINAPI HashData(const unsigned char *lpSrc, DWORD nSrcLen,
1430 unsigned char *lpDest, DWORD nDestLen)
1432 INT srcCount = nSrcLen - 1, destCount = nDestLen - 1;
1434 if (IsBadReadPtr(lpSrc, nSrcLen) ||
1435 IsBadWritePtr(lpDest, nDestLen))
1436 return E_INVALIDARG;
1438 while (destCount >= 0)
1440 lpDest[destCount] = (destCount & 0xff);
1444 while (srcCount >= 0)
1446 destCount = nDestLen - 1;
1447 while (destCount >= 0)
1449 lpDest[destCount] = HashDataLookup[lpSrc[srcCount] ^ lpDest[destCount]];
1457 /*************************************************************************
1458 * UrlHashA [SHLWAPI.@]
1460 * Produce a Hash from a Url.
1463 * pszUrl [I] Url to hash
1464 * lpDest [O] Destinationh for hash
1465 * nDestLen [I] Length of lpDest
1468 * Success: S_OK. lpDest is filled with the computed hash value.
1469 * Failure: E_INVALIDARG, if any argument is invalid.
1471 HRESULT WINAPI UrlHashA(LPCSTR pszUrl, unsigned char *lpDest, DWORD nDestLen)
1473 if (IsBadStringPtrA(pszUrl, -1) || IsBadWritePtr(lpDest, nDestLen))
1474 return E_INVALIDARG;
1476 HashData((PBYTE)pszUrl, (int)strlen(pszUrl), lpDest, nDestLen);
1480 /*************************************************************************
1481 * UrlHashW [SHLWAPI.@]
1485 HRESULT WINAPI UrlHashW(LPCWSTR pszUrl, unsigned char *lpDest, DWORD nDestLen)
1487 char szUrl[MAX_PATH];
1489 TRACE("(%s,%p,%ld)\n",debugstr_w(pszUrl), lpDest, nDestLen);
1491 if (IsBadStringPtrW(pszUrl, -1) || IsBadWritePtr(lpDest, nDestLen))
1492 return E_INVALIDARG;
1494 /* Win32 hashes the data as an ASCII string, presumably so that both A+W
1495 * return the same digests for the same URL.
1497 WideCharToMultiByte(0, 0, pszUrl, -1, szUrl, MAX_PATH, 0, 0);
1498 HashData((PBYTE)szUrl, (int)strlen(szUrl), lpDest, nDestLen);
1502 /*************************************************************************
1503 * UrlApplySchemeA [SHLWAPI.@]
1505 * Apply a scheme to a Url.
1508 * pszIn [I] Url to apply scheme to
1509 * pszOut [O] Destination for modified Url
1510 * pcchOut [I/O] Length of pszOut/destination for length of pszOut
1511 * dwFlags [I] URL_ flags from "shlwapi.h"
1514 * Success: S_OK: pszOut contains the modified Url, pcchOut contains its length.
1515 * Failure: An HRESULT error code describing the error.
1517 HRESULT WINAPI UrlApplySchemeA(LPCSTR pszIn, LPSTR pszOut, LPDWORD pcchOut, DWORD dwFlags)
1520 DWORD ret, len, len2;
1522 TRACE("(in %s, out size %ld, flags %08lx) using W version\n",
1523 debugstr_a(pszIn), *pcchOut, dwFlags);
1525 in = (LPWSTR) HeapAlloc(GetProcessHeap(), 0,
1526 (2*INTERNET_MAX_URL_LENGTH) * sizeof(WCHAR));
1527 out = in + INTERNET_MAX_URL_LENGTH;
1529 MultiByteToWideChar(0, 0, pszIn, -1, in, INTERNET_MAX_URL_LENGTH);
1530 len = INTERNET_MAX_URL_LENGTH;
1532 ret = UrlApplySchemeW(in, out, &len, dwFlags);
1533 if ((ret != S_OK) && (ret != S_FALSE)) {
1534 HeapFree(GetProcessHeap(), 0, in);
1538 len2 = WideCharToMultiByte(0, 0, out, len+1, 0, 0, 0, 0);
1539 if (len2 > *pcchOut) {
1541 HeapFree(GetProcessHeap(), 0, in);
1544 WideCharToMultiByte(0, 0, out, len+1, pszOut, *pcchOut, 0, 0);
1546 HeapFree(GetProcessHeap(), 0, in);
1550 static HRESULT URL_GuessScheme(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut)
1555 DWORD value_len, data_len, dwType, i;
1556 WCHAR reg_path[MAX_PATH];
1557 WCHAR value[MAX_PATH], data[MAX_PATH];
1560 MultiByteToWideChar(0, 0,
1561 "Software\\Microsoft\\Windows\\CurrentVersion\\URL\\Prefixes",
1562 -1, reg_path, MAX_PATH);
1563 RegOpenKeyExW(HKEY_LOCAL_MACHINE, reg_path, 0, 1, &newkey);
1565 while(value_len = data_len = MAX_PATH,
1566 RegEnumValueW(newkey, index, value, &value_len,
1567 0, &dwType, (LPVOID)data, &data_len) == 0) {
1568 TRACE("guess %d %s is %s\n",
1569 index, debugstr_w(value), debugstr_w(data));
1572 for(i=0; i<value_len; i++) {
1575 /* remember that TRUE is not-equal */
1576 j = ChrCmpIW(Wxx, Wyy);
1579 if ((i == value_len) && !j) {
1580 if (strlenW(data) + strlenW(pszIn) + 1 > *pcchOut) {
1581 *pcchOut = strlenW(data) + strlenW(pszIn) + 1;
1582 RegCloseKey(newkey);
1585 strcpyW(pszOut, data);
1586 strcatW(pszOut, pszIn);
1587 *pcchOut = strlenW(pszOut);
1588 TRACE("matched and set to %s\n", debugstr_w(pszOut));
1589 RegCloseKey(newkey);
1594 RegCloseKey(newkey);
1598 static HRESULT URL_ApplyDefault(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut)
1601 DWORD data_len, dwType;
1602 WCHAR reg_path[MAX_PATH];
1603 WCHAR value[MAX_PATH], data[MAX_PATH];
1605 /* get and prepend default */
1606 MultiByteToWideChar(0, 0,
1607 "Software\\Microsoft\\Windows\\CurrentVersion\\URL\\DefaultPrefix",
1608 -1, reg_path, MAX_PATH);
1609 RegOpenKeyExW(HKEY_LOCAL_MACHINE, reg_path, 0, 1, &newkey);
1610 data_len = MAX_PATH;
1613 RegQueryValueExW(newkey, value, 0, &dwType, (LPBYTE)data, &data_len);
1614 RegCloseKey(newkey);
1615 if (strlenW(data) + strlenW(pszIn) + 1 > *pcchOut) {
1616 *pcchOut = strlenW(data) + strlenW(pszIn) + 1;
1619 strcpyW(pszOut, data);
1620 strcatW(pszOut, pszIn);
1621 *pcchOut = strlenW(pszOut);
1622 TRACE("used default %s\n", debugstr_w(pszOut));
1626 /*************************************************************************
1627 * UrlApplySchemeW [SHLWAPI.@]
1629 * See UrlApplySchemeA.
1631 HRESULT WINAPI UrlApplySchemeW(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut, DWORD dwFlags)
1633 UNKNOWN_SHLWAPI_2 in_scheme;
1637 TRACE("(in %s, out size %ld, flags %08lx)\n",
1638 debugstr_w(pszIn), *pcchOut, dwFlags);
1640 if (dwFlags & URL_APPLY_GUESSFILE) {
1641 FIXME("(%s %p %p(%ld) 0x%08lx): stub URL_APPLY_GUESSFILE not implemented\n",
1642 debugstr_w(pszIn), pszOut, pcchOut, *pcchOut, dwFlags);
1643 strcpyW(pszOut, pszIn);
1644 *pcchOut = strlenW(pszOut);
1648 in_scheme.size = 24;
1649 /* See if the base has a scheme */
1650 res1 = ParseURLW(pszIn, &in_scheme);
1652 /* no scheme in input, need to see if we need to guess */
1653 if (dwFlags & URL_APPLY_GUESSSCHEME) {
1654 if ((ret = URL_GuessScheme(pszIn, pszOut, pcchOut)) != -1)
1659 /* we have a scheme, see if valid (known scheme) */
1660 if (in_scheme.fcncde) {
1661 /* have valid scheme, so just copy and exit */
1662 if (strlenW(pszIn) + 1 > *pcchOut) {
1663 *pcchOut = strlenW(pszIn) + 1;
1666 strcpyW(pszOut, pszIn);
1667 *pcchOut = strlenW(pszOut);
1668 TRACE("valid scheme, returing copy\n");
1673 /* If we are here, then either invalid scheme,
1674 * or no scheme and can't/failed guess.
1676 if ( ( ((res1 == 0) && (dwFlags & URL_APPLY_FORCEAPPLY)) ||
1678 (dwFlags & URL_APPLY_DEFAULT)) {
1679 /* find and apply default scheme */
1680 return URL_ApplyDefault(pszIn, pszOut, pcchOut);
1683 /* just copy and give proper return code */
1684 if (strlenW(pszIn) + 1 > *pcchOut) {
1685 *pcchOut = strlenW(pszIn) + 1;
1688 strcpyW(pszOut, pszIn);
1689 *pcchOut = strlenW(pszOut);
1690 TRACE("returning copy, left alone\n");
1694 /*************************************************************************
1695 * UrlIsA [SHLWAPI.@]
1697 * Determine if a Url is of a certain class.
1700 * pszUrl [I] Url to check
1701 * Urlis [I] URLIS_ constant from "shlwapi.h"
1704 * TRUE if pszUrl belongs to the class type in Urlis.
1707 BOOL WINAPI UrlIsA(LPCSTR pszUrl, URLIS Urlis)
1709 UNKNOWN_SHLWAPI_1 base;
1716 res1 = ParseURLA(pszUrl, &base);
1717 if (res1) return FALSE; /* invalid scheme */
1718 if ((*base.ap2 == '/') && (*(base.ap2+1) == '/'))
1719 /* has scheme followed by 2 '/' */
1724 case URLIS_NOHISTORY:
1726 case URLIS_APPLIABLE:
1727 case URLIS_DIRECTORY:
1728 case URLIS_HASQUERY:
1730 FIXME("(%s %d): stub\n", debugstr_a(pszUrl), Urlis);
1735 /*************************************************************************
1736 * UrlIsW [SHLWAPI.@]
1740 BOOL WINAPI UrlIsW(LPCWSTR pszUrl, URLIS Urlis)
1742 UNKNOWN_SHLWAPI_2 base;
1749 res1 = ParseURLW(pszUrl, &base);
1750 if (res1) return FALSE; /* invalid scheme */
1751 if ((*base.ap2 == L'/') && (*(base.ap2+1) == L'/'))
1752 /* has scheme followed by 2 '/' */
1757 case URLIS_NOHISTORY:
1759 case URLIS_APPLIABLE:
1760 case URLIS_DIRECTORY:
1761 case URLIS_HASQUERY:
1763 FIXME("(%s %d): stub\n", debugstr_w(pszUrl), Urlis);
1768 /*************************************************************************
1769 * UrlIsNoHistoryA [SHLWAPI.@]
1771 * Determine if a Url should not be stored in the users history list.
1774 * pszUrl [I] Url to check
1777 * TRUE, if pszUrl should be excluded from the history list,
1780 BOOL WINAPI UrlIsNoHistoryA(LPCSTR pszUrl)
1782 return UrlIsA(pszUrl, URLIS_NOHISTORY);
1785 /*************************************************************************
1786 * UrlIsNoHistoryW [SHLWAPI.@]
1788 * See UrlIsNoHistoryA.
1790 BOOL WINAPI UrlIsNoHistoryW(LPCWSTR pszUrl)
1792 return UrlIsW(pszUrl, URLIS_NOHISTORY);
1795 /*************************************************************************
1796 * UrlIsOpaqueA [SHLWAPI.@]
1798 * Determine if a Url is opaque.
1801 * pszUrl [I] Url to check
1804 * TRUE if pszUrl is opaque,
1808 * An opaque Url is one that does not start with "<protocol>://".
1810 BOOL WINAPI UrlIsOpaqueA(LPCSTR pszUrl)
1812 return UrlIsA(pszUrl, URLIS_OPAQUE);
1815 /*************************************************************************
1816 * UrlIsOpaqueW [SHLWAPI.@]
1820 BOOL WINAPI UrlIsOpaqueW(LPCWSTR pszUrl)
1822 return UrlIsW(pszUrl, URLIS_OPAQUE);
1825 /*************************************************************************
1826 * Scans for characters of type "type" and when not matching found,
1827 * returns pointer to it and length in size.
1829 * Characters tested based on RFC 1738
1831 static LPCWSTR URL_ScanID(LPCWSTR start, LPDWORD size, WINE_URL_SCAN_TYPE type)
1833 static DWORD alwayszero = 0;
1842 if ( (islowerW(*start) && isalphaW(*start)) ||
1857 if ( isalphaW(*start) ||
1859 /* user/password only characters */
1864 /* *extra* characters */
1867 (*start == L'\'') ||
1871 /* *safe* characters */
1879 } else if (*start == L'%') {
1880 if (isxdigitW(*(start+1)) &&
1881 isxdigitW(*(start+2))) {
1893 if (isdigitW(*start)) {
1904 if (isalnumW(*start) ||
1906 (*start == L'.') ) {
1915 FIXME("unknown type %d\n", type);
1916 return (LPWSTR)&alwayszero;
1918 /* TRACE("scanned %ld characters next char %p<%c>\n",
1919 *size, start, *start); */
1923 /*************************************************************************
1924 * Attempt to parse URL into pieces.
1926 static LONG URL_ParseUrl(LPCWSTR pszUrl, WINE_PARSE_URL *pl)
1930 memset(pl, 0, sizeof(WINE_PARSE_URL));
1931 pl->pScheme = pszUrl;
1932 work = URL_ScanID(pl->pScheme, &pl->szScheme, SCHEME);
1933 if (!*work || (*work != L':')) goto ErrorExit;
1935 if ((*work != L'/') || (*(work+1) != L'/')) goto ErrorExit;
1936 pl->pUserName = work + 2;
1937 work = URL_ScanID(pl->pUserName, &pl->szUserName, USERPASS);
1938 if (*work == L':' ) {
1939 /* parse password */
1941 pl->pPassword = work;
1942 work = URL_ScanID(pl->pPassword, &pl->szPassword, USERPASS);
1943 if (*work != L'@') {
1944 /* what we just parsed must be the hostname and port
1945 * so reset pointers and clear then let it parse */
1946 pl->szUserName = pl->szPassword = 0;
1947 work = pl->pUserName - 1;
1948 pl->pUserName = pl->pPassword = 0;
1950 } else if (*work == L'@') {
1954 } else if (!*work || (*work == L'/') || (*work == L'.')) {
1955 /* what was parsed was hostname, so reset pointers and let it parse */
1956 pl->szUserName = pl->szPassword = 0;
1957 work = pl->pUserName - 1;
1958 pl->pUserName = pl->pPassword = 0;
1959 } else goto ErrorExit;
1961 /* now start parsing hostname or hostnumber */
1963 pl->pHostName = work;
1964 work = URL_ScanID(pl->pHostName, &pl->szHostName, HOST);
1965 if (*work == L':') {
1969 work = URL_ScanID(pl->pPort, &pl->szPort, PORT);
1971 if (*work == L'/') {
1972 /* see if query string */
1973 pl->pQuery = strchrW(work, L'?');
1974 if (pl->pQuery) pl->szQuery = strlenW(pl->pQuery);
1976 TRACE("parse successful: scheme=%p(%ld), user=%p(%ld), pass=%p(%ld), host=%p(%ld), port=%p(%ld), query=%p(%ld)\n",
1977 pl->pScheme, pl->szScheme,
1978 pl->pUserName, pl->szUserName,
1979 pl->pPassword, pl->szPassword,
1980 pl->pHostName, pl->szHostName,
1981 pl->pPort, pl->szPort,
1982 pl->pQuery, pl->szQuery);
1985 FIXME("failed to parse %s\n", debugstr_w(pszUrl));
1986 return E_INVALIDARG;
1989 /*************************************************************************
1990 * UrlGetPartA [SHLWAPI.@]
1992 * Retrieve part of a Url.
1995 * pszIn [I] Url to parse
1996 * pszOut [O] Destination for part of pszIn requested
1997 * pcchOut [I/O] Length of pszOut/destination for length of pszOut
1998 * dwPart [I] URL_PART_ enum from "shlwapi.h"
1999 * dwFlags [I] URL_ flags from "shlwapi.h"
2002 * Success: S_OK. pszOut contains the part requested, pcchOut contains its length.
2003 * Failure: An HRESULT error code describing the error.
2005 HRESULT WINAPI UrlGetPartA(LPCSTR pszIn, LPSTR pszOut, LPDWORD pcchOut,
2006 DWORD dwPart, DWORD dwFlags)
2009 DWORD ret, len, len2;
2011 in = (LPWSTR) HeapAlloc(GetProcessHeap(), 0,
2012 (2*INTERNET_MAX_URL_LENGTH) * sizeof(WCHAR));
2013 out = in + INTERNET_MAX_URL_LENGTH;
2015 MultiByteToWideChar(0, 0, pszIn, -1, in, INTERNET_MAX_URL_LENGTH);
2017 len = INTERNET_MAX_URL_LENGTH;
2018 ret = UrlGetPartW(in, out, &len, dwPart, dwFlags);
2021 HeapFree(GetProcessHeap(), 0, in);
2025 len2 = WideCharToMultiByte(0, 0, out, len, 0, 0, 0, 0);
2026 if (len2 > *pcchOut) {
2028 HeapFree(GetProcessHeap(), 0, in);
2031 WideCharToMultiByte(0, 0, out, len+1, pszOut, *pcchOut, 0, 0);
2033 HeapFree(GetProcessHeap(), 0, in);
2037 /*************************************************************************
2038 * UrlGetPartW [SHLWAPI.@]
2042 HRESULT WINAPI UrlGetPartW(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut,
2043 DWORD dwPart, DWORD dwFlags)
2047 DWORD size, schsize;
2048 LPCWSTR addr, schaddr;
2051 TRACE("(%s %p %p(%ld) %08lx %08lx)\n",
2052 debugstr_w(pszIn), pszOut, pcchOut, *pcchOut, dwPart, dwFlags);
2054 ret = URL_ParseUrl(pszIn, &pl);
2056 schaddr = pl.pScheme;
2057 schsize = pl.szScheme;
2060 case URL_PART_SCHEME:
2061 if (!pl.szScheme) return E_INVALIDARG;
2066 case URL_PART_HOSTNAME:
2067 if (!pl.szHostName) return E_INVALIDARG;
2068 addr = pl.pHostName;
2069 size = pl.szHostName;
2072 case URL_PART_USERNAME:
2073 if (!pl.szUserName) return E_INVALIDARG;
2074 addr = pl.pUserName;
2075 size = pl.szUserName;
2078 case URL_PART_PASSWORD:
2079 if (!pl.szPassword) return E_INVALIDARG;
2080 addr = pl.pPassword;
2081 size = pl.szPassword;
2085 if (!pl.szPort) return E_INVALIDARG;
2090 case URL_PART_QUERY:
2091 if (!pl.szQuery) return E_INVALIDARG;
2097 return E_INVALIDARG;
2100 if (dwFlags == URL_PARTFLAG_KEEPSCHEME) {
2101 if (*pcchOut < size + schsize + 2) {
2102 *pcchOut = size + schsize + 2;
2105 strncpyW(pszOut, schaddr, schsize);
2106 work = pszOut + schsize;
2108 strncpyW(work+1, addr, size);
2109 *pcchOut = size + schsize + 1;
2114 if (*pcchOut < size + 1) {*pcchOut = size+1; return E_POINTER;}
2115 strncpyW(pszOut, addr, size);
2117 work = pszOut + size;
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)
2139 UNKNOWN_SHLWAPI_1 base;
2142 if (!lpstrPath || !*lpstrPath) return FALSE;
2145 base.size = sizeof(base);
2146 res1 = ParseURLA(lpstrPath, &base);
2147 return (base.fcncde > 0);
2150 /*************************************************************************
2151 * PathIsURLW [SHLWAPI.@]
2155 BOOL WINAPI PathIsURLW(LPCWSTR lpstrPath)
2157 UNKNOWN_SHLWAPI_2 base;
2160 if (!lpstrPath || !*lpstrPath) return FALSE;
2163 base.size = sizeof(base);
2164 res1 = ParseURLW(lpstrPath, &base);
2165 return (base.fcncde > 0);
2168 /*************************************************************************
2169 * UrlCreateFromPathA [SHLWAPI.@]
2171 * Create a Url from a file path.
2174 * pszPath [I] Path to convert
2175 * pszUrl [O] Destination for the converted Url
2176 * pcchUrl [I/O] Length of pszUrl
2177 * dwReserved [I] Reserved, must be 0
2180 * Success: S_OK. pszUrl contains the converted path.
2181 * Failure: An HRESULT error code.
2183 HRESULT WINAPI UrlCreateFromPathA(LPCSTR pszPath, LPSTR pszUrl, LPDWORD pcchUrl, DWORD dwReserved)
2185 DWORD nCharBeforeColon = 0;
2187 DWORD dwChRequired = 0;
2188 LPSTR pszNewUrl = NULL;
2189 LPCSTR pszConstPointer = NULL;
2190 LPSTR pszPointer = NULL;
2194 TRACE("(%s, %p, %p, 0x%08lx)\n", debugstr_a(pszPath), pszUrl, pcchUrl, dwReserved);
2196 /* Validate arguments */
2197 if (dwReserved != 0)
2199 FIXME("dwReserved should be 0: 0x%08lx\n", dwReserved);
2200 return E_INVALIDARG;
2202 if (!pszUrl || !pcchUrl || !pszUrl)
2204 ERR("Invalid argument\n");
2205 return E_INVALIDARG;
2208 for (pszConstPointer = pszPath; *pszConstPointer; pszConstPointer++)
2210 if (isalpha(*pszConstPointer) || isdigit(*pszConstPointer) ||
2211 *pszConstPointer == '.' || *pszConstPointer == '-')
2215 if (*pszConstPointer == ':') /* then already in URL format, so copy */
2217 dwChRequired = lstrlenA(pszPath);
2218 if (dwChRequired > *pcchUrl)
2220 *pcchUrl = dwChRequired;
2225 *pcchUrl = dwChRequired;
2226 StrCpyA(pszUrl, pszPath);
2230 /* then must need converting to file: format */
2232 /* Strip off leading slashes */
2233 while (*pszPath == '\\' || *pszPath == '/')
2239 dwChRequired = *pcchUrl; /* UrlEscape will fill this in with the correct amount */
2240 TRACE("pszUrl: %s\n", debugstr_a(pszPath));
2241 pszNewUrl = HeapAlloc(GetProcessHeap(), 0, dwChRequired + 1);
2242 ret = UrlEscapeA(pszPath, pszNewUrl, &dwChRequired, URL_ESCAPE_PERCENT);
2243 TRACE("ret: 0x%08lx, pszUrl: %s\n", ret, debugstr_a(pszNewUrl));
2244 TRACE("%ld\n", dwChRequired);
2245 if (ret != E_POINTER && FAILED(ret))
2247 dwChRequired += 5; /* "file:" */
2248 if ((lstrlenA(pszUrl) > 1) && isalpha(pszUrl[0]) && (pszUrl[1] == ':'))
2250 dwChRequired += 3; /* "///" */
2256 case 0: /* no slashes */
2258 case 2: /* two slashes */
2265 default: /* three slashes */
2270 if (dwChRequired > *pcchUrl)
2272 *pcchUrl = dwChRequired; /* Return number of chars required (not including termination) */
2273 StrCpyA(pszUrl, "file:");
2274 pszPointer = pszUrl + lstrlenA(pszUrl);
2275 for (i=0; i < nSlashes; i++)
2280 StrCpyA(pszPointer, pszNewUrl);
2281 TRACE("<- %s\n", debugstr_a(pszUrl));
2285 /*************************************************************************
2286 * UrlCreateFromPathW [SHLWAPI.@]
2288 * See UrlCreateFromPathA.
2290 HRESULT WINAPI UrlCreateFromPathW(LPCWSTR pszPath, LPWSTR pszUrl, LPDWORD pcchUrl, DWORD dwReserved)
2292 DWORD nCharBeforeColon = 0;
2294 DWORD dwChRequired = 0;
2295 LPWSTR pszNewUrl = NULL;
2296 LPCWSTR pszConstPointer = NULL;
2297 LPWSTR pszPointer = NULL;
2301 TRACE("(%s, %p, %p, 0x%08lx)\n", debugstr_w(pszPath), pszUrl, pcchUrl, dwReserved);
2303 /* Validate arguments */
2304 if (dwReserved != 0)
2305 return E_INVALIDARG;
2306 if (!pszUrl || !pcchUrl || !pszUrl)
2307 return E_INVALIDARG;
2309 for (pszConstPointer = pszPath; *pszConstPointer; pszConstPointer++)
2311 if (isalphaW(*pszConstPointer) || isdigitW(*pszConstPointer) ||
2312 *pszConstPointer == '.' || *pszConstPointer == '-')
2316 if (*pszConstPointer == ':') /* then already in URL format, so copy */
2318 dwChRequired = lstrlenW(pszPath);
2319 *pcchUrl = dwChRequired;
2320 if (dwChRequired > *pcchUrl)
2324 StrCpyW(pszUrl, pszPath);
2328 /* then must need converting to file: format */
2330 /* Strip off leading slashes */
2331 while (*pszPath == '\\' || *pszPath == '/')
2337 dwChRequired = *pcchUrl; /* UrlEscape will fill this in with the correct amount */
2338 ret = UrlEscapeW(pszPath, pszUrl, &dwChRequired, URL_ESCAPE_PERCENT);
2339 if (ret != E_POINTER && FAILED(ret))
2341 dwChRequired += 5; /* "file:" */
2342 if ((lstrlenW(pszUrl) > 1) && isalphaW(pszUrl[0]) && (pszUrl[1] == ':'))
2344 dwChRequired += 3; /* "///" */
2350 case 0: /* no slashes */
2352 case 2: /* two slashes */
2359 default: /* three slashes */
2364 *pcchUrl = dwChRequired; /* Return number of chars required (not including termination) */
2365 if (dwChRequired > *pcchUrl)
2367 pszNewUrl = HeapAlloc(GetProcessHeap(), 0, (dwChRequired + 1) * sizeof(WCHAR));
2368 StrCpyW(pszNewUrl, fileW);
2369 pszPointer = pszNewUrl + 4;
2372 for (i=0; i < nSlashes; i++)
2377 StrCpyW(pszPointer, pszPath);
2378 StrCpyW(pszUrl, pszNewUrl);
2382 /*************************************************************************
2383 * SHAutoComplete [SHLWAPI.@]
2385 * Enable auto-completion for an edit control.
2388 * hwndEdit [I] Handle of control to enable auto-completion for
2389 * dwFlags [I] SHACF_ flags from "shlwapi.h"
2392 * Success: S_OK. Auto-completion is enabled for the control.
2393 * Failure: An HRESULT error code indicating the error.
2395 HRESULT WINAPI SHAutoComplete(HWND hwndEdit, DWORD dwFlags)
2397 FIXME("SHAutoComplete stub\n");
2401 /*************************************************************************
2402 * MLBuildResURLA [SHLWAPI.405]
2404 * Create a Url pointing to a resource in a module.
2407 * lpszLibName [I] Name of the module containing the resource
2408 * hMod [I] Callers module handle
2409 * dwFlags [I] Undocumented flags for loading the module
2410 * lpszRes [I] Resource name
2411 * lpszDest [O] Destination for resulting Url
2412 * dwDestLen [I] Length of lpszDest
2415 * Success: S_OK. lpszDest constains the resource Url.
2416 * Failure: E_INVALIDARG, if any argument is invalid, or
2417 * E_FAIL if dwDestLen is too small.
2419 HRESULT WINAPI MLBuildResURLA(LPCSTR lpszLibName, HMODULE hMod, DWORD dwFlags,
2420 LPCSTR lpszRes, LPSTR lpszDest, DWORD dwDestLen)
2422 WCHAR szLibName[MAX_PATH], szRes[MAX_PATH], szDest[MAX_PATH];
2426 MultiByteToWideChar(CP_ACP, 0, lpszLibName, -1, szLibName, sizeof(szLibName)/sizeof(WCHAR));
2429 MultiByteToWideChar(CP_ACP, 0, lpszRes, -1, szRes, sizeof(szRes)/sizeof(WCHAR));
2431 if (dwDestLen > sizeof(szLibName)/sizeof(WCHAR))
2432 dwDestLen = sizeof(szLibName)/sizeof(WCHAR);
2434 hRet = MLBuildResURLW(lpszLibName ? szLibName : NULL, hMod, dwFlags,
2435 lpszRes ? szRes : NULL, lpszDest ? szDest : NULL, dwDestLen);
2436 if (SUCCEEDED(hRet) && lpszDest)
2437 WideCharToMultiByte(CP_ACP, 0, szDest, -1, lpszDest, dwDestLen, 0, 0);
2442 /*************************************************************************
2443 * MLBuildResURLA [SHLWAPI.406]
2445 * See MLBuildResURLA.
2447 HRESULT WINAPI MLBuildResURLW(LPCWSTR lpszLibName, HMODULE hMod, DWORD dwFlags,
2448 LPCWSTR lpszRes, LPWSTR lpszDest, DWORD dwDestLen)
2450 static const WCHAR szRes[] = { 'r','e','s',':','/','/','\0' };
2451 #define szResLen ((sizeof(szRes) - sizeof(WCHAR))/sizeof(WCHAR))
2452 HRESULT hRet = E_FAIL;
2454 TRACE("(%s,%p,0x%08lx,%s,%p,%ld)\n", debugstr_w(lpszLibName), hMod, dwFlags,
2455 debugstr_w(lpszRes), lpszDest, dwDestLen);
2457 if (!lpszLibName || !hMod || hMod == INVALID_HANDLE_VALUE || !lpszRes ||
2458 !lpszDest || (dwFlags && dwFlags != 2))
2459 return E_INVALIDARG;
2461 if (dwDestLen >= szResLen + 1)
2463 dwDestLen -= (szResLen + 1);
2464 memcpy(lpszDest, szRes, sizeof(szRes));
2466 hMod = MLLoadLibraryW(lpszLibName, hMod, dwFlags);
2470 WCHAR szBuff[MAX_PATH];
2472 if (GetModuleFileNameW(hMod, szBuff, sizeof(szBuff)/sizeof(WCHAR)))
2474 DWORD dwPathLen = strlenW(szBuff) + 1;
2476 if (dwDestLen >= dwPathLen)
2480 dwDestLen -= dwPathLen;
2481 memcpy(lpszDest + szResLen, szBuff, dwPathLen * sizeof(WCHAR));
2483 dwResLen = strlenW(lpszRes) + 1;
2484 if (dwDestLen >= dwResLen + 1)
2486 lpszDest[szResLen + dwPathLen + dwResLen] = '/';
2487 memcpy(lpszDest + szResLen + dwPathLen, lpszRes, dwResLen * sizeof(WCHAR));
2492 MLFreeLibrary(hMod);