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));
585 if(dwFlags & URL_UNESCAPE)
586 UrlUnescapeW(lpszUrlCpy, NULL, NULL, URL_UNESCAPE_INPLACE);
588 if((EscapeFlags = dwFlags & (URL_ESCAPE_UNSAFE |
589 URL_ESCAPE_SPACES_ONLY |
591 URL_DONT_ESCAPE_EXTRA_INFO |
592 URL_ESCAPE_SEGMENT_ONLY ))) {
593 EscapeFlags &= ~URL_ESCAPE_UNSAFE;
594 hr = UrlEscapeW(lpszUrlCpy, pszCanonicalized, pcchCanonicalized,
596 } else { /* No escaping needed, just copy the string */
597 nLen = lstrlenW(lpszUrlCpy);
598 if(nLen < *pcchCanonicalized)
599 memcpy(pszCanonicalized, lpszUrlCpy, (nLen + 1)*sizeof(WCHAR));
604 *pcchCanonicalized = nLen;
607 HeapFree(GetProcessHeap(), 0, lpszUrlCpy);
610 TRACE("result %s\n", debugstr_w(pszCanonicalized));
615 /*************************************************************************
616 * UrlCombineA [SHLWAPI.@]
621 * pszBase [I] Base Url
622 * pszRelative [I] Url to combine with pszBase
623 * pszCombined [O] Destination for combined Url
624 * pcchCombined [O] Destination for length of pszCombined
625 * dwFlags [I] URL_ flags from "shlwapi.h"
628 * Success: S_OK. pszCombined contains the combined Url, pcchCombined
629 * contains its length.
630 * Failure: An HRESULT error code indicating the error.
632 HRESULT WINAPI UrlCombineA(LPCSTR pszBase, LPCSTR pszRelative,
633 LPSTR pszCombined, LPDWORD pcchCombined,
636 LPWSTR base, relative, combined;
637 DWORD ret, len, len2;
639 TRACE("(base %s, Relative %s, Combine size %ld, flags %08lx) using W version\n",
640 debugstr_a(pszBase),debugstr_a(pszRelative),
641 pcchCombined?*pcchCombined:0,dwFlags);
643 if(!pszBase || !pszRelative || !pszCombined || !pcchCombined)
646 base = (LPWSTR) HeapAlloc(GetProcessHeap(), 0,
647 (3*INTERNET_MAX_URL_LENGTH) * sizeof(WCHAR));
648 relative = base + INTERNET_MAX_URL_LENGTH;
649 combined = relative + INTERNET_MAX_URL_LENGTH;
651 MultiByteToWideChar(0, 0, pszBase, -1, base, INTERNET_MAX_URL_LENGTH);
652 MultiByteToWideChar(0, 0, pszRelative, -1, relative, INTERNET_MAX_URL_LENGTH);
653 len = INTERNET_MAX_URL_LENGTH;
655 ret = UrlCombineW(base, relative, combined, &len, dwFlags);
657 HeapFree(GetProcessHeap(), 0, base);
661 len2 = WideCharToMultiByte(0, 0, combined, len, 0, 0, 0, 0);
662 if (len2 > *pcchCombined) {
663 *pcchCombined = len2;
664 HeapFree(GetProcessHeap(), 0, base);
667 WideCharToMultiByte(0, 0, combined, len+1, pszCombined, *pcchCombined,
669 *pcchCombined = len2;
670 HeapFree(GetProcessHeap(), 0, base);
674 /*************************************************************************
675 * UrlCombineW [SHLWAPI.@]
679 HRESULT WINAPI UrlCombineW(LPCWSTR pszBase, LPCWSTR pszRelative,
680 LPWSTR pszCombined, LPDWORD pcchCombined,
683 UNKNOWN_SHLWAPI_2 base, relative;
684 DWORD myflags, sizeloc = 0;
685 DWORD len, res1, res2, process_case = 0;
686 LPWSTR work, preliminary, mbase, mrelative;
687 WCHAR myfilestr[] = {'f','i','l','e',':','/','/','/','\0'};
688 WCHAR single_slash[] = {'/','\0'};
691 TRACE("(base %s, Relative %s, Combine size %ld, flags %08lx)\n",
692 debugstr_w(pszBase),debugstr_w(pszRelative),
693 pcchCombined?*pcchCombined:0,dwFlags);
695 if(!pszBase || !pszRelative || !pszCombined || !pcchCombined)
701 /* Get space for duplicates of the input and the output */
702 preliminary = HeapAlloc(GetProcessHeap(), 0, (3*INTERNET_MAX_URL_LENGTH) *
704 mbase = preliminary + INTERNET_MAX_URL_LENGTH;
705 mrelative = mbase + INTERNET_MAX_URL_LENGTH;
706 *preliminary = L'\0';
708 /* Canonicalize the base input prior to looking for the scheme */
709 myflags = dwFlags & (URL_DONT_SIMPLIFY | URL_UNESCAPE);
710 len = INTERNET_MAX_URL_LENGTH;
711 ret = UrlCanonicalizeW(pszBase, mbase, &len, myflags);
713 /* Canonicalize the relative input prior to looking for the scheme */
714 len = INTERNET_MAX_URL_LENGTH;
715 ret = UrlCanonicalizeW(pszRelative, mrelative, &len, myflags);
717 /* See if the base has a scheme */
718 res1 = ParseURLW(mbase, &base);
720 /* if pszBase has no scheme, then return pszRelative */
721 TRACE("no scheme detected in Base\n");
726 /* get size of location field (if it exists) */
727 work = (LPWSTR)base.ap2;
729 if (*work++ == L'/') {
730 if (*work++ == L'/') {
731 /* At this point have start of location and
732 * it ends at next '/' or end of string.
734 while(*work && (*work != L'/')) work++;
735 sizeloc = (DWORD)(work - base.ap2);
739 /* Change .sizep2 to not have the last leaf in it,
740 * Note: we need to start after the location (if it exists)
742 work = strrchrW((base.ap2+sizeloc), L'/');
744 len = (DWORD)(work - base.ap2 + 1);
749 * .ap2 points to location (starting with '//')
750 * .sizep2 length of location (above) and rest less the last
752 * sizeloc length of location (above) up to but not including
756 res2 = ParseURLW(mrelative, &relative);
758 /* no scheme in pszRelative */
759 TRACE("no scheme detected in Relative\n");
760 relative.ap2 = mrelative; /* case 3,4,5 depends on this */
761 relative.sizep2 = strlenW(mrelative);
762 if (*pszRelative == L':') {
763 /* case that is either left alone or uses pszBase */
764 if (dwFlags & URL_PLUGGABLE_PROTOCOL) {
771 if (isalnum(*mrelative) && (*(mrelative + 1) == L':')) {
772 /* case that becomes "file:///" */
773 strcpyW(preliminary, myfilestr);
777 if ((*mrelative == L'/') && (*(mrelative+1) == L'/')) {
778 /* pszRelative has location and rest */
782 if (*mrelative == L'/') {
783 /* case where pszRelative is root to location */
787 process_case = (*base.ap2 == L'/') ? 5 : 3;
791 /* handle cases where pszRelative has scheme */
792 if ((base.sizep1 == relative.sizep1) &&
793 (strncmpW(base.ap1, relative.ap1, base.sizep1) == 0)) {
795 /* since the schemes are the same */
796 if ((*relative.ap2 == L'/') && (*(relative.ap2+1) == L'/')) {
797 /* case where pszRelative replaces location and following */
801 if (*relative.ap2 == L'/') {
802 /* case where pszRelative is root to location */
806 /* case where scheme is followed by document path */
810 if ((*relative.ap2 == L'/') && (*(relative.ap2+1) == L'/')) {
811 /* case where pszRelative replaces scheme, location,
812 * and following and handles PLUGGABLE
819 } while(FALSE); /* a litte trick to allow easy exit from nested if's */
823 switch (process_case) {
826 * Return pszRelative appended to what ever is in pszCombined,
827 * (which may the string "file:///"
829 len = strlenW(mrelative) + strlenW(preliminary);
830 if (len+1 > *pcchCombined) {
835 strcatW(preliminary, mrelative);
839 * Same as case 1, but if URL_PLUGGABLE_PROTOCOL was specified
840 * and pszRelative starts with "//", then append a "/"
842 len = strlenW(mrelative) + 1;
843 if (len+1 > *pcchCombined) {
848 strcpyW(preliminary, mrelative);
849 if (!(dwFlags & URL_PLUGGABLE_PROTOCOL) &&
850 URL_JustLocation(relative.ap2))
851 strcatW(preliminary, single_slash);
855 * Return the pszBase scheme with pszRelative. Basicly
856 * keeps the scheme and replaces the domain and following.
858 len = base.sizep1 + 1 + relative.sizep2 + 1;
859 if (len+1 > *pcchCombined) {
864 strncpyW(preliminary, base.ap1, base.sizep1 + 1);
865 work = preliminary + base.sizep1 + 1;
866 strcpyW(work, relative.ap2);
867 if (!(dwFlags & URL_PLUGGABLE_PROTOCOL) &&
868 URL_JustLocation(relative.ap2))
869 strcatW(work, single_slash);
873 * Return the pszBase scheme and location but everything
874 * after the location is pszRelative. (Replace document
877 len = base.sizep1 + 1 + sizeloc + relative.sizep2 + 1;
878 if (len+1 > *pcchCombined) {
883 strncpyW(preliminary, base.ap1, base.sizep1+1+sizeloc);
884 work = preliminary + base.sizep1 + 1 + sizeloc;
885 if (dwFlags & URL_PLUGGABLE_PROTOCOL)
887 strcpyW(work, relative.ap2);
891 * Return the pszBase without its document (if any) and
892 * append pszRelative after its scheme.
894 len = base.sizep1 + 1 + base.sizep2 + relative.sizep2;
895 if (len+1 > *pcchCombined) {
900 strncpyW(preliminary, base.ap1, base.sizep1+1+base.sizep2);
901 work = preliminary + base.sizep1+1+base.sizep2 - 1;
904 strcpyW(work, relative.ap2);
908 FIXME("How did we get here????? process_case=%ld\n", process_case);
914 * Now that the combining is done, process the escape options if
915 * necessary, otherwise just copy the string.
917 myflags = dwFlags & (URL_ESCAPE_PERCENT |
918 URL_ESCAPE_SPACES_ONLY |
919 URL_DONT_ESCAPE_EXTRA_INFO |
920 URL_ESCAPE_SEGMENT_ONLY);
922 ret = UrlEscapeW(preliminary, pszCombined,
923 pcchCombined, myflags);
925 len = (strlenW(preliminary) + 1) * sizeof(WCHAR);
926 memcpy(pszCombined, preliminary, len);
927 *pcchCombined = strlenW(preliminary);
929 TRACE("return-%ld len=%ld, %s\n",
930 process_case, *pcchCombined, debugstr_w(pszCombined));
932 HeapFree(GetProcessHeap(), 0, preliminary);
936 /*************************************************************************
937 * UrlEscapeA [SHLWAPI.@]
939 * Converts unsafe characters in a Url into escape sequences.
942 * pszUrl [I] Url to modify
943 * pszEscaped [O] Destination for modified Url
944 * pcchEscaped [I/O] Length of pszUrl, destination for length of pszEscaped
945 * dwFlags [I] URL_ flags from "shlwapi.h"
948 * Success: S_OK. pszEscaped contains the escaped Url, pcchEscaped
949 * contains its length.
950 * Failure: E_POINTER, if pszEscaped is not large enough. In this case
951 * pcchEscaped is set to the required length.
953 * Converts unsafe characters into their escape sequences.
956 * - By default this function stops converting at the first '?' or
957 * '#' character (MSDN does not document this).
958 * - If dwFlags contains URL_ESCAPE_SPACES_ONLY then only spaces are
959 * converted, but the conversion continues past a '?' or '#'.
960 * - Note that this function did not work well (or at all) in shlwapi version 4.
963 * Only the following flags are implemented:
964 *| URL_ESCAPE_SPACES_ONLY
965 *| URL_DONT_ESCAPE_EXTRA_INFO
966 *| URL_ESCAPE_SEGMENT_ONLY
967 *| URL_ESCAPE_PERCENT
969 HRESULT WINAPI UrlEscapeA(
976 DWORD needed = 0, ret;
977 BOOL stop_escaping = FALSE;
978 char next[3], *dst = pszEscaped;
981 TRACE("(%s %p %lx 0x%08lx)\n", debugstr_a(pszUrl), pszEscaped,
982 pcchEscaped?*pcchEscaped:0, dwFlags);
984 if(!pszUrl || !pszEscaped || !pcchEscaped)
987 if(dwFlags & ~(URL_ESCAPE_SPACES_ONLY |
988 URL_ESCAPE_SEGMENT_ONLY |
989 URL_DONT_ESCAPE_EXTRA_INFO |
991 FIXME("Unimplemented flags: %08lx\n", dwFlags);
994 if (dwFlags & URL_ESCAPE_SPACES_ONLY)
995 /* if SPACES_ONLY specified, reset the other controls */
996 dwFlags &= ~(URL_DONT_ESCAPE_EXTRA_INFO |
998 URL_ESCAPE_SEGMENT_ONLY);
1001 /* if SPACES_ONLY *not* specified then assume DONT_ESCAPE_EXTRA_INFO */
1002 dwFlags |= URL_DONT_ESCAPE_EXTRA_INFO;
1004 for(src = pszUrl; *src; src++) {
1005 if(!(dwFlags & URL_ESCAPE_SEGMENT_ONLY) &&
1006 (dwFlags & URL_DONT_ESCAPE_EXTRA_INFO) &&
1007 (*src == '#' || *src == '?'))
1008 stop_escaping = TRUE;
1010 if(URL_NeedEscapeA(*src, dwFlags) && stop_escaping == FALSE) {
1011 /* TRACE("escaping %c\n", *src); */
1013 next[1] = hexDigits[(*src >> 4) & 0xf];
1014 next[2] = hexDigits[*src & 0xf];
1017 /* TRACE("passing %c\n", *src); */
1022 if(needed + len <= *pcchEscaped) {
1023 memcpy(dst, next, len);
1029 if(needed < *pcchEscaped) {
1033 needed++; /* add one for the '\0' */
1036 *pcchEscaped = needed;
1040 /*************************************************************************
1041 * UrlEscapeW [SHLWAPI.@]
1045 HRESULT WINAPI UrlEscapeW(
1048 LPDWORD pcchEscaped,
1052 DWORD needed = 0, ret;
1053 BOOL stop_escaping = FALSE;
1054 WCHAR next[5], *dst = pszEscaped;
1057 TRACE("(%s %p %p 0x%08lx)\n", debugstr_w(pszUrl), pszEscaped,
1058 pcchEscaped, dwFlags);
1060 if(!pszUrl || !pszEscaped || !pcchEscaped)
1061 return E_INVALIDARG;
1063 if(dwFlags & ~(URL_ESCAPE_SPACES_ONLY |
1064 URL_ESCAPE_SEGMENT_ONLY |
1065 URL_DONT_ESCAPE_EXTRA_INFO |
1066 URL_ESCAPE_PERCENT))
1067 FIXME("Unimplemented flags: %08lx\n", dwFlags);
1070 if (dwFlags & URL_ESCAPE_SPACES_ONLY)
1071 /* if SPACES_ONLY specified, reset the other controls */
1072 dwFlags &= ~(URL_DONT_ESCAPE_EXTRA_INFO |
1073 URL_ESCAPE_PERCENT |
1074 URL_ESCAPE_SEGMENT_ONLY);
1077 /* if SPACES_ONLY *not* specified the assume DONT_ESCAPE_EXTRA_INFO */
1078 dwFlags |= URL_DONT_ESCAPE_EXTRA_INFO;
1080 for(src = pszUrl; *src; src++) {
1082 * if(!(dwFlags & URL_ESCAPE_SPACES_ONLY) &&
1083 * (*src == L'#' || *src == L'?'))
1084 * stop_escaping = TRUE;
1086 if(!(dwFlags & URL_ESCAPE_SEGMENT_ONLY) &&
1087 (dwFlags & URL_DONT_ESCAPE_EXTRA_INFO) &&
1088 (*src == L'#' || *src == L'?'))
1089 stop_escaping = TRUE;
1091 if(URL_NeedEscapeW(*src, dwFlags) && stop_escaping == FALSE) {
1092 /* TRACE("escaping %c\n", *src); */
1095 * I would have assumed that the W form would escape
1096 * the character with 4 hex digits (or even 8),
1097 * however, experiments show that native shlwapi escapes
1098 * with only 2 hex digits.
1099 * next[1] = hexDigits[(*src >> 12) & 0xf];
1100 * next[2] = hexDigits[(*src >> 8) & 0xf];
1101 * next[3] = hexDigits[(*src >> 4) & 0xf];
1102 * next[4] = hexDigits[*src & 0xf];
1105 next[1] = hexDigits[(*src >> 4) & 0xf];
1106 next[2] = hexDigits[*src & 0xf];
1109 /* TRACE("passing %c\n", *src); */
1114 if(needed + len <= *pcchEscaped) {
1115 memcpy(dst, next, len*sizeof(WCHAR));
1121 if(needed < *pcchEscaped) {
1125 needed++; /* add one for the '\0' */
1128 *pcchEscaped = needed;
1133 /*************************************************************************
1134 * UrlUnescapeA [SHLWAPI.@]
1136 * Converts Url escape sequences back to ordinary characters.
1139 * pszUrl [I/O] Url to convert
1140 * pszUnescaped [O] Destination for converted Url
1141 * pcchUnescaped [I/O] Size of output string
1142 * dwFlags [I] URL_ESCAPE_ Flags from "shlwapi.h"
1145 * Success: S_OK. The converted value is in pszUnescaped, or in pszUrl if
1146 * dwFlags includes URL_ESCAPE_INPLACE.
1147 * Failure: E_POINTER if the converted Url is bigger than pcchUnescaped. In
1148 * this case pcchUnescaped is set to the size required.
1150 * If dwFlags includes URL_DONT_ESCAPE_EXTRA_INFO, the conversion stops at
1151 * the first occurrence of either a '?' or '#' character.
1153 HRESULT WINAPI UrlUnescapeA(
1156 LPDWORD pcchUnescaped,
1163 BOOL stop_unescaping = FALSE;
1165 TRACE("(%s, %p, %p, 0x%08lx)\n", debugstr_a(pszUrl), pszUnescaped,
1166 pcchUnescaped, dwFlags);
1168 if(!pszUrl || !pszUnescaped || !pcchUnescaped)
1169 return E_INVALIDARG;
1171 if(dwFlags & URL_UNESCAPE_INPLACE)
1176 for(src = pszUrl, needed = 0; *src; src++, needed++) {
1177 if(dwFlags & URL_DONT_UNESCAPE_EXTRA_INFO &&
1178 (*src == '#' || *src == '?')) {
1179 stop_unescaping = TRUE;
1181 } else if(*src == '%' && isxdigit(*(src + 1)) && isxdigit(*(src + 2))
1182 && stop_unescaping == FALSE) {
1185 memcpy(buf, src + 1, 2);
1187 ih = strtol(buf, NULL, 16);
1189 src += 2; /* Advance to end of escape */
1193 if(dwFlags & URL_UNESCAPE_INPLACE || needed < *pcchUnescaped)
1197 if(dwFlags & URL_UNESCAPE_INPLACE || needed < *pcchUnescaped) {
1201 needed++; /* add one for the '\0' */
1204 if(!(dwFlags & URL_UNESCAPE_INPLACE))
1205 *pcchUnescaped = needed;
1208 TRACE("result %s\n", (dwFlags & URL_UNESCAPE_INPLACE) ?
1209 debugstr_a(pszUrl) : debugstr_a(pszUnescaped));
1215 /*************************************************************************
1216 * UrlUnescapeW [SHLWAPI.@]
1220 HRESULT WINAPI UrlUnescapeW(
1222 LPWSTR pszUnescaped,
1223 LPDWORD pcchUnescaped,
1230 BOOL stop_unescaping = FALSE;
1232 TRACE("(%s, %p, %p, 0x%08lx)\n", debugstr_w(pszUrl), pszUnescaped,
1233 pcchUnescaped, dwFlags);
1235 if(!pszUrl || !pszUnescaped || !pcchUnescaped)
1236 return E_INVALIDARG;
1238 if(dwFlags & URL_UNESCAPE_INPLACE)
1243 for(src = pszUrl, needed = 0; *src; src++, needed++) {
1244 if(dwFlags & URL_DONT_UNESCAPE_EXTRA_INFO &&
1245 (*src == L'#' || *src == L'?')) {
1246 stop_unescaping = TRUE;
1248 } else if(*src == L'%' && isxdigitW(*(src + 1)) && isxdigitW(*(src + 2))
1249 && stop_unescaping == FALSE) {
1252 memcpy(buf, src + 1, 2*sizeof(WCHAR));
1254 ih = StrToIntW(buf);
1256 src += 2; /* Advance to end of escape */
1260 if(dwFlags & URL_UNESCAPE_INPLACE || needed < *pcchUnescaped)
1264 if(dwFlags & URL_UNESCAPE_INPLACE || needed < *pcchUnescaped) {
1268 needed++; /* add one for the '\0' */
1271 if(!(dwFlags & URL_UNESCAPE_INPLACE))
1272 *pcchUnescaped = needed;
1275 TRACE("result %s\n", (dwFlags & URL_UNESCAPE_INPLACE) ?
1276 debugstr_w(pszUrl) : debugstr_w(pszUnescaped));
1282 /*************************************************************************
1283 * UrlGetLocationA [SHLWAPI.@]
1285 * Get the location from a Url.
1288 * pszUrl [I] Url to get the location from
1291 * A pointer to the start of the location in pszUrl, or NULL if there is
1295 * - MSDN erroneously states that "The location is the segment of the Url
1296 * starting with a '?' or '#' character". Neither V4 nor V5 of shlwapi.dll
1297 * stop at '?' and always return a NULL in this case.
1298 * - MSDN also erroneously states that "If a file URL has a query string,
1299 * the returned string is the query string". In all tested cases, if the
1300 * Url starts with "fi" then a NULL is returned. V5 gives the following results:
1303 *| NULL file://aa/b/cd#hohoh
1304 *| #hohoh http://aa/b/cd#hohoh
1305 *| NULL fi://aa/b/cd#hohoh
1306 *| #hohoh ff://aa/b/cd#hohoh
1308 LPCSTR WINAPI UrlGetLocationA(
1311 UNKNOWN_SHLWAPI_1 base;
1315 res1 = ParseURLA(pszUrl, &base);
1316 if (res1) return NULL; /* invalid scheme */
1318 /* if scheme is file: then never return pointer */
1319 if (strncmp(base.ap1, "file", min(4,base.sizep1)) == 0) return NULL;
1321 /* Look for '#' and return its addr */
1322 return strchr(base.ap2, '#');
1325 /*************************************************************************
1326 * UrlGetLocationW [SHLWAPI.@]
1328 * See UrlGetLocationA.
1330 LPCWSTR WINAPI UrlGetLocationW(
1333 UNKNOWN_SHLWAPI_2 base;
1337 res1 = ParseURLW(pszUrl, &base);
1338 if (res1) return NULL; /* invalid scheme */
1340 /* if scheme is file: then never return pointer */
1341 if (strncmpW(base.ap1, fileW, min(4,base.sizep1)) == 0) return NULL;
1343 /* Look for '#' and return its addr */
1344 return strchrW(base.ap2, L'#');
1347 /*************************************************************************
1348 * UrlCompareA [SHLWAPI.@]
1353 * pszUrl1 [I] First Url to compare
1354 * pszUrl2 [I] Url to compare to pszUrl1
1355 * fIgnoreSlash [I] TRUE = compare only up to a final slash
1358 * less than zero, zero, or greater than zero indicating pszUrl2 is greater
1359 * than, equal to, or less than pszUrl1 respectively.
1361 INT WINAPI UrlCompareA(
1366 INT ret, len, len1, len2;
1369 return strcmp(pszUrl1, pszUrl2);
1370 len1 = strlen(pszUrl1);
1371 if (pszUrl1[len1-1] == '/') len1--;
1372 len2 = strlen(pszUrl2);
1373 if (pszUrl2[len2-1] == '/') len2--;
1375 return strncmp(pszUrl1, pszUrl2, len1);
1376 len = min(len1, len2);
1377 ret = strncmp(pszUrl1, pszUrl2, len);
1378 if (ret) return ret;
1379 if (len1 > len2) return 1;
1383 /*************************************************************************
1384 * UrlCompareW [SHLWAPI.@]
1388 INT WINAPI UrlCompareW(
1394 size_t len, len1, len2;
1397 return strcmpW(pszUrl1, pszUrl2);
1398 len1 = strlenW(pszUrl1);
1399 if (pszUrl1[len1-1] == '/') len1--;
1400 len2 = strlenW(pszUrl2);
1401 if (pszUrl2[len2-1] == '/') len2--;
1403 return strncmpW(pszUrl1, pszUrl2, len1);
1404 len = min(len1, len2);
1405 ret = strncmpW(pszUrl1, pszUrl2, len);
1406 if (ret) return ret;
1407 if (len1 > len2) return 1;
1411 /*************************************************************************
1412 * HashData [SHLWAPI.@]
1414 * Hash an input block into a variable sized digest.
1417 * lpSrc [I] Input block
1418 * nSrcLen [I] Length of lpSrc
1419 * lpDest [I] Output for hash digest
1420 * nDestLen [I] Length of lpDest
1423 * Success: TRUE. lpDest is filled with the computed hash value.
1424 * Failure: FALSE, if any argument is invalid.
1426 HRESULT WINAPI HashData(const unsigned char *lpSrc, DWORD nSrcLen,
1427 unsigned char *lpDest, DWORD nDestLen)
1429 INT srcCount = nSrcLen - 1, destCount = nDestLen - 1;
1431 if (IsBadReadPtr(lpSrc, nSrcLen) ||
1432 IsBadWritePtr(lpDest, nDestLen))
1433 return E_INVALIDARG;
1435 while (destCount >= 0)
1437 lpDest[destCount] = (destCount & 0xff);
1441 while (srcCount >= 0)
1443 destCount = nDestLen - 1;
1444 while (destCount >= 0)
1446 lpDest[destCount] = HashDataLookup[lpSrc[srcCount] ^ lpDest[destCount]];
1454 /*************************************************************************
1455 * UrlHashA [SHLWAPI.@]
1457 * Produce a Hash from a Url.
1460 * pszUrl [I] Url to hash
1461 * lpDest [O] Destinationh for hash
1462 * nDestLen [I] Length of lpDest
1465 * Success: S_OK. lpDest is filled with the computed hash value.
1466 * Failure: E_INVALIDARG, if any argument is invalid.
1468 HRESULT WINAPI UrlHashA(LPCSTR pszUrl, unsigned char *lpDest, DWORD nDestLen)
1470 if (IsBadStringPtrA(pszUrl, -1) || IsBadWritePtr(lpDest, nDestLen))
1471 return E_INVALIDARG;
1473 HashData((PBYTE)pszUrl, (int)strlen(pszUrl), lpDest, nDestLen);
1477 /*************************************************************************
1478 * UrlHashW [SHLWAPI.@]
1482 HRESULT WINAPI UrlHashW(LPCWSTR pszUrl, unsigned char *lpDest, DWORD nDestLen)
1484 char szUrl[MAX_PATH];
1486 TRACE("(%s,%p,%ld)\n",debugstr_w(pszUrl), lpDest, nDestLen);
1488 if (IsBadStringPtrW(pszUrl, -1) || IsBadWritePtr(lpDest, nDestLen))
1489 return E_INVALIDARG;
1491 /* Win32 hashes the data as an ASCII string, presumably so that both A+W
1492 * return the same digests for the same URL.
1494 WideCharToMultiByte(0, 0, pszUrl, -1, szUrl, MAX_PATH, 0, 0);
1495 HashData((PBYTE)szUrl, (int)strlen(szUrl), lpDest, nDestLen);
1499 /*************************************************************************
1500 * UrlApplySchemeA [SHLWAPI.@]
1502 * Apply a scheme to a Url.
1505 * pszIn [I] Url to apply scheme to
1506 * pszOut [O] Destination for modified Url
1507 * pcchOut [I/O] Length of pszOut/destination for length of pszOut
1508 * dwFlags [I] URL_ flags from "shlwapi.h"
1511 * Success: S_OK: pszOut contains the modified Url, pcchOut contains its length.
1512 * Failure: An HRESULT error code describing the error.
1514 HRESULT WINAPI UrlApplySchemeA(LPCSTR pszIn, LPSTR pszOut, LPDWORD pcchOut, DWORD dwFlags)
1517 DWORD ret, len, len2;
1519 TRACE("(in %s, out size %ld, flags %08lx) using W version\n",
1520 debugstr_a(pszIn), *pcchOut, dwFlags);
1522 in = (LPWSTR) HeapAlloc(GetProcessHeap(), 0,
1523 (2*INTERNET_MAX_URL_LENGTH) * sizeof(WCHAR));
1524 out = in + INTERNET_MAX_URL_LENGTH;
1526 MultiByteToWideChar(0, 0, pszIn, -1, in, INTERNET_MAX_URL_LENGTH);
1527 len = INTERNET_MAX_URL_LENGTH;
1529 ret = UrlApplySchemeW(in, out, &len, dwFlags);
1530 if ((ret != S_OK) && (ret != S_FALSE)) {
1531 HeapFree(GetProcessHeap(), 0, in);
1535 len2 = WideCharToMultiByte(0, 0, out, len+1, 0, 0, 0, 0);
1536 if (len2 > *pcchOut) {
1538 HeapFree(GetProcessHeap(), 0, in);
1541 WideCharToMultiByte(0, 0, out, len+1, pszOut, *pcchOut, 0, 0);
1543 HeapFree(GetProcessHeap(), 0, in);
1547 static HRESULT URL_GuessScheme(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut)
1552 DWORD value_len, data_len, dwType, i;
1553 WCHAR reg_path[MAX_PATH];
1554 WCHAR value[MAX_PATH], data[MAX_PATH];
1557 MultiByteToWideChar(0, 0,
1558 "Software\\Microsoft\\Windows\\CurrentVersion\\URL\\Prefixes",
1559 -1, reg_path, MAX_PATH);
1560 RegOpenKeyExW(HKEY_LOCAL_MACHINE, reg_path, 0, 1, &newkey);
1562 while(value_len = data_len = MAX_PATH,
1563 RegEnumValueW(newkey, index, value, &value_len,
1564 0, &dwType, (LPVOID)data, &data_len) == 0) {
1565 TRACE("guess %d %s is %s\n",
1566 index, debugstr_w(value), debugstr_w(data));
1569 for(i=0; i<value_len; i++) {
1572 /* remember that TRUE is not-equal */
1573 j = ChrCmpIW(Wxx, Wyy);
1576 if ((i == value_len) && !j) {
1577 if (strlenW(data) + strlenW(pszIn) + 1 > *pcchOut) {
1578 *pcchOut = strlenW(data) + strlenW(pszIn) + 1;
1579 RegCloseKey(newkey);
1582 strcpyW(pszOut, data);
1583 strcatW(pszOut, pszIn);
1584 *pcchOut = strlenW(pszOut);
1585 TRACE("matched and set to %s\n", debugstr_w(pszOut));
1586 RegCloseKey(newkey);
1591 RegCloseKey(newkey);
1595 static HRESULT URL_ApplyDefault(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut)
1598 DWORD data_len, dwType;
1599 WCHAR reg_path[MAX_PATH];
1600 WCHAR value[MAX_PATH], data[MAX_PATH];
1602 /* get and prepend default */
1603 MultiByteToWideChar(0, 0,
1604 "Software\\Microsoft\\Windows\\CurrentVersion\\URL\\DefaultPrefix",
1605 -1, reg_path, MAX_PATH);
1606 RegOpenKeyExW(HKEY_LOCAL_MACHINE, reg_path, 0, 1, &newkey);
1607 data_len = MAX_PATH;
1610 RegQueryValueExW(newkey, value, 0, &dwType, (LPBYTE)data, &data_len);
1611 RegCloseKey(newkey);
1612 if (strlenW(data) + strlenW(pszIn) + 1 > *pcchOut) {
1613 *pcchOut = strlenW(data) + strlenW(pszIn) + 1;
1616 strcpyW(pszOut, data);
1617 strcatW(pszOut, pszIn);
1618 *pcchOut = strlenW(pszOut);
1619 TRACE("used default %s\n", debugstr_w(pszOut));
1623 /*************************************************************************
1624 * UrlApplySchemeW [SHLWAPI.@]
1626 * See UrlApplySchemeA.
1628 HRESULT WINAPI UrlApplySchemeW(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut, DWORD dwFlags)
1630 UNKNOWN_SHLWAPI_2 in_scheme;
1634 TRACE("(in %s, out size %ld, flags %08lx)\n",
1635 debugstr_w(pszIn), *pcchOut, dwFlags);
1637 if (dwFlags & URL_APPLY_GUESSFILE) {
1638 FIXME("(%s %p %p(%ld) 0x%08lx): stub URL_APPLY_GUESSFILE not implemented\n",
1639 debugstr_w(pszIn), pszOut, pcchOut, *pcchOut, dwFlags);
1640 strcpyW(pszOut, pszIn);
1641 *pcchOut = strlenW(pszOut);
1645 in_scheme.size = 24;
1646 /* See if the base has a scheme */
1647 res1 = ParseURLW(pszIn, &in_scheme);
1649 /* no scheme in input, need to see if we need to guess */
1650 if (dwFlags & URL_APPLY_GUESSSCHEME) {
1651 if ((ret = URL_GuessScheme(pszIn, pszOut, pcchOut)) != -1)
1656 /* we have a scheme, see if valid (known scheme) */
1657 if (in_scheme.fcncde) {
1658 /* have valid scheme, so just copy and exit */
1659 if (strlenW(pszIn) + 1 > *pcchOut) {
1660 *pcchOut = strlenW(pszIn) + 1;
1663 strcpyW(pszOut, pszIn);
1664 *pcchOut = strlenW(pszOut);
1665 TRACE("valid scheme, returing copy\n");
1670 /* If we are here, then either invalid scheme,
1671 * or no scheme and can't/failed guess.
1673 if ( ( ((res1 == 0) && (dwFlags & URL_APPLY_FORCEAPPLY)) ||
1675 (dwFlags & URL_APPLY_DEFAULT)) {
1676 /* find and apply default scheme */
1677 return URL_ApplyDefault(pszIn, pszOut, pcchOut);
1680 /* just copy and give proper return code */
1681 if (strlenW(pszIn) + 1 > *pcchOut) {
1682 *pcchOut = strlenW(pszIn) + 1;
1685 strcpyW(pszOut, pszIn);
1686 *pcchOut = strlenW(pszOut);
1687 TRACE("returning copy, left alone\n");
1691 /*************************************************************************
1692 * UrlIsA [SHLWAPI.@]
1694 * Determine if a Url is of a certain class.
1697 * pszUrl [I] Url to check
1698 * Urlis [I] URLIS_ constant from "shlwapi.h"
1701 * TRUE if pszUrl belongs to the class type in Urlis.
1704 BOOL WINAPI UrlIsA(LPCSTR pszUrl, URLIS Urlis)
1706 UNKNOWN_SHLWAPI_1 base;
1713 res1 = ParseURLA(pszUrl, &base);
1714 if (res1) return FALSE; /* invalid scheme */
1715 if ((*base.ap2 == '/') && (*(base.ap2+1) == '/'))
1716 /* has scheme followed by 2 '/' */
1721 case URLIS_NOHISTORY:
1723 case URLIS_APPLIABLE:
1724 case URLIS_DIRECTORY:
1725 case URLIS_HASQUERY:
1727 FIXME("(%s %d): stub\n", debugstr_a(pszUrl), Urlis);
1732 /*************************************************************************
1733 * UrlIsW [SHLWAPI.@]
1737 BOOL WINAPI UrlIsW(LPCWSTR pszUrl, URLIS Urlis)
1739 UNKNOWN_SHLWAPI_2 base;
1746 res1 = ParseURLW(pszUrl, &base);
1747 if (res1) return FALSE; /* invalid scheme */
1748 if ((*base.ap2 == L'/') && (*(base.ap2+1) == L'/'))
1749 /* has scheme followed by 2 '/' */
1754 case URLIS_NOHISTORY:
1756 case URLIS_APPLIABLE:
1757 case URLIS_DIRECTORY:
1758 case URLIS_HASQUERY:
1760 FIXME("(%s %d): stub\n", debugstr_w(pszUrl), Urlis);
1765 /*************************************************************************
1766 * UrlIsNoHistoryA [SHLWAPI.@]
1768 * Determine if a Url should not be stored in the users history list.
1771 * pszUrl [I] Url to check
1774 * TRUE, if pszUrl should be excluded from the history list,
1777 BOOL WINAPI UrlIsNoHistoryA(LPCSTR pszUrl)
1779 return UrlIsA(pszUrl, URLIS_NOHISTORY);
1782 /*************************************************************************
1783 * UrlIsNoHistoryW [SHLWAPI.@]
1785 * See UrlIsNoHistoryA.
1787 BOOL WINAPI UrlIsNoHistoryW(LPCWSTR pszUrl)
1789 return UrlIsW(pszUrl, URLIS_NOHISTORY);
1792 /*************************************************************************
1793 * UrlIsOpaqueA [SHLWAPI.@]
1795 * Determine if a Url is opaque.
1798 * pszUrl [I] Url to check
1801 * TRUE if pszUrl is opaque,
1805 * An opaque Url is one that does not start with "<protocol>://".
1807 BOOL WINAPI UrlIsOpaqueA(LPCSTR pszUrl)
1809 return UrlIsA(pszUrl, URLIS_OPAQUE);
1812 /*************************************************************************
1813 * UrlIsOpaqueW [SHLWAPI.@]
1817 BOOL WINAPI UrlIsOpaqueW(LPCWSTR pszUrl)
1819 return UrlIsW(pszUrl, URLIS_OPAQUE);
1822 /*************************************************************************
1823 * Scans for characters of type "type" and when not matching found,
1824 * returns pointer to it and length in size.
1826 * Characters tested based on RFC 1738
1828 static LPCWSTR URL_ScanID(LPCWSTR start, LPDWORD size, WINE_URL_SCAN_TYPE type)
1830 static DWORD alwayszero = 0;
1839 if ( (islowerW(*start) && isalphaW(*start)) ||
1854 if ( isalphaW(*start) ||
1856 /* user/password only characters */
1861 /* *extra* characters */
1864 (*start == L'\'') ||
1868 /* *safe* characters */
1876 } else if (*start == L'%') {
1877 if (isxdigitW(*(start+1)) &&
1878 isxdigitW(*(start+2))) {
1890 if (isdigitW(*start)) {
1901 if (isalnumW(*start) ||
1903 (*start == L'.') ) {
1912 FIXME("unknown type %d\n", type);
1913 return (LPWSTR)&alwayszero;
1915 /* TRACE("scanned %ld characters next char %p<%c>\n",
1916 *size, start, *start); */
1920 /*************************************************************************
1921 * Attempt to parse URL into pieces.
1923 static LONG URL_ParseUrl(LPCWSTR pszUrl, WINE_PARSE_URL *pl)
1927 memset(pl, 0, sizeof(WINE_PARSE_URL));
1928 pl->pScheme = pszUrl;
1929 work = URL_ScanID(pl->pScheme, &pl->szScheme, SCHEME);
1930 if (!*work || (*work != L':')) goto ErrorExit;
1932 if ((*work != L'/') || (*(work+1) != L'/')) goto ErrorExit;
1933 pl->pUserName = work + 2;
1934 work = URL_ScanID(pl->pUserName, &pl->szUserName, USERPASS);
1935 if (*work == L':' ) {
1936 /* parse password */
1938 pl->pPassword = work;
1939 work = URL_ScanID(pl->pPassword, &pl->szPassword, USERPASS);
1940 if (*work != L'@') {
1941 /* what we just parsed must be the hostname and port
1942 * so reset pointers and clear then let it parse */
1943 pl->szUserName = pl->szPassword = 0;
1944 work = pl->pUserName - 1;
1945 pl->pUserName = pl->pPassword = 0;
1947 } else if (*work == L'@') {
1951 } else if (!*work || (*work == L'/') || (*work == L'.')) {
1952 /* what was parsed was hostname, so reset pointers and let it parse */
1953 pl->szUserName = pl->szPassword = 0;
1954 work = pl->pUserName - 1;
1955 pl->pUserName = pl->pPassword = 0;
1956 } else goto ErrorExit;
1958 /* now start parsing hostname or hostnumber */
1960 pl->pHostName = work;
1961 work = URL_ScanID(pl->pHostName, &pl->szHostName, HOST);
1962 if (*work == L':') {
1966 work = URL_ScanID(pl->pPort, &pl->szPort, PORT);
1968 if (*work == L'/') {
1969 /* see if query string */
1970 pl->pQuery = strchrW(work, L'?');
1971 if (pl->pQuery) pl->szQuery = strlenW(pl->pQuery);
1973 TRACE("parse successful: scheme=%p(%ld), user=%p(%ld), pass=%p(%ld), host=%p(%ld), port=%p(%ld), query=%p(%ld)\n",
1974 pl->pScheme, pl->szScheme,
1975 pl->pUserName, pl->szUserName,
1976 pl->pPassword, pl->szPassword,
1977 pl->pHostName, pl->szHostName,
1978 pl->pPort, pl->szPort,
1979 pl->pQuery, pl->szQuery);
1982 FIXME("failed to parse %s\n", debugstr_w(pszUrl));
1983 return E_INVALIDARG;
1986 /*************************************************************************
1987 * UrlGetPartA [SHLWAPI.@]
1989 * Retrieve part of a Url.
1992 * pszIn [I] Url to parse
1993 * pszOut [O] Destination for part of pszIn requested
1994 * pcchOut [I/O] Length of pszOut/destination for length of pszOut
1995 * dwPart [I] URL_PART_ enum from "shlwapi.h"
1996 * dwFlags [I] URL_ flags from "shlwapi.h"
1999 * Success: S_OK. pszOut contains the part requested, pcchOut contains its length.
2000 * Failure: An HRESULT error code describing the error.
2002 HRESULT WINAPI UrlGetPartA(LPCSTR pszIn, LPSTR pszOut, LPDWORD pcchOut,
2003 DWORD dwPart, DWORD dwFlags)
2006 DWORD ret, len, len2;
2008 in = (LPWSTR) HeapAlloc(GetProcessHeap(), 0,
2009 (2*INTERNET_MAX_URL_LENGTH) * sizeof(WCHAR));
2010 out = in + INTERNET_MAX_URL_LENGTH;
2012 MultiByteToWideChar(0, 0, pszIn, -1, in, INTERNET_MAX_URL_LENGTH);
2014 len = INTERNET_MAX_URL_LENGTH;
2015 ret = UrlGetPartW(in, out, &len, dwPart, dwFlags);
2018 HeapFree(GetProcessHeap(), 0, in);
2022 len2 = WideCharToMultiByte(0, 0, out, len, 0, 0, 0, 0);
2023 if (len2 > *pcchOut) {
2025 HeapFree(GetProcessHeap(), 0, in);
2028 WideCharToMultiByte(0, 0, out, len+1, pszOut, *pcchOut, 0, 0);
2030 HeapFree(GetProcessHeap(), 0, in);
2034 /*************************************************************************
2035 * UrlGetPartW [SHLWAPI.@]
2039 HRESULT WINAPI UrlGetPartW(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut,
2040 DWORD dwPart, DWORD dwFlags)
2044 DWORD size, schsize;
2045 LPCWSTR addr, schaddr;
2048 TRACE("(%s %p %p(%ld) %08lx %08lx)\n",
2049 debugstr_w(pszIn), pszOut, pcchOut, *pcchOut, dwPart, dwFlags);
2051 ret = URL_ParseUrl(pszIn, &pl);
2053 schaddr = pl.pScheme;
2054 schsize = pl.szScheme;
2057 case URL_PART_SCHEME:
2058 if (!pl.szScheme) return E_INVALIDARG;
2063 case URL_PART_HOSTNAME:
2064 if (!pl.szHostName) return E_INVALIDARG;
2065 addr = pl.pHostName;
2066 size = pl.szHostName;
2069 case URL_PART_USERNAME:
2070 if (!pl.szUserName) return E_INVALIDARG;
2071 addr = pl.pUserName;
2072 size = pl.szUserName;
2075 case URL_PART_PASSWORD:
2076 if (!pl.szPassword) return E_INVALIDARG;
2077 addr = pl.pPassword;
2078 size = pl.szPassword;
2082 if (!pl.szPort) return E_INVALIDARG;
2087 case URL_PART_QUERY:
2088 if (!pl.szQuery) return E_INVALIDARG;
2094 return E_INVALIDARG;
2097 if (dwFlags == URL_PARTFLAG_KEEPSCHEME) {
2098 if (*pcchOut < size + schsize + 2) {
2099 *pcchOut = size + schsize + 2;
2102 strncpyW(pszOut, schaddr, schsize);
2103 work = pszOut + schsize;
2105 strncpyW(work+1, addr, size);
2106 *pcchOut = size + schsize + 1;
2111 if (*pcchOut < size + 1) {*pcchOut = size+1; return E_POINTER;}
2112 strncpyW(pszOut, addr, size);
2114 work = pszOut + size;
2117 TRACE("len=%ld %s\n", *pcchOut, debugstr_w(pszOut));
2122 /*************************************************************************
2123 * PathIsURLA [SHLWAPI.@]
2125 * Check if the given path is a Url.
2128 * lpszPath [I] Path to check.
2131 * TRUE if lpszPath is a Url.
2132 * FALSE if lpszPath is NULL or not a Url.
2134 BOOL WINAPI PathIsURLA(LPCSTR lpstrPath)
2136 UNKNOWN_SHLWAPI_1 base;
2139 if (!lpstrPath || !*lpstrPath) return FALSE;
2142 base.size = sizeof(base);
2143 res1 = ParseURLA(lpstrPath, &base);
2144 return (base.fcncde > 0);
2147 /*************************************************************************
2148 * PathIsURLW [SHLWAPI.@]
2152 BOOL WINAPI PathIsURLW(LPCWSTR lpstrPath)
2154 UNKNOWN_SHLWAPI_2 base;
2157 if (!lpstrPath || !*lpstrPath) return FALSE;
2160 base.size = sizeof(base);
2161 res1 = ParseURLW(lpstrPath, &base);
2162 return (base.fcncde > 0);
2165 /*************************************************************************
2166 * UrlCreateFromPathA [SHLWAPI.@]
2168 * Create a Url from a file path.
2171 * pszPath [I] Path to convert
2172 * pszUrl [O] Destination for the converted Url
2173 * pcchUrl [I/O] Length of pszUrl
2174 * dwReserved [I] Reserved, must be 0
2177 * Success: S_OK. pszUrl contains the converted path.
2178 * Failure: An HRESULT error code.
2180 HRESULT WINAPI UrlCreateFromPathA(LPCSTR pszPath, LPSTR pszUrl, LPDWORD pcchUrl, DWORD dwReserved)
2182 DWORD nCharBeforeColon = 0;
2184 DWORD dwChRequired = 0;
2185 LPSTR pszNewUrl = NULL;
2186 LPCSTR pszConstPointer = NULL;
2187 LPSTR pszPointer = NULL;
2191 TRACE("(%s, %p, %p, 0x%08lx)\n", debugstr_a(pszPath), pszUrl, pcchUrl, dwReserved);
2193 /* Validate arguments */
2194 if (dwReserved != 0)
2196 FIXME("dwReserved should be 0: 0x%08lx\n", dwReserved);
2197 return E_INVALIDARG;
2199 if (!pszUrl || !pcchUrl || !pszUrl)
2201 ERR("Invalid argument\n");
2202 return E_INVALIDARG;
2205 for (pszConstPointer = pszPath; *pszConstPointer; pszConstPointer++)
2207 if (isalpha(*pszConstPointer) || isdigit(*pszConstPointer) ||
2208 *pszConstPointer == '.' || *pszConstPointer == '-')
2212 if (*pszConstPointer == ':') /* then already in URL format, so copy */
2214 dwChRequired = lstrlenA(pszPath);
2215 if (dwChRequired > *pcchUrl)
2217 *pcchUrl = dwChRequired;
2222 *pcchUrl = dwChRequired;
2223 StrCpyA(pszUrl, pszPath);
2227 /* then must need converting to file: format */
2229 /* Strip off leading slashes */
2230 while (*pszPath == '\\' || *pszPath == '/')
2236 dwChRequired = *pcchUrl; /* UrlEscape will fill this in with the correct amount */
2237 TRACE("pszUrl: %s\n", debugstr_a(pszPath));
2238 pszNewUrl = HeapAlloc(GetProcessHeap(), 0, dwChRequired + 1);
2239 ret = UrlEscapeA(pszPath, pszNewUrl, &dwChRequired, URL_ESCAPE_PERCENT);
2240 TRACE("ret: 0x%08lx, pszUrl: %s\n", ret, debugstr_a(pszNewUrl));
2241 TRACE("%ld\n", dwChRequired);
2242 if (ret != E_POINTER && FAILED(ret))
2244 dwChRequired += 5; /* "file:" */
2245 if ((lstrlenA(pszUrl) > 1) && isalpha(pszUrl[0]) && (pszUrl[1] == ':'))
2247 dwChRequired += 3; /* "///" */
2253 case 0: /* no slashes */
2255 case 2: /* two slashes */
2262 default: /* three slashes */
2267 if (dwChRequired > *pcchUrl)
2269 *pcchUrl = dwChRequired; /* Return number of chars required (not including termination) */
2270 StrCpyA(pszUrl, "file:");
2271 pszPointer = pszUrl + lstrlenA(pszUrl);
2272 for (i=0; i < nSlashes; i++)
2277 StrCpyA(pszPointer, pszNewUrl);
2278 TRACE("<- %s\n", debugstr_a(pszUrl));
2282 /*************************************************************************
2283 * UrlCreateFromPathW [SHLWAPI.@]
2285 * See UrlCreateFromPathA.
2287 HRESULT WINAPI UrlCreateFromPathW(LPCWSTR pszPath, LPWSTR pszUrl, LPDWORD pcchUrl, DWORD dwReserved)
2289 DWORD nCharBeforeColon = 0;
2291 DWORD dwChRequired = 0;
2292 LPWSTR pszNewUrl = NULL;
2293 LPCWSTR pszConstPointer = NULL;
2294 LPWSTR pszPointer = NULL;
2298 TRACE("(%s, %p, %p, 0x%08lx)\n", debugstr_w(pszPath), pszUrl, pcchUrl, dwReserved);
2300 /* Validate arguments */
2301 if (dwReserved != 0)
2302 return E_INVALIDARG;
2303 if (!pszUrl || !pcchUrl || !pszUrl)
2304 return E_INVALIDARG;
2306 for (pszConstPointer = pszPath; *pszConstPointer; pszConstPointer++)
2308 if (isalphaW(*pszConstPointer) || isdigitW(*pszConstPointer) ||
2309 *pszConstPointer == '.' || *pszConstPointer == '-')
2313 if (*pszConstPointer == ':') /* then already in URL format, so copy */
2315 dwChRequired = lstrlenW(pszPath);
2316 *pcchUrl = dwChRequired;
2317 if (dwChRequired > *pcchUrl)
2321 StrCpyW(pszUrl, pszPath);
2325 /* then must need converting to file: format */
2327 /* Strip off leading slashes */
2328 while (*pszPath == '\\' || *pszPath == '/')
2334 dwChRequired = *pcchUrl; /* UrlEscape will fill this in with the correct amount */
2335 ret = UrlEscapeW(pszPath, pszUrl, &dwChRequired, URL_ESCAPE_PERCENT);
2336 if (ret != E_POINTER && FAILED(ret))
2338 dwChRequired += 5; /* "file:" */
2339 if ((lstrlenW(pszUrl) > 1) && isalphaW(pszUrl[0]) && (pszUrl[1] == ':'))
2341 dwChRequired += 3; /* "///" */
2347 case 0: /* no slashes */
2349 case 2: /* two slashes */
2356 default: /* three slashes */
2361 *pcchUrl = dwChRequired; /* Return number of chars required (not including termination) */
2362 if (dwChRequired > *pcchUrl)
2364 pszNewUrl = HeapAlloc(GetProcessHeap(), 0, (dwChRequired + 1) * sizeof(WCHAR));
2365 StrCpyW(pszNewUrl, fileW);
2366 pszPointer = pszNewUrl + 4;
2369 for (i=0; i < nSlashes; i++)
2374 StrCpyW(pszPointer, pszPath);
2375 StrCpyW(pszUrl, pszNewUrl);
2379 /*************************************************************************
2380 * SHAutoComplete [SHLWAPI.@]
2382 * Enable auto-completion for an edit control.
2385 * hwndEdit [I] Handle of control to enable auto-completion for
2386 * dwFlags [I] SHACF_ flags from "shlwapi.h"
2389 * Success: S_OK. Auto-completion is enabled for the control.
2390 * Failure: An HRESULT error code indicating the error.
2392 HRESULT WINAPI SHAutoComplete(HWND hwndEdit, DWORD dwFlags)
2394 FIXME("SHAutoComplete stub\n");
2398 /*************************************************************************
2399 * MLBuildResURLA [SHLWAPI.405]
2401 * Create a Url pointing to a resource in a module.
2404 * lpszLibName [I] Name of the module containing the resource
2405 * hMod [I] Callers module handle
2406 * dwFlags [I] Undocumented flags for loading the module
2407 * lpszRes [I] Resource name
2408 * lpszDest [O] Destination for resulting Url
2409 * dwDestLen [I] Length of lpszDest
2412 * Success: S_OK. lpszDest constains the resource Url.
2413 * Failure: E_INVALIDARG, if any argument is invalid, or
2414 * E_FAIL if dwDestLen is too small.
2416 HRESULT WINAPI MLBuildResURLA(LPCSTR lpszLibName, HMODULE hMod, DWORD dwFlags,
2417 LPCSTR lpszRes, LPSTR lpszDest, DWORD dwDestLen)
2419 WCHAR szLibName[MAX_PATH], szRes[MAX_PATH], szDest[MAX_PATH];
2423 MultiByteToWideChar(CP_ACP, 0, lpszLibName, -1, szLibName, sizeof(szLibName)/sizeof(WCHAR));
2426 MultiByteToWideChar(CP_ACP, 0, lpszRes, -1, szRes, sizeof(szRes)/sizeof(WCHAR));
2428 if (dwDestLen > sizeof(szLibName)/sizeof(WCHAR))
2429 dwDestLen = sizeof(szLibName)/sizeof(WCHAR);
2431 hRet = MLBuildResURLW(lpszLibName ? szLibName : NULL, hMod, dwFlags,
2432 lpszRes ? szRes : NULL, lpszDest ? szDest : NULL, dwDestLen);
2433 if (SUCCEEDED(hRet) && lpszDest)
2434 WideCharToMultiByte(CP_ACP, 0, szDest, -1, lpszDest, dwDestLen, 0, 0);
2439 /*************************************************************************
2440 * MLBuildResURLA [SHLWAPI.406]
2442 * See MLBuildResURLA.
2444 HRESULT WINAPI MLBuildResURLW(LPCWSTR lpszLibName, HMODULE hMod, DWORD dwFlags,
2445 LPCWSTR lpszRes, LPWSTR lpszDest, DWORD dwDestLen)
2447 static const WCHAR szRes[] = { 'r','e','s',':','/','/','\0' };
2448 #define szResLen ((sizeof(szRes) - sizeof(WCHAR))/sizeof(WCHAR))
2449 HRESULT hRet = E_FAIL;
2451 TRACE("(%s,%p,0x%08lx,%s,%p,%ld)\n", debugstr_w(lpszLibName), hMod, dwFlags,
2452 debugstr_w(lpszRes), lpszDest, dwDestLen);
2454 if (!lpszLibName || !hMod || hMod == INVALID_HANDLE_VALUE || !lpszRes ||
2455 !lpszDest || (dwFlags && dwFlags != 2))
2456 return E_INVALIDARG;
2458 if (dwDestLen >= szResLen + 1)
2460 dwDestLen -= (szResLen + 1);
2461 memcpy(lpszDest, szRes, sizeof(szRes));
2463 hMod = MLLoadLibraryW(lpszLibName, hMod, dwFlags);
2467 WCHAR szBuff[MAX_PATH];
2469 if (GetModuleFileNameW(hMod, szBuff, sizeof(szBuff)/sizeof(WCHAR)))
2471 DWORD dwPathLen = strlenW(szBuff) + 1;
2473 if (dwDestLen >= dwPathLen)
2477 dwDestLen -= dwPathLen;
2478 memcpy(lpszDest + szResLen, szBuff, dwPathLen * sizeof(WCHAR));
2480 dwResLen = strlenW(lpszRes) + 1;
2481 if (dwDestLen >= dwResLen + 1)
2483 lpszDest[szResLen + dwPathLen + dwResLen] = '/';
2484 memcpy(lpszDest + szResLen + dwPathLen, lpszRes, dwResLen * sizeof(WCHAR));
2489 MLFreeLibrary(hMod);