4 * Copyright 2000 Huw D M Davies for CodeWeavers.
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
22 #include "wine/port.h"
30 #include "wine/unicode.h"
34 #define NO_SHLWAPI_STREAM
36 #include "wine/debug.h"
38 HMODULE WINAPI MLLoadLibraryW(LPCWSTR,HMODULE,DWORD);
39 BOOL WINAPI MLFreeLibrary(HMODULE);
40 HRESULT WINAPI MLBuildResURLW(LPCWSTR,HMODULE,DWORD,LPCWSTR,LPWSTR,DWORD);
42 WINE_DEFAULT_DEBUG_CHANNEL(shell);
44 /* The following schemes were identified in the native version of
45 * SHLWAPI.DLL version 5.50
48 URL_SCHEME scheme_number;
52 static const SHL_2_inet_scheme shlwapi_schemes[] = {
53 {URL_SCHEME_FTP, "ftp"},
54 {URL_SCHEME_HTTP, "http"},
55 {URL_SCHEME_GOPHER, "gopher"},
56 {URL_SCHEME_MAILTO, "mailto"},
57 {URL_SCHEME_NEWS, "news"},
58 {URL_SCHEME_NNTP, "nntp"},
59 {URL_SCHEME_TELNET, "telnet"},
60 {URL_SCHEME_WAIS, "wais"},
61 {URL_SCHEME_FILE, "file"},
62 {URL_SCHEME_MK, "mk"},
63 {URL_SCHEME_HTTPS, "https"},
64 {URL_SCHEME_SHELL, "shell"},
65 {URL_SCHEME_SNEWS, "snews"},
66 {URL_SCHEME_LOCAL, "local"},
67 {URL_SCHEME_JAVASCRIPT, "javascript"},
68 {URL_SCHEME_VBSCRIPT, "vbscript"},
69 {URL_SCHEME_ABOUT, "about"},
70 {URL_SCHEME_RES, "res"},
75 LPCWSTR pScheme; /* [out] start of scheme */
76 DWORD szScheme; /* [out] size of scheme (until colon) */
77 LPCWSTR pUserName; /* [out] start of Username */
78 DWORD szUserName; /* [out] size of Username (until ":" or "@") */
79 LPCWSTR pPassword; /* [out] start of Password */
80 DWORD szPassword; /* [out] size of Password (until "@") */
81 LPCWSTR pHostName; /* [out] start of Hostname */
82 DWORD szHostName; /* [out] size of Hostname (until ":" or "/") */
83 LPCWSTR pPort; /* [out] start of Port */
84 DWORD szPort; /* [out] size of Port (until "/" or eos) */
85 LPCWSTR pQuery; /* [out] start of Query */
86 DWORD szQuery; /* [out] size of Query (until eos) */
96 static const CHAR hexDigits[] = "0123456789ABCDEF";
98 static const WCHAR fileW[] = {'f','i','l','e','\0'};
100 static const unsigned char HashDataLookup[256] = {
101 0x01, 0x0E, 0x6E, 0x19, 0x61, 0xAE, 0x84, 0x77, 0x8A, 0xAA, 0x7D, 0x76, 0x1B,
102 0xE9, 0x8C, 0x33, 0x57, 0xC5, 0xB1, 0x6B, 0xEA, 0xA9, 0x38, 0x44, 0x1E, 0x07,
103 0xAD, 0x49, 0xBC, 0x28, 0x24, 0x41, 0x31, 0xD5, 0x68, 0xBE, 0x39, 0xD3, 0x94,
104 0xDF, 0x30, 0x73, 0x0F, 0x02, 0x43, 0xBA, 0xD2, 0x1C, 0x0C, 0xB5, 0x67, 0x46,
105 0x16, 0x3A, 0x4B, 0x4E, 0xB7, 0xA7, 0xEE, 0x9D, 0x7C, 0x93, 0xAC, 0x90, 0xB0,
106 0xA1, 0x8D, 0x56, 0x3C, 0x42, 0x80, 0x53, 0x9C, 0xF1, 0x4F, 0x2E, 0xA8, 0xC6,
107 0x29, 0xFE, 0xB2, 0x55, 0xFD, 0xED, 0xFA, 0x9A, 0x85, 0x58, 0x23, 0xCE, 0x5F,
108 0x74, 0xFC, 0xC0, 0x36, 0xDD, 0x66, 0xDA, 0xFF, 0xF0, 0x52, 0x6A, 0x9E, 0xC9,
109 0x3D, 0x03, 0x59, 0x09, 0x2A, 0x9B, 0x9F, 0x5D, 0xA6, 0x50, 0x32, 0x22, 0xAF,
110 0xC3, 0x64, 0x63, 0x1A, 0x96, 0x10, 0x91, 0x04, 0x21, 0x08, 0xBD, 0x79, 0x40,
111 0x4D, 0x48, 0xD0, 0xF5, 0x82, 0x7A, 0x8F, 0x37, 0x69, 0x86, 0x1D, 0xA4, 0xB9,
112 0xC2, 0xC1, 0xEF, 0x65, 0xF2, 0x05, 0xAB, 0x7E, 0x0B, 0x4A, 0x3B, 0x89, 0xE4,
113 0x6C, 0xBF, 0xE8, 0x8B, 0x06, 0x18, 0x51, 0x14, 0x7F, 0x11, 0x5B, 0x5C, 0xFB,
114 0x97, 0xE1, 0xCF, 0x15, 0x62, 0x71, 0x70, 0x54, 0xE2, 0x12, 0xD6, 0xC7, 0xBB,
115 0x0D, 0x20, 0x5E, 0xDC, 0xE0, 0xD4, 0xF7, 0xCC, 0xC4, 0x2B, 0xF9, 0xEC, 0x2D,
116 0xF4, 0x6F, 0xB6, 0x99, 0x88, 0x81, 0x5A, 0xD9, 0xCA, 0x13, 0xA5, 0xE7, 0x47,
117 0xE6, 0x8E, 0x60, 0xE3, 0x3E, 0xB3, 0xF6, 0x72, 0xA2, 0x35, 0xA0, 0xD7, 0xCD,
118 0xB4, 0x2F, 0x6D, 0x2C, 0x26, 0x1F, 0x95, 0x87, 0x00, 0xD8, 0x34, 0x3F, 0x17,
119 0x25, 0x45, 0x27, 0x75, 0x92, 0xB8, 0xA3, 0xC8, 0xDE, 0xEB, 0xF8, 0xF3, 0xDB,
120 0x0A, 0x98, 0x83, 0x7B, 0xE5, 0xCB, 0x4C, 0x78, 0xD1 };
122 static BOOL URL_JustLocation(LPCWSTR str)
124 while(*str && (*str == L'/')) str++;
126 while (*str && ((*str == L'-') ||
128 isalnumW(*str))) str++;
129 if (*str == L'/') return FALSE;
135 /*************************************************************************
138 * Parse a Url into its constituent parts.
142 * y [O] Undocumented structure holding the parsed information
145 * Success: S_OK. y contains the parsed Url details.
146 * Failure: An HRESULT error code.
148 HRESULT WINAPI ParseURLA(LPCSTR x, PARSEDURLA *y)
151 const SHL_2_inet_scheme *inet_pro;
153 y->nScheme = URL_SCHEME_INVALID;
154 if (y->cbSize != sizeof(*y)) return E_INVALIDARG;
155 /* FIXME: leading white space generates error of 0x80041001 which
158 if (*x <= ' ') return 0x80041001;
164 y->cchProtocol = cnt;
173 /* check for no scheme in string start */
174 /* (apparently schemes *must* be larger than a single character) */
175 if ((*x == '\0') || (y->cchProtocol <= 1)) {
176 y->pszProtocol = NULL;
180 /* found scheme, set length of remainder */
181 y->cchSuffix = lstrlenA(y->pszSuffix);
183 /* see if known scheme and return indicator number */
184 y->nScheme = URL_SCHEME_UNKNOWN;
185 inet_pro = shlwapi_schemes;
186 while (inet_pro->scheme_name) {
187 if (!strncasecmp(inet_pro->scheme_name, y->pszProtocol,
188 min(y->cchProtocol, lstrlenA(inet_pro->scheme_name)))) {
189 y->nScheme = inet_pro->scheme_number;
197 /*************************************************************************
200 * Unicode version of ParseURLA.
202 HRESULT WINAPI ParseURLW(LPCWSTR x, PARSEDURLW *y)
205 const SHL_2_inet_scheme *inet_pro;
209 y->nScheme = URL_SCHEME_INVALID;
210 if (y->cbSize != sizeof(*y)) return E_INVALIDARG;
211 /* FIXME: leading white space generates error of 0x80041001 which
214 if (*x <= L' ') return 0x80041001;
220 y->cchProtocol = cnt;
229 /* check for no scheme in string start */
230 /* (apparently schemes *must* be larger than a single character) */
231 if ((*x == L'\0') || (y->cchProtocol <= 1)) {
232 y->pszProtocol = NULL;
236 /* found scheme, set length of remainder */
237 y->cchSuffix = lstrlenW(y->pszSuffix);
239 /* see if known scheme and return indicator number */
240 len = WideCharToMultiByte(0, 0, y->pszProtocol, y->cchProtocol, 0, 0, 0, 0);
241 cmpstr = HeapAlloc(GetProcessHeap(), 0, len);
242 WideCharToMultiByte(0, 0, y->pszProtocol, y->cchProtocol, cmpstr, len, 0, 0);
243 y->nScheme = URL_SCHEME_UNKNOWN;
244 inet_pro = shlwapi_schemes;
245 while (inet_pro->scheme_name) {
246 if (!strncasecmp(inet_pro->scheme_name, cmpstr,
247 min(len, lstrlenA(inet_pro->scheme_name)))) {
248 y->nScheme = inet_pro->scheme_number;
253 HeapFree(GetProcessHeap(), 0, cmpstr);
257 /*************************************************************************
258 * UrlCanonicalizeA [SHLWAPI.@]
260 * Canonicalize a Url.
263 * pszUrl [I] Url to cCanonicalize
264 * pszCanonicalized [O] Destination for converted Url.
265 * pcchCanonicalized [I/O] Length of pszUrl, destination for length of pszCanonicalized
266 * dwFlags [I] Flags controlling the conversion.
269 * Success: S_OK. The pszCanonicalized contains the converted Url.
270 * Failure: E_POINTER, if *pcchCanonicalized is too small.
272 * MSDN incorrectly describes the flags for this function. They should be:
273 *| URL_DONT_ESCAPE_EXTRA_INFO 0x02000000
274 *| URL_ESCAPE_SPACES_ONLY 0x04000000
275 *| URL_ESCAPE_PERCENT 0x00001000
276 *| URL_ESCAPE_UNSAFE 0x10000000
277 *| URL_UNESCAPE 0x10000000
278 *| URL_DONT_SIMPLIFY 0x08000000
279 *| URL_ESCAPE_SEGMENT_ONLY 0x00002000
281 HRESULT WINAPI UrlCanonicalizeA(LPCSTR pszUrl, LPSTR pszCanonicalized,
282 LPDWORD pcchCanonicalized, DWORD dwFlags)
284 LPWSTR base, canonical;
285 DWORD ret, len, len2;
287 TRACE("(%s %p %p 0x%08lx) using W version\n",
288 debugstr_a(pszUrl), pszCanonicalized,
289 pcchCanonicalized, dwFlags);
291 if(!pszUrl || !pszCanonicalized || !pcchCanonicalized)
294 base = HeapAlloc(GetProcessHeap(), 0,
295 (2*INTERNET_MAX_URL_LENGTH) * sizeof(WCHAR));
296 canonical = base + INTERNET_MAX_URL_LENGTH;
298 MultiByteToWideChar(0, 0, pszUrl, -1, base, INTERNET_MAX_URL_LENGTH);
299 len = INTERNET_MAX_URL_LENGTH;
301 ret = UrlCanonicalizeW(base, canonical, &len, dwFlags);
303 HeapFree(GetProcessHeap(), 0, base);
307 len2 = WideCharToMultiByte(0, 0, canonical, len, 0, 0, 0, 0);
308 if (len2 > *pcchCanonicalized) {
309 *pcchCanonicalized = len;
310 HeapFree(GetProcessHeap(), 0, base);
313 WideCharToMultiByte(0, 0, canonical, len+1, pszCanonicalized,
314 *pcchCanonicalized, 0, 0);
315 *pcchCanonicalized = len2;
316 HeapFree(GetProcessHeap(), 0, base);
320 /*************************************************************************
321 * UrlCanonicalizeW [SHLWAPI.@]
323 * See UrlCanonicalizeA.
325 HRESULT WINAPI UrlCanonicalizeW(LPCWSTR pszUrl, LPWSTR pszCanonicalized,
326 LPDWORD pcchCanonicalized, DWORD dwFlags)
330 LPWSTR lpszUrlCpy, wk1, wk2, mp, mp2, root;
335 static const WCHAR wszFile[] = {'f','i','l','e',':'};
337 TRACE("(%s %p %p 0x%08lx)\n", debugstr_w(pszUrl), pszCanonicalized,
338 pcchCanonicalized, dwFlags);
340 if(!pszUrl || !pszCanonicalized || !pcchCanonicalized)
343 nByteLen = (lstrlenW(pszUrl) + 1) * sizeof(WCHAR); /* length in bytes */
344 lpszUrlCpy = HeapAlloc(GetProcessHeap(), 0, nByteLen);
346 if((dwFlags & URL_FILE_USE_PATHURL) && nByteLen >= sizeof(wszFile)
347 && !memcmp(wszFile, pszUrl, sizeof(wszFile)))
353 * 1 have 2[+] alnum 2,3
354 * 2 have scheme (found :) 4,6,3
355 * 3 failed (no location)
357 * 5 have 1[+] alnum 6,3
358 * 6 have location (found /) save root location
361 wk1 = (LPWSTR)pszUrl;
367 if (!isalnumW(*wk1)) {state = 3; break;}
369 if (!isalnumW(*wk1)) {state = 3; break;}
375 if (*wk1++ == L':') state = 2;
378 if (*wk1 != L'/') {state = 3; break;}
380 if (*wk1 != L'/') {state = 6; break;}
382 if(*wk1 == '/' && (dwFlags & URL_FILE_USE_PATHURL))
387 nWkLen = strlenW(wk1);
388 memcpy(wk2, wk1, (nWkLen + 1) * sizeof(WCHAR));
394 if(*mp == '/' || *mp == '\\')
400 if (!isalnumW(*wk1) && (*wk1 != L'-') && (*wk1 != L'.') && (*wk1 != ':'))
402 while(isalnumW(*wk1) || (*wk1 == L'-') || (*wk1 == L'.') || (*wk1 == ':'))
407 if (*wk1 != '/' && *wk1 != '\\') {state = 3; break;}
408 while(*wk1 == '/' || *wk1 == '\\') {
415 if(dwFlags & URL_DONT_SIMPLIFY) {
420 /* Now at root location, cannot back up any more. */
421 /* "root" will point at the '/' */
425 mp = strchrW(wk1, '/');
426 mp2 = strchrW(wk1, '\\');
427 if(mp2 && (!mp || mp2 < mp))
430 nWkLen = strlenW(wk1);
431 memcpy(wk2, wk1, (nWkLen + 1) * sizeof(WCHAR));
438 memcpy(wk2, wk1, nLen * sizeof(WCHAR));
446 TRACE("found '/.'\n");
447 if (wk1[1] == '/' || wk1[1] == '\\') {
448 /* case of /./ -> skip the ./ */
451 else if (wk1[1] == '.') {
452 /* found /.. look for next / */
453 TRACE("found '/..'\n");
454 if (wk1[2] == '/' || wk1[2] == '\\' ||wk1[2] == '?'
455 || wk1[2] == '#' || !wk1[2]) {
456 /* case /../ -> need to backup wk2 */
457 TRACE("found '/../'\n");
458 *(wk2-1) = L'\0'; /* set end of string */
459 mp = strrchrW(root, slash);
460 if (mp && (mp >= root)) {
461 /* found valid backup point */
463 if(wk1[2] != '/' && wk1[2] != '\\')
469 /* did not find point, restore '/' */
479 FIXME("how did we get here - state=%d\n", state);
480 HeapFree(GetProcessHeap(), 0, lpszUrlCpy);
484 TRACE("Simplified, orig <%s>, simple <%s>\n",
485 debugstr_w(pszUrl), debugstr_w(lpszUrlCpy));
487 nLen = lstrlenW(lpszUrlCpy);
488 while ((nLen > 0) && ((lpszUrlCpy[nLen-1] == '\r')||(lpszUrlCpy[nLen-1] == '\n')))
489 lpszUrlCpy[--nLen]=0;
491 if(dwFlags & (URL_UNESCAPE | URL_FILE_USE_PATHURL))
492 UrlUnescapeW(lpszUrlCpy, NULL, &nLen, URL_UNESCAPE_INPLACE);
494 if((EscapeFlags = dwFlags & (URL_ESCAPE_UNSAFE |
495 URL_ESCAPE_SPACES_ONLY |
497 URL_DONT_ESCAPE_EXTRA_INFO |
498 URL_ESCAPE_SEGMENT_ONLY ))) {
499 EscapeFlags &= ~URL_ESCAPE_UNSAFE;
500 hr = UrlEscapeW(lpszUrlCpy, pszCanonicalized, pcchCanonicalized,
502 } else { /* No escaping needed, just copy the string */
503 nLen = lstrlenW(lpszUrlCpy);
504 if(nLen < *pcchCanonicalized)
505 memcpy(pszCanonicalized, lpszUrlCpy, (nLen + 1)*sizeof(WCHAR));
510 *pcchCanonicalized = nLen;
513 HeapFree(GetProcessHeap(), 0, lpszUrlCpy);
516 TRACE("result %s\n", debugstr_w(pszCanonicalized));
521 /*************************************************************************
522 * UrlCombineA [SHLWAPI.@]
527 * pszBase [I] Base Url
528 * pszRelative [I] Url to combine with pszBase
529 * pszCombined [O] Destination for combined Url
530 * pcchCombined [O] Destination for length of pszCombined
531 * dwFlags [I] URL_ flags from "shlwapi.h"
534 * Success: S_OK. pszCombined contains the combined Url, pcchCombined
535 * contains its length.
536 * Failure: An HRESULT error code indicating the error.
538 HRESULT WINAPI UrlCombineA(LPCSTR pszBase, LPCSTR pszRelative,
539 LPSTR pszCombined, LPDWORD pcchCombined,
542 LPWSTR base, relative, combined;
543 DWORD ret, len, len2;
545 TRACE("(base %s, Relative %s, Combine size %ld, flags %08lx) using W version\n",
546 debugstr_a(pszBase),debugstr_a(pszRelative),
547 pcchCombined?*pcchCombined:0,dwFlags);
549 if(!pszBase || !pszRelative || !pcchCombined)
552 base = HeapAlloc(GetProcessHeap(), 0,
553 (3*INTERNET_MAX_URL_LENGTH) * sizeof(WCHAR));
554 relative = base + INTERNET_MAX_URL_LENGTH;
555 combined = relative + INTERNET_MAX_URL_LENGTH;
557 MultiByteToWideChar(0, 0, pszBase, -1, base, INTERNET_MAX_URL_LENGTH);
558 MultiByteToWideChar(0, 0, pszRelative, -1, relative, INTERNET_MAX_URL_LENGTH);
561 ret = UrlCombineW(base, relative, pszCombined?combined:NULL, &len, dwFlags);
564 HeapFree(GetProcessHeap(), 0, base);
568 len2 = WideCharToMultiByte(0, 0, combined, len, 0, 0, 0, 0);
569 if (len2 > *pcchCombined) {
570 *pcchCombined = len2;
571 HeapFree(GetProcessHeap(), 0, base);
574 WideCharToMultiByte(0, 0, combined, len+1, pszCombined, (*pcchCombined)+1,
576 *pcchCombined = len2;
577 HeapFree(GetProcessHeap(), 0, base);
581 /*************************************************************************
582 * UrlCombineW [SHLWAPI.@]
586 HRESULT WINAPI UrlCombineW(LPCWSTR pszBase, LPCWSTR pszRelative,
587 LPWSTR pszCombined, LPDWORD pcchCombined,
590 PARSEDURLW base, relative;
591 DWORD myflags, sizeloc = 0;
592 DWORD len, res1, res2, process_case = 0;
593 LPWSTR work, preliminary, mbase, mrelative;
594 static const WCHAR myfilestr[] = {'f','i','l','e',':','/','/','/','\0'};
595 static const WCHAR single_slash[] = {'/','\0'};
598 TRACE("(base %s, Relative %s, Combine size %ld, flags %08lx)\n",
599 debugstr_w(pszBase),debugstr_w(pszRelative),
600 pcchCombined?*pcchCombined:0,dwFlags);
602 if(!pszBase || !pszRelative || !pcchCombined)
605 base.cbSize = sizeof(base);
606 relative.cbSize = sizeof(relative);
608 /* Get space for duplicates of the input and the output */
609 preliminary = HeapAlloc(GetProcessHeap(), 0, (3*INTERNET_MAX_URL_LENGTH) *
611 mbase = preliminary + INTERNET_MAX_URL_LENGTH;
612 mrelative = mbase + INTERNET_MAX_URL_LENGTH;
613 *preliminary = L'\0';
615 /* Canonicalize the base input prior to looking for the scheme */
616 myflags = dwFlags & (URL_DONT_SIMPLIFY | URL_UNESCAPE);
617 len = INTERNET_MAX_URL_LENGTH;
618 ret = UrlCanonicalizeW(pszBase, mbase, &len, myflags);
620 /* Canonicalize the relative input prior to looking for the scheme */
621 len = INTERNET_MAX_URL_LENGTH;
622 ret = UrlCanonicalizeW(pszRelative, mrelative, &len, myflags);
624 /* See if the base has a scheme */
625 res1 = ParseURLW(mbase, &base);
627 /* if pszBase has no scheme, then return pszRelative */
628 TRACE("no scheme detected in Base\n");
633 /* get size of location field (if it exists) */
634 work = (LPWSTR)base.pszSuffix;
636 if (*work++ == L'/') {
637 if (*work++ == L'/') {
638 /* At this point have start of location and
639 * it ends at next '/' or end of string.
641 while(*work && (*work != L'/')) work++;
642 sizeloc = (DWORD)(work - base.pszSuffix);
646 /* Change .sizep2 to not have the last leaf in it,
647 * Note: we need to start after the location (if it exists)
649 work = strrchrW((base.pszSuffix+sizeloc), L'/');
651 len = (DWORD)(work - base.pszSuffix + 1);
652 base.cchSuffix = len;
656 * .pszSuffix points to location (starting with '//')
657 * .cchSuffix length of location (above) and rest less the last
659 * sizeloc length of location (above) up to but not including
663 res2 = ParseURLW(mrelative, &relative);
665 /* no scheme in pszRelative */
666 TRACE("no scheme detected in Relative\n");
667 relative.pszSuffix = mrelative; /* case 3,4,5 depends on this */
668 relative.cchSuffix = strlenW(mrelative);
669 if (*pszRelative == L':') {
670 /* case that is either left alone or uses pszBase */
671 if (dwFlags & URL_PLUGGABLE_PROTOCOL) {
678 if (isalnum(*mrelative) && (*(mrelative + 1) == L':')) {
679 /* case that becomes "file:///" */
680 strcpyW(preliminary, myfilestr);
684 if ((*mrelative == L'/') && (*(mrelative+1) == L'/')) {
685 /* pszRelative has location and rest */
689 if (*mrelative == L'/') {
690 /* case where pszRelative is root to location */
694 process_case = (*base.pszSuffix == L'/') ? 5 : 3;
698 /* handle cases where pszRelative has scheme */
699 if ((base.cchProtocol == relative.cchProtocol) &&
700 (strncmpW(base.pszProtocol, relative.pszProtocol, base.cchProtocol) == 0)) {
702 /* since the schemes are the same */
703 if ((*relative.pszSuffix == L'/') && (*(relative.pszSuffix+1) == L'/')) {
704 /* case where pszRelative replaces location and following */
708 if (*relative.pszSuffix == L'/') {
709 /* case where pszRelative is root to location */
713 /* case where scheme is followed by document path */
717 if ((*relative.pszSuffix == L'/') && (*(relative.pszSuffix+1) == L'/')) {
718 /* case where pszRelative replaces scheme, location,
719 * and following and handles PLUGGABLE
726 } while(FALSE); /* a litte trick to allow easy exit from nested if's */
730 switch (process_case) {
733 * Return pszRelative appended to what ever is in pszCombined,
734 * (which may the string "file:///"
736 strcatW(preliminary, mrelative);
740 * Same as case 1, but if URL_PLUGGABLE_PROTOCOL was specified
741 * and pszRelative starts with "//", then append a "/"
743 strcpyW(preliminary, mrelative);
744 if (!(dwFlags & URL_PLUGGABLE_PROTOCOL) &&
745 URL_JustLocation(relative.pszSuffix))
746 strcatW(preliminary, single_slash);
750 * Return the pszBase scheme with pszRelative. Basically
751 * keeps the scheme and replaces the domain and following.
753 memcpy(preliminary, base.pszProtocol, (base.cchProtocol + 1)*sizeof(WCHAR));
754 work = preliminary + base.cchProtocol + 1;
755 strcpyW(work, relative.pszSuffix);
756 if (!(dwFlags & URL_PLUGGABLE_PROTOCOL) &&
757 URL_JustLocation(relative.pszSuffix))
758 strcatW(work, single_slash);
762 * Return the pszBase scheme and location but everything
763 * after the location is pszRelative. (Replace document
766 memcpy(preliminary, base.pszProtocol, (base.cchProtocol+1+sizeloc)*sizeof(WCHAR));
767 work = preliminary + base.cchProtocol + 1 + sizeloc;
768 if (dwFlags & URL_PLUGGABLE_PROTOCOL)
770 strcpyW(work, relative.pszSuffix);
774 * Return the pszBase without its document (if any) and
775 * append pszRelative after its scheme.
777 memcpy(preliminary, base.pszProtocol,
778 (base.cchProtocol+1+base.cchSuffix)*sizeof(WCHAR));
779 work = preliminary + base.cchProtocol+1+base.cchSuffix - 1;
782 strcpyW(work, relative.pszSuffix);
786 FIXME("How did we get here????? process_case=%ld\n", process_case);
791 /* Reuse mrelative as temp storage as its already allocated and not needed anymore */
792 ret = UrlCanonicalizeW(preliminary, mrelative, pcchCombined, dwFlags);
793 if(SUCCEEDED(ret) && pszCombined) {
794 lstrcpyW(pszCombined, mrelative);
796 TRACE("return-%ld len=%ld, %s\n",
797 process_case, *pcchCombined, debugstr_w(pszCombined));
799 HeapFree(GetProcessHeap(), 0, preliminary);
803 /*************************************************************************
804 * UrlEscapeA [SHLWAPI.@]
807 HRESULT WINAPI UrlEscapeA(
813 WCHAR bufW[INTERNET_MAX_URL_LENGTH];
814 WCHAR *escapedW = bufW;
817 DWORD lenW = sizeof(bufW)/sizeof(WCHAR), lenA;
819 if(!RtlCreateUnicodeStringFromAsciiz(&urlW, pszUrl))
821 if((ret = UrlEscapeW(urlW.Buffer, escapedW, &lenW, dwFlags)) == E_POINTER) {
822 escapedW = HeapAlloc(GetProcessHeap(), 0, lenW * sizeof(WCHAR));
823 ret = UrlEscapeW(urlW.Buffer, escapedW, &lenW, dwFlags);
826 RtlUnicodeToMultiByteSize(&lenA, escapedW, lenW * sizeof(WCHAR));
827 if(*pcchEscaped > lenA) {
828 RtlUnicodeToMultiByteN(pszEscaped, *pcchEscaped - 1, &lenA, escapedW, lenW * sizeof(WCHAR));
829 pszEscaped[lenA] = 0;
832 *pcchEscaped = lenA + 1;
836 if(escapedW != bufW) HeapFree(GetProcessHeap(), 0, escapedW);
837 RtlFreeUnicodeString(&urlW);
841 #define WINE_URL_BASH_AS_SLASH 0x01
842 #define WINE_URL_COLLAPSE_SLASHES 0x02
843 #define WINE_URL_ESCAPE_SLASH 0x04
844 #define WINE_URL_ESCAPE_HASH 0x08
845 #define WINE_URL_ESCAPE_QUESTION 0x10
846 #define WINE_URL_STOP_ON_HASH 0x20
847 #define WINE_URL_STOP_ON_QUESTION 0x40
849 static inline BOOL URL_NeedEscapeW(WCHAR ch, DWORD dwFlags, DWORD int_flags)
855 if(dwFlags & URL_ESCAPE_SPACES_ONLY) {
862 if ((dwFlags & URL_ESCAPE_PERCENT) && (ch == '%'))
865 if (ch <= 31 || ch >= 127)
886 if (int_flags & WINE_URL_ESCAPE_SLASH) return TRUE;
890 if (int_flags & WINE_URL_ESCAPE_QUESTION) return TRUE;
894 if (int_flags & WINE_URL_ESCAPE_HASH) return TRUE;
904 /*************************************************************************
905 * UrlEscapeW [SHLWAPI.@]
907 * Converts unsafe characters in a Url into escape sequences.
910 * pszUrl [I] Url to modify
911 * pszEscaped [O] Destination for modified Url
912 * pcchEscaped [I/O] Length of pszUrl, destination for length of pszEscaped
913 * dwFlags [I] URL_ flags from "shlwapi.h"
916 * Success: S_OK. pszEscaped contains the escaped Url, pcchEscaped
917 * contains its length.
918 * Failure: E_POINTER, if pszEscaped is not large enough. In this case
919 * pcchEscaped is set to the required length.
921 * Converts unsafe characters into their escape sequences.
924 * - By default this function stops converting at the first '?' or
926 * - If dwFlags contains URL_ESCAPE_SPACES_ONLY then only spaces are
927 * converted, but the conversion continues past a '?' or '#'.
928 * - Note that this function did not work well (or at all) in shlwapi version 4.
931 * Only the following flags are implemented:
932 *| URL_ESCAPE_SPACES_ONLY
933 *| URL_DONT_ESCAPE_EXTRA_INFO
934 *| URL_ESCAPE_SEGMENT_ONLY
935 *| URL_ESCAPE_PERCENT
937 HRESULT WINAPI UrlEscapeW(
944 DWORD needed = 0, ret;
945 BOOL stop_escaping = FALSE;
946 WCHAR next[5], *dst = pszEscaped;
948 PARSEDURLW parsed_url;
951 static const WCHAR localhost[] = {'l','o','c','a','l','h','o','s','t',0};
953 TRACE("(%s %p %p 0x%08lx)\n", debugstr_w(pszUrl), pszEscaped,
954 pcchEscaped, dwFlags);
956 if(!pszUrl || !pszEscaped || !pcchEscaped)
959 if(dwFlags & ~(URL_ESCAPE_SPACES_ONLY |
960 URL_ESCAPE_SEGMENT_ONLY |
961 URL_DONT_ESCAPE_EXTRA_INFO |
963 FIXME("Unimplemented flags: %08lx\n", dwFlags);
966 if (dwFlags & URL_ESCAPE_SPACES_ONLY)
967 /* if SPACES_ONLY specified, reset the other controls */
968 dwFlags &= ~(URL_DONT_ESCAPE_EXTRA_INFO |
970 URL_ESCAPE_SEGMENT_ONLY);
973 /* if SPACES_ONLY *not* specified the assume DONT_ESCAPE_EXTRA_INFO */
974 dwFlags |= URL_DONT_ESCAPE_EXTRA_INFO;
978 if(dwFlags & URL_ESCAPE_SEGMENT_ONLY) {
979 int_flags = WINE_URL_ESCAPE_QUESTION | WINE_URL_ESCAPE_HASH | WINE_URL_ESCAPE_SLASH;
981 parsed_url.cbSize = sizeof(parsed_url);
982 if(ParseURLW(pszUrl, &parsed_url) != S_OK)
983 parsed_url.nScheme = URL_SCHEME_INVALID;
985 TRACE("scheme = %d (%s)\n", parsed_url.nScheme, debugstr_wn(parsed_url.pszProtocol, parsed_url.cchProtocol));
987 if(dwFlags & URL_DONT_ESCAPE_EXTRA_INFO)
988 int_flags = WINE_URL_STOP_ON_HASH | WINE_URL_STOP_ON_QUESTION;
990 switch(parsed_url.nScheme) {
991 case URL_SCHEME_FILE:
992 int_flags |= WINE_URL_BASH_AS_SLASH | WINE_URL_COLLAPSE_SLASHES | WINE_URL_ESCAPE_HASH;
993 int_flags &= ~WINE_URL_STOP_ON_HASH;
996 case URL_SCHEME_HTTP:
997 case URL_SCHEME_HTTPS:
998 int_flags |= WINE_URL_BASH_AS_SLASH;
999 if(parsed_url.pszSuffix[0] != '/' && parsed_url.pszSuffix[0] != '\\')
1000 int_flags |= WINE_URL_ESCAPE_SLASH;
1003 case URL_SCHEME_MAILTO:
1004 int_flags |= WINE_URL_ESCAPE_SLASH | WINE_URL_ESCAPE_QUESTION | WINE_URL_ESCAPE_HASH;
1005 int_flags &= ~(WINE_URL_STOP_ON_QUESTION | WINE_URL_STOP_ON_HASH);
1008 case URL_SCHEME_INVALID:
1011 case URL_SCHEME_FTP:
1013 if(parsed_url.pszSuffix[0] != '/')
1014 int_flags |= WINE_URL_ESCAPE_SLASH;
1019 for(src = pszUrl; *src; ) {
1023 if((int_flags & WINE_URL_COLLAPSE_SLASHES) && src == pszUrl + parsed_url.cchProtocol + 1) {
1024 int localhost_len = sizeof(localhost)/sizeof(WCHAR) - 1;
1025 while(cur == '/' || cur == '\\') {
1029 if(slashes == 2 && !strncmpiW(src, localhost, localhost_len)) { /* file://localhost/ -> file:/// */
1030 if(*(src + localhost_len) == '/' || *(src + localhost_len) == '\\')
1031 src += localhost_len + 1;
1038 next[0] = next[1] = next[2] = '/';
1045 next[0] = next[1] = '/';
1052 if(cur == '#' && (int_flags & WINE_URL_STOP_ON_HASH))
1053 stop_escaping = TRUE;
1055 if(cur == '?' && (int_flags & WINE_URL_STOP_ON_QUESTION))
1056 stop_escaping = TRUE;
1058 if(cur == '\\' && (int_flags & WINE_URL_BASH_AS_SLASH) && !stop_escaping) cur = '/';
1060 if(URL_NeedEscapeW(cur, dwFlags, int_flags) && stop_escaping == FALSE) {
1062 next[1] = hexDigits[(cur >> 4) & 0xf];
1063 next[2] = hexDigits[cur & 0xf];
1072 if(needed + len <= *pcchEscaped) {
1073 memcpy(dst, next, len*sizeof(WCHAR));
1079 if(needed < *pcchEscaped) {
1083 needed++; /* add one for the '\0' */
1086 *pcchEscaped = needed;
1091 /*************************************************************************
1092 * UrlUnescapeA [SHLWAPI.@]
1094 * Converts Url escape sequences back to ordinary characters.
1097 * pszUrl [I/O] Url to convert
1098 * pszUnescaped [O] Destination for converted Url
1099 * pcchUnescaped [I/O] Size of output string
1100 * dwFlags [I] URL_ESCAPE_ Flags from "shlwapi.h"
1103 * Success: S_OK. The converted value is in pszUnescaped, or in pszUrl if
1104 * dwFlags includes URL_ESCAPE_INPLACE.
1105 * Failure: E_POINTER if the converted Url is bigger than pcchUnescaped. In
1106 * this case pcchUnescaped is set to the size required.
1108 * If dwFlags includes URL_DONT_ESCAPE_EXTRA_INFO, the conversion stops at
1109 * the first occurrence of either a '?' or '#' character.
1111 HRESULT WINAPI UrlUnescapeA(
1114 LPDWORD pcchUnescaped,
1121 BOOL stop_unescaping = FALSE;
1123 TRACE("(%s, %p, %p, 0x%08lx)\n", debugstr_a(pszUrl), pszUnescaped,
1124 pcchUnescaped, dwFlags);
1126 if(!pszUrl || (!pszUnescaped && !(dwFlags & URL_UNESCAPE_INPLACE)) || !pcchUnescaped)
1127 return E_INVALIDARG;
1129 if(dwFlags & URL_UNESCAPE_INPLACE)
1134 for(src = pszUrl, needed = 0; *src; src++, needed++) {
1135 if(dwFlags & URL_DONT_UNESCAPE_EXTRA_INFO &&
1136 (*src == '#' || *src == '?')) {
1137 stop_unescaping = TRUE;
1139 } else if(*src == '%' && isxdigit(*(src + 1)) && isxdigit(*(src + 2))
1140 && stop_unescaping == FALSE) {
1143 memcpy(buf, src + 1, 2);
1145 ih = strtol(buf, NULL, 16);
1147 src += 2; /* Advance to end of escape */
1151 if(dwFlags & URL_UNESCAPE_INPLACE || needed < *pcchUnescaped)
1155 if(dwFlags & URL_UNESCAPE_INPLACE || needed < *pcchUnescaped) {
1159 needed++; /* add one for the '\0' */
1162 if(!(dwFlags & URL_UNESCAPE_INPLACE))
1163 *pcchUnescaped = needed;
1166 TRACE("result %s\n", (dwFlags & URL_UNESCAPE_INPLACE) ?
1167 debugstr_a(pszUrl) : debugstr_a(pszUnescaped));
1173 /*************************************************************************
1174 * UrlUnescapeW [SHLWAPI.@]
1178 HRESULT WINAPI UrlUnescapeW(
1180 LPWSTR pszUnescaped,
1181 LPDWORD pcchUnescaped,
1188 BOOL stop_unescaping = FALSE;
1190 TRACE("(%s, %p, %p, 0x%08lx)\n", debugstr_w(pszUrl), pszUnescaped,
1191 pcchUnescaped, dwFlags);
1193 if(!pszUrl || (!pszUnescaped && !(dwFlags & URL_UNESCAPE_INPLACE))|| !pcchUnescaped)
1194 return E_INVALIDARG;
1196 if(dwFlags & URL_UNESCAPE_INPLACE)
1201 for(src = pszUrl, needed = 0; *src; src++, needed++) {
1202 if(dwFlags & URL_DONT_UNESCAPE_EXTRA_INFO &&
1203 (*src == L'#' || *src == L'?')) {
1204 stop_unescaping = TRUE;
1206 } else if(*src == L'%' && isxdigitW(*(src + 1)) && isxdigitW(*(src + 2))
1207 && stop_unescaping == FALSE) {
1209 WCHAR buf[5] = {'0','x',0};
1210 memcpy(buf + 2, src + 1, 2*sizeof(WCHAR));
1212 StrToIntExW(buf, STIF_SUPPORT_HEX, &ih);
1214 src += 2; /* Advance to end of escape */
1218 if(dwFlags & URL_UNESCAPE_INPLACE || needed < *pcchUnescaped)
1222 if(dwFlags & URL_UNESCAPE_INPLACE || needed < *pcchUnescaped) {
1226 needed++; /* add one for the '\0' */
1229 if(!(dwFlags & URL_UNESCAPE_INPLACE))
1230 *pcchUnescaped = needed;
1233 TRACE("result %s\n", (dwFlags & URL_UNESCAPE_INPLACE) ?
1234 debugstr_w(pszUrl) : debugstr_w(pszUnescaped));
1240 /*************************************************************************
1241 * UrlGetLocationA [SHLWAPI.@]
1243 * Get the location from a Url.
1246 * pszUrl [I] Url to get the location from
1249 * A pointer to the start of the location in pszUrl, or NULL if there is
1253 * - MSDN erroneously states that "The location is the segment of the Url
1254 * starting with a '?' or '#' character". Neither V4 nor V5 of shlwapi.dll
1255 * stop at '?' and always return a NULL in this case.
1256 * - MSDN also erroneously states that "If a file URL has a query string,
1257 * the returned string is the query string". In all tested cases, if the
1258 * Url starts with "fi" then a NULL is returned. V5 gives the following results:
1261 *| NULL file://aa/b/cd#hohoh
1262 *| #hohoh http://aa/b/cd#hohoh
1263 *| NULL fi://aa/b/cd#hohoh
1264 *| #hohoh ff://aa/b/cd#hohoh
1266 LPCSTR WINAPI UrlGetLocationA(
1272 base.cbSize = sizeof(base);
1273 res1 = ParseURLA(pszUrl, &base);
1274 if (res1) return NULL; /* invalid scheme */
1276 /* if scheme is file: then never return pointer */
1277 if (strncmp(base.pszProtocol, "file", min(4,base.cchProtocol)) == 0) return NULL;
1279 /* Look for '#' and return its addr */
1280 return strchr(base.pszSuffix, '#');
1283 /*************************************************************************
1284 * UrlGetLocationW [SHLWAPI.@]
1286 * See UrlGetLocationA.
1288 LPCWSTR WINAPI UrlGetLocationW(
1294 base.cbSize = sizeof(base);
1295 res1 = ParseURLW(pszUrl, &base);
1296 if (res1) return NULL; /* invalid scheme */
1298 /* if scheme is file: then never return pointer */
1299 if (strncmpW(base.pszProtocol, fileW, min(4,base.cchProtocol)) == 0) return NULL;
1301 /* Look for '#' and return its addr */
1302 return strchrW(base.pszSuffix, L'#');
1305 /*************************************************************************
1306 * UrlCompareA [SHLWAPI.@]
1311 * pszUrl1 [I] First Url to compare
1312 * pszUrl2 [I] Url to compare to pszUrl1
1313 * fIgnoreSlash [I] TRUE = compare only up to a final slash
1316 * less than zero, zero, or greater than zero indicating pszUrl2 is greater
1317 * than, equal to, or less than pszUrl1 respectively.
1319 INT WINAPI UrlCompareA(
1324 INT ret, len, len1, len2;
1327 return strcmp(pszUrl1, pszUrl2);
1328 len1 = strlen(pszUrl1);
1329 if (pszUrl1[len1-1] == '/') len1--;
1330 len2 = strlen(pszUrl2);
1331 if (pszUrl2[len2-1] == '/') len2--;
1333 return strncmp(pszUrl1, pszUrl2, len1);
1334 len = min(len1, len2);
1335 ret = strncmp(pszUrl1, pszUrl2, len);
1336 if (ret) return ret;
1337 if (len1 > len2) return 1;
1341 /*************************************************************************
1342 * UrlCompareW [SHLWAPI.@]
1346 INT WINAPI UrlCompareW(
1352 size_t len, len1, len2;
1355 return strcmpW(pszUrl1, pszUrl2);
1356 len1 = strlenW(pszUrl1);
1357 if (pszUrl1[len1-1] == '/') len1--;
1358 len2 = strlenW(pszUrl2);
1359 if (pszUrl2[len2-1] == '/') len2--;
1361 return strncmpW(pszUrl1, pszUrl2, len1);
1362 len = min(len1, len2);
1363 ret = strncmpW(pszUrl1, pszUrl2, len);
1364 if (ret) return ret;
1365 if (len1 > len2) return 1;
1369 /*************************************************************************
1370 * HashData [SHLWAPI.@]
1372 * Hash an input block into a variable sized digest.
1375 * lpSrc [I] Input block
1376 * nSrcLen [I] Length of lpSrc
1377 * lpDest [I] Output for hash digest
1378 * nDestLen [I] Length of lpDest
1381 * Success: TRUE. lpDest is filled with the computed hash value.
1382 * Failure: FALSE, if any argument is invalid.
1384 HRESULT WINAPI HashData(const unsigned char *lpSrc, DWORD nSrcLen,
1385 unsigned char *lpDest, DWORD nDestLen)
1387 INT srcCount = nSrcLen - 1, destCount = nDestLen - 1;
1389 if (IsBadReadPtr(lpSrc, nSrcLen) ||
1390 IsBadWritePtr(lpDest, nDestLen))
1391 return E_INVALIDARG;
1393 while (destCount >= 0)
1395 lpDest[destCount] = (destCount & 0xff);
1399 while (srcCount >= 0)
1401 destCount = nDestLen - 1;
1402 while (destCount >= 0)
1404 lpDest[destCount] = HashDataLookup[lpSrc[srcCount] ^ lpDest[destCount]];
1412 /*************************************************************************
1413 * UrlHashA [SHLWAPI.@]
1415 * Produce a Hash from a Url.
1418 * pszUrl [I] Url to hash
1419 * lpDest [O] Destinationh for hash
1420 * nDestLen [I] Length of lpDest
1423 * Success: S_OK. lpDest is filled with the computed hash value.
1424 * Failure: E_INVALIDARG, if any argument is invalid.
1426 HRESULT WINAPI UrlHashA(LPCSTR pszUrl, unsigned char *lpDest, DWORD nDestLen)
1428 if (IsBadStringPtrA(pszUrl, -1) || IsBadWritePtr(lpDest, nDestLen))
1429 return E_INVALIDARG;
1431 HashData((const BYTE*)pszUrl, (int)strlen(pszUrl), lpDest, nDestLen);
1435 /*************************************************************************
1436 * UrlHashW [SHLWAPI.@]
1440 HRESULT WINAPI UrlHashW(LPCWSTR pszUrl, unsigned char *lpDest, DWORD nDestLen)
1442 char szUrl[MAX_PATH];
1444 TRACE("(%s,%p,%ld)\n",debugstr_w(pszUrl), lpDest, nDestLen);
1446 if (IsBadStringPtrW(pszUrl, -1) || IsBadWritePtr(lpDest, nDestLen))
1447 return E_INVALIDARG;
1449 /* Win32 hashes the data as an ASCII string, presumably so that both A+W
1450 * return the same digests for the same URL.
1452 WideCharToMultiByte(0, 0, pszUrl, -1, szUrl, MAX_PATH, 0, 0);
1453 HashData((const BYTE*)szUrl, (int)strlen(szUrl), lpDest, nDestLen);
1457 /*************************************************************************
1458 * UrlApplySchemeA [SHLWAPI.@]
1460 * Apply a scheme to a Url.
1463 * pszIn [I] Url to apply scheme to
1464 * pszOut [O] Destination for modified Url
1465 * pcchOut [I/O] Length of pszOut/destination for length of pszOut
1466 * dwFlags [I] URL_ flags from "shlwapi.h"
1469 * Success: S_OK: pszOut contains the modified Url, pcchOut contains its length.
1470 * Failure: An HRESULT error code describing the error.
1472 HRESULT WINAPI UrlApplySchemeA(LPCSTR pszIn, LPSTR pszOut, LPDWORD pcchOut, DWORD dwFlags)
1475 DWORD ret, len, len2;
1477 TRACE("(in %s, out size %ld, flags %08lx) using W version\n",
1478 debugstr_a(pszIn), *pcchOut, dwFlags);
1480 in = HeapAlloc(GetProcessHeap(), 0,
1481 (2*INTERNET_MAX_URL_LENGTH) * sizeof(WCHAR));
1482 out = in + INTERNET_MAX_URL_LENGTH;
1484 MultiByteToWideChar(0, 0, pszIn, -1, in, INTERNET_MAX_URL_LENGTH);
1485 len = INTERNET_MAX_URL_LENGTH;
1487 ret = UrlApplySchemeW(in, out, &len, dwFlags);
1488 if ((ret != S_OK) && (ret != S_FALSE)) {
1489 HeapFree(GetProcessHeap(), 0, in);
1493 len2 = WideCharToMultiByte(0, 0, out, len+1, 0, 0, 0, 0);
1494 if (len2 > *pcchOut) {
1496 HeapFree(GetProcessHeap(), 0, in);
1499 WideCharToMultiByte(0, 0, out, len+1, pszOut, *pcchOut, 0, 0);
1501 HeapFree(GetProcessHeap(), 0, in);
1505 static HRESULT URL_GuessScheme(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut)
1510 DWORD value_len, data_len, dwType, i;
1511 WCHAR reg_path[MAX_PATH];
1512 WCHAR value[MAX_PATH], data[MAX_PATH];
1515 MultiByteToWideChar(0, 0,
1516 "Software\\Microsoft\\Windows\\CurrentVersion\\URL\\Prefixes",
1517 -1, reg_path, MAX_PATH);
1518 RegOpenKeyExW(HKEY_LOCAL_MACHINE, reg_path, 0, 1, &newkey);
1520 while(value_len = data_len = MAX_PATH,
1521 RegEnumValueW(newkey, index, value, &value_len,
1522 0, &dwType, (LPVOID)data, &data_len) == 0) {
1523 TRACE("guess %d %s is %s\n",
1524 index, debugstr_w(value), debugstr_w(data));
1527 for(i=0; i<value_len; i++) {
1530 /* remember that TRUE is not-equal */
1531 j = ChrCmpIW(Wxx, Wyy);
1534 if ((i == value_len) && !j) {
1535 if (strlenW(data) + strlenW(pszIn) + 1 > *pcchOut) {
1536 *pcchOut = strlenW(data) + strlenW(pszIn) + 1;
1537 RegCloseKey(newkey);
1540 strcpyW(pszOut, data);
1541 strcatW(pszOut, pszIn);
1542 *pcchOut = strlenW(pszOut);
1543 TRACE("matched and set to %s\n", debugstr_w(pszOut));
1544 RegCloseKey(newkey);
1549 RegCloseKey(newkey);
1553 static HRESULT URL_ApplyDefault(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut)
1556 DWORD data_len, dwType;
1557 WCHAR reg_path[MAX_PATH];
1558 WCHAR value[MAX_PATH], data[MAX_PATH];
1560 /* get and prepend default */
1561 MultiByteToWideChar(0, 0,
1562 "Software\\Microsoft\\Windows\\CurrentVersion\\URL\\DefaultPrefix",
1563 -1, reg_path, MAX_PATH);
1564 RegOpenKeyExW(HKEY_LOCAL_MACHINE, reg_path, 0, 1, &newkey);
1565 data_len = MAX_PATH;
1568 RegQueryValueExW(newkey, value, 0, &dwType, (LPBYTE)data, &data_len);
1569 RegCloseKey(newkey);
1570 if (strlenW(data) + strlenW(pszIn) + 1 > *pcchOut) {
1571 *pcchOut = strlenW(data) + strlenW(pszIn) + 1;
1574 strcpyW(pszOut, data);
1575 strcatW(pszOut, pszIn);
1576 *pcchOut = strlenW(pszOut);
1577 TRACE("used default %s\n", debugstr_w(pszOut));
1581 /*************************************************************************
1582 * UrlApplySchemeW [SHLWAPI.@]
1584 * See UrlApplySchemeA.
1586 HRESULT WINAPI UrlApplySchemeW(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut, DWORD dwFlags)
1588 PARSEDURLW in_scheme;
1592 TRACE("(in %s, out size %ld, flags %08lx)\n",
1593 debugstr_w(pszIn), *pcchOut, dwFlags);
1595 if (dwFlags & URL_APPLY_GUESSFILE) {
1596 FIXME("(%s %p %p(%ld) 0x%08lx): stub URL_APPLY_GUESSFILE not implemented\n",
1597 debugstr_w(pszIn), pszOut, pcchOut, *pcchOut, dwFlags);
1598 strcpyW(pszOut, pszIn);
1599 *pcchOut = strlenW(pszOut);
1603 in_scheme.cbSize = sizeof(in_scheme);
1604 /* See if the base has a scheme */
1605 res1 = ParseURLW(pszIn, &in_scheme);
1607 /* no scheme in input, need to see if we need to guess */
1608 if (dwFlags & URL_APPLY_GUESSSCHEME) {
1609 if ((ret = URL_GuessScheme(pszIn, pszOut, pcchOut)) != -1)
1614 /* we have a scheme, see if valid (known scheme) */
1615 if (in_scheme.nScheme) {
1616 /* have valid scheme, so just copy and exit */
1617 if (strlenW(pszIn) + 1 > *pcchOut) {
1618 *pcchOut = strlenW(pszIn) + 1;
1621 strcpyW(pszOut, pszIn);
1622 *pcchOut = strlenW(pszOut);
1623 TRACE("valid scheme, returing copy\n");
1628 /* If we are here, then either invalid scheme,
1629 * or no scheme and can't/failed guess.
1631 if ( ( ((res1 == 0) && (dwFlags & URL_APPLY_FORCEAPPLY)) ||
1633 (dwFlags & URL_APPLY_DEFAULT)) {
1634 /* find and apply default scheme */
1635 return URL_ApplyDefault(pszIn, pszOut, pcchOut);
1638 /* just copy and give proper return code */
1639 if (strlenW(pszIn) + 1 > *pcchOut) {
1640 *pcchOut = strlenW(pszIn) + 1;
1643 strcpyW(pszOut, pszIn);
1644 *pcchOut = strlenW(pszOut);
1645 TRACE("returning copy, left alone\n");
1649 /*************************************************************************
1650 * UrlIsA [SHLWAPI.@]
1652 * Determine if a Url is of a certain class.
1655 * pszUrl [I] Url to check
1656 * Urlis [I] URLIS_ constant from "shlwapi.h"
1659 * TRUE if pszUrl belongs to the class type in Urlis.
1662 BOOL WINAPI UrlIsA(LPCSTR pszUrl, URLIS Urlis)
1668 TRACE("(%s %d)\n", debugstr_a(pszUrl), Urlis);
1673 base.cbSize = sizeof(base);
1674 res1 = ParseURLA(pszUrl, &base);
1675 if (res1) return FALSE; /* invalid scheme */
1676 switch (base.nScheme)
1678 case URL_SCHEME_MAILTO:
1679 case URL_SCHEME_SHELL:
1680 case URL_SCHEME_JAVASCRIPT:
1681 case URL_SCHEME_VBSCRIPT:
1682 case URL_SCHEME_ABOUT:
1688 return !StrCmpNA("file:", pszUrl, 5);
1690 case URLIS_DIRECTORY:
1691 last = pszUrl + strlen(pszUrl) - 1;
1692 return (last >= pszUrl && (*last == '/' || *last == '\\' ));
1695 return PathIsURLA(pszUrl);
1697 case URLIS_NOHISTORY:
1698 case URLIS_APPLIABLE:
1699 case URLIS_HASQUERY:
1701 FIXME("(%s %d): stub\n", debugstr_a(pszUrl), Urlis);
1706 /*************************************************************************
1707 * UrlIsW [SHLWAPI.@]
1711 BOOL WINAPI UrlIsW(LPCWSTR pszUrl, URLIS Urlis)
1713 static const WCHAR stemp[] = { 'f','i','l','e',':',0 };
1718 TRACE("(%s %d)\n", debugstr_w(pszUrl), Urlis);
1723 base.cbSize = sizeof(base);
1724 res1 = ParseURLW(pszUrl, &base);
1725 if (res1) return FALSE; /* invalid scheme */
1726 switch (base.nScheme)
1728 case URL_SCHEME_MAILTO:
1729 case URL_SCHEME_SHELL:
1730 case URL_SCHEME_JAVASCRIPT:
1731 case URL_SCHEME_VBSCRIPT:
1732 case URL_SCHEME_ABOUT:
1738 return !strncmpW(stemp, pszUrl, 5);
1740 case URLIS_DIRECTORY:
1741 last = pszUrl + strlenW(pszUrl) - 1;
1742 return (last >= pszUrl && (*last == '/' || *last == '\\'));
1745 return PathIsURLW(pszUrl);
1747 case URLIS_NOHISTORY:
1748 case URLIS_APPLIABLE:
1749 case URLIS_HASQUERY:
1751 FIXME("(%s %d): stub\n", debugstr_w(pszUrl), Urlis);
1756 /*************************************************************************
1757 * UrlIsNoHistoryA [SHLWAPI.@]
1759 * Determine if a Url should not be stored in the users history list.
1762 * pszUrl [I] Url to check
1765 * TRUE, if pszUrl should be excluded from the history list,
1768 BOOL WINAPI UrlIsNoHistoryA(LPCSTR pszUrl)
1770 return UrlIsA(pszUrl, URLIS_NOHISTORY);
1773 /*************************************************************************
1774 * UrlIsNoHistoryW [SHLWAPI.@]
1776 * See UrlIsNoHistoryA.
1778 BOOL WINAPI UrlIsNoHistoryW(LPCWSTR pszUrl)
1780 return UrlIsW(pszUrl, URLIS_NOHISTORY);
1783 /*************************************************************************
1784 * UrlIsOpaqueA [SHLWAPI.@]
1786 * Determine if a Url is opaque.
1789 * pszUrl [I] Url to check
1792 * TRUE if pszUrl is opaque,
1796 * An opaque Url is one that does not start with "<protocol>://".
1798 BOOL WINAPI UrlIsOpaqueA(LPCSTR pszUrl)
1800 return UrlIsA(pszUrl, URLIS_OPAQUE);
1803 /*************************************************************************
1804 * UrlIsOpaqueW [SHLWAPI.@]
1808 BOOL WINAPI UrlIsOpaqueW(LPCWSTR pszUrl)
1810 return UrlIsW(pszUrl, URLIS_OPAQUE);
1813 /*************************************************************************
1814 * Scans for characters of type "type" and when not matching found,
1815 * returns pointer to it and length in size.
1817 * Characters tested based on RFC 1738
1819 static LPCWSTR URL_ScanID(LPCWSTR start, LPDWORD size, WINE_URL_SCAN_TYPE type)
1821 static DWORD alwayszero = 0;
1830 if ( (islowerW(*start) && isalphaW(*start)) ||
1845 if ( isalphaW(*start) ||
1847 /* user/password only characters */
1852 /* *extra* characters */
1855 (*start == L'\'') ||
1859 /* *safe* characters */
1867 } else if (*start == L'%') {
1868 if (isxdigitW(*(start+1)) &&
1869 isxdigitW(*(start+2))) {
1881 if (isdigitW(*start)) {
1892 if (isalnumW(*start) ||
1894 (*start == L'.') ) {
1903 FIXME("unknown type %d\n", type);
1904 return (LPWSTR)&alwayszero;
1906 /* TRACE("scanned %ld characters next char %p<%c>\n",
1907 *size, start, *start); */
1911 /*************************************************************************
1912 * Attempt to parse URL into pieces.
1914 static LONG URL_ParseUrl(LPCWSTR pszUrl, WINE_PARSE_URL *pl)
1918 memset(pl, 0, sizeof(WINE_PARSE_URL));
1919 pl->pScheme = pszUrl;
1920 work = URL_ScanID(pl->pScheme, &pl->szScheme, SCHEME);
1921 if (!*work || (*work != L':')) goto ErrorExit;
1923 if ((*work != L'/') || (*(work+1) != L'/')) goto ErrorExit;
1924 pl->pUserName = work + 2;
1925 work = URL_ScanID(pl->pUserName, &pl->szUserName, USERPASS);
1926 if (*work == L':' ) {
1927 /* parse password */
1929 pl->pPassword = work;
1930 work = URL_ScanID(pl->pPassword, &pl->szPassword, USERPASS);
1931 if (*work != L'@') {
1932 /* what we just parsed must be the hostname and port
1933 * so reset pointers and clear then let it parse */
1934 pl->szUserName = pl->szPassword = 0;
1935 work = pl->pUserName - 1;
1936 pl->pUserName = pl->pPassword = 0;
1938 } else if (*work == L'@') {
1942 } else if (!*work || (*work == L'/') || (*work == L'.')) {
1943 /* what was parsed was hostname, so reset pointers and let it parse */
1944 pl->szUserName = pl->szPassword = 0;
1945 work = pl->pUserName - 1;
1946 pl->pUserName = pl->pPassword = 0;
1947 } else goto ErrorExit;
1949 /* now start parsing hostname or hostnumber */
1951 pl->pHostName = work;
1952 work = URL_ScanID(pl->pHostName, &pl->szHostName, HOST);
1953 if (*work == L':') {
1957 work = URL_ScanID(pl->pPort, &pl->szPort, PORT);
1959 if (*work == L'/') {
1960 /* see if query string */
1961 pl->pQuery = strchrW(work, L'?');
1962 if (pl->pQuery) pl->szQuery = strlenW(pl->pQuery);
1964 TRACE("parse successful: scheme=%p(%ld), user=%p(%ld), pass=%p(%ld), host=%p(%ld), port=%p(%ld), query=%p(%ld)\n",
1965 pl->pScheme, pl->szScheme,
1966 pl->pUserName, pl->szUserName,
1967 pl->pPassword, pl->szPassword,
1968 pl->pHostName, pl->szHostName,
1969 pl->pPort, pl->szPort,
1970 pl->pQuery, pl->szQuery);
1973 FIXME("failed to parse %s\n", debugstr_w(pszUrl));
1974 return E_INVALIDARG;
1977 /*************************************************************************
1978 * UrlGetPartA [SHLWAPI.@]
1980 * Retrieve part of a Url.
1983 * pszIn [I] Url to parse
1984 * pszOut [O] Destination for part of pszIn requested
1985 * pcchOut [I] Size of pszOut
1986 * [O] length of pszOut string EXLUDING '\0' if S_OK, otherwise
1987 * needed size of pszOut INCLUDING '\0'.
1988 * dwPart [I] URL_PART_ enum from "shlwapi.h"
1989 * dwFlags [I] URL_ flags from "shlwapi.h"
1992 * Success: S_OK. pszOut contains the part requested, pcchOut contains its length.
1993 * Failure: An HRESULT error code describing the error.
1995 HRESULT WINAPI UrlGetPartA(LPCSTR pszIn, LPSTR pszOut, LPDWORD pcchOut,
1996 DWORD dwPart, DWORD dwFlags)
1999 DWORD ret, len, len2;
2001 in = HeapAlloc(GetProcessHeap(), 0,
2002 (2*INTERNET_MAX_URL_LENGTH) * sizeof(WCHAR));
2003 out = in + INTERNET_MAX_URL_LENGTH;
2005 MultiByteToWideChar(0, 0, pszIn, -1, in, INTERNET_MAX_URL_LENGTH);
2007 len = INTERNET_MAX_URL_LENGTH;
2008 ret = UrlGetPartW(in, out, &len, dwPart, dwFlags);
2011 HeapFree(GetProcessHeap(), 0, in);
2015 len2 = WideCharToMultiByte(0, 0, out, len, 0, 0, 0, 0);
2016 if (len2 > *pcchOut) {
2018 HeapFree(GetProcessHeap(), 0, in);
2021 WideCharToMultiByte(0, 0, out, len+1, pszOut, *pcchOut, 0, 0);
2023 HeapFree(GetProcessHeap(), 0, in);
2027 /*************************************************************************
2028 * UrlGetPartW [SHLWAPI.@]
2032 HRESULT WINAPI UrlGetPartW(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut,
2033 DWORD dwPart, DWORD dwFlags)
2037 DWORD size, schsize;
2038 LPCWSTR addr, schaddr;
2040 TRACE("(%s %p %p(%ld) %08lx %08lx)\n",
2041 debugstr_w(pszIn), pszOut, pcchOut, *pcchOut, dwPart, dwFlags);
2043 ret = URL_ParseUrl(pszIn, &pl);
2045 schaddr = pl.pScheme;
2046 schsize = pl.szScheme;
2049 case URL_PART_SCHEME:
2050 if (!pl.szScheme) return E_INVALIDARG;
2055 case URL_PART_HOSTNAME:
2056 if (!pl.szHostName) return E_INVALIDARG;
2057 addr = pl.pHostName;
2058 size = pl.szHostName;
2061 case URL_PART_USERNAME:
2062 if (!pl.szUserName) return E_INVALIDARG;
2063 addr = pl.pUserName;
2064 size = pl.szUserName;
2067 case URL_PART_PASSWORD:
2068 if (!pl.szPassword) return E_INVALIDARG;
2069 addr = pl.pPassword;
2070 size = pl.szPassword;
2074 if (!pl.szPort) return E_INVALIDARG;
2079 case URL_PART_QUERY:
2080 if (!pl.szQuery) return E_INVALIDARG;
2086 return E_INVALIDARG;
2089 if (dwFlags == URL_PARTFLAG_KEEPSCHEME) {
2090 if (*pcchOut < schsize + size + 2) {
2091 *pcchOut = schsize + size + 2;
2094 memcpy(pszOut, schaddr, schsize*sizeof(WCHAR));
2095 pszOut[schsize] = ':';
2096 memcpy(pszOut+schsize+1, addr, size*sizeof(WCHAR));
2097 pszOut[schsize+1+size] = 0;
2098 *pcchOut = schsize + 1 + size;
2101 if (*pcchOut < size + 1) {*pcchOut = size+1; return E_POINTER;}
2102 memcpy(pszOut, addr, size*sizeof(WCHAR));
2106 TRACE("len=%ld %s\n", *pcchOut, debugstr_w(pszOut));
2111 /*************************************************************************
2112 * PathIsURLA [SHLWAPI.@]
2114 * Check if the given path is a Url.
2117 * lpszPath [I] Path to check.
2120 * TRUE if lpszPath is a Url.
2121 * FALSE if lpszPath is NULL or not a Url.
2123 BOOL WINAPI PathIsURLA(LPCSTR lpstrPath)
2128 if (!lpstrPath || !*lpstrPath) return FALSE;
2131 base.cbSize = sizeof(base);
2132 res1 = ParseURLA(lpstrPath, &base);
2133 return (base.nScheme != URL_SCHEME_INVALID);
2136 /*************************************************************************
2137 * PathIsURLW [SHLWAPI.@]
2141 BOOL WINAPI PathIsURLW(LPCWSTR lpstrPath)
2146 if (!lpstrPath || !*lpstrPath) return FALSE;
2149 base.cbSize = sizeof(base);
2150 res1 = ParseURLW(lpstrPath, &base);
2151 return (base.nScheme != URL_SCHEME_INVALID);
2154 /*************************************************************************
2155 * UrlCreateFromPathA [SHLWAPI.@]
2157 * See UrlCreateFromPathW
2159 HRESULT WINAPI UrlCreateFromPathA(LPCSTR pszPath, LPSTR pszUrl, LPDWORD pcchUrl, DWORD dwReserved)
2161 WCHAR bufW[INTERNET_MAX_URL_LENGTH];
2163 UNICODE_STRING pathW;
2165 DWORD lenW = sizeof(bufW)/sizeof(WCHAR), lenA;
2167 if(!RtlCreateUnicodeStringFromAsciiz(&pathW, pszPath))
2168 return E_INVALIDARG;
2169 if((ret = UrlCreateFromPathW(pathW.Buffer, urlW, &lenW, dwReserved)) == E_POINTER) {
2170 urlW = HeapAlloc(GetProcessHeap(), 0, lenW * sizeof(WCHAR));
2171 ret = UrlCreateFromPathW(pathW.Buffer, urlW, &lenW, dwReserved);
2173 if(ret == S_OK || ret == S_FALSE) {
2174 RtlUnicodeToMultiByteSize(&lenA, urlW, lenW * sizeof(WCHAR));
2175 if(*pcchUrl > lenA) {
2176 RtlUnicodeToMultiByteN(pszUrl, *pcchUrl - 1, &lenA, urlW, lenW * sizeof(WCHAR));
2180 *pcchUrl = lenA + 1;
2184 if(urlW != bufW) HeapFree(GetProcessHeap(), 0, urlW);
2185 RtlFreeUnicodeString(&pathW);
2189 /*************************************************************************
2190 * UrlCreateFromPathW [SHLWAPI.@]
2192 * Create a Url from a file path.
2195 * pszPath [I] Path to convert
2196 * pszUrl [O] Destination for the converted Url
2197 * pcchUrl [I/O] Length of pszUrl
2198 * dwReserved [I] Reserved, must be 0
2201 * Success: S_OK pszUrl contains the converted path, S_FALSE if the path is already a Url
2202 * Failure: An HRESULT error code.
2204 HRESULT WINAPI UrlCreateFromPathW(LPCWSTR pszPath, LPWSTR pszUrl, LPDWORD pcchUrl, DWORD dwReserved)
2209 WCHAR file_colonW[] = {'f','i','l','e',':',0};
2210 WCHAR three_slashesW[] = {'/','/','/',0};
2211 PARSEDURLW parsed_url;
2213 TRACE("(%s, %p, %p, 0x%08lx)\n", debugstr_w(pszPath), pszUrl, pcchUrl, dwReserved);
2215 /* Validate arguments */
2216 if (dwReserved != 0)
2217 return E_INVALIDARG;
2218 if (!pszUrl || !pcchUrl)
2219 return E_INVALIDARG;
2222 parsed_url.cbSize = sizeof(parsed_url);
2223 if(ParseURLW(pszPath, &parsed_url) == S_OK) {
2224 if(parsed_url.nScheme != URL_SCHEME_INVALID && parsed_url.cchProtocol > 1) {
2225 needed = strlenW(pszPath);
2226 if (needed >= *pcchUrl) {
2227 *pcchUrl = needed + 1;
2231 strcpyW(pszUrl, pszPath);
2237 pszNewUrl = HeapAlloc(GetProcessHeap(), 0, (strlenW(pszPath) + 9) * sizeof(WCHAR)); /* "file:///" + pszPath_len + 1 */
2238 strcpyW(pszNewUrl, file_colonW);
2239 if(isalphaW(pszPath[0]) && pszPath[1] == ':')
2240 strcatW(pszNewUrl, three_slashesW);
2241 strcatW(pszNewUrl, pszPath);
2242 ret = UrlEscapeW(pszNewUrl, pszUrl, pcchUrl, URL_ESCAPE_PERCENT);
2244 HeapFree(GetProcessHeap(), 0, pszNewUrl);
2248 /*************************************************************************
2249 * SHAutoComplete [SHLWAPI.@]
2251 * Enable auto-completion for an edit control.
2254 * hwndEdit [I] Handle of control to enable auto-completion for
2255 * dwFlags [I] SHACF_ flags from "shlwapi.h"
2258 * Success: S_OK. Auto-completion is enabled for the control.
2259 * Failure: An HRESULT error code indicating the error.
2261 HRESULT WINAPI SHAutoComplete(HWND hwndEdit, DWORD dwFlags)
2263 FIXME("SHAutoComplete stub\n");
2267 /*************************************************************************
2268 * MLBuildResURLA [SHLWAPI.405]
2270 * Create a Url pointing to a resource in a module.
2273 * lpszLibName [I] Name of the module containing the resource
2274 * hMod [I] Callers module handle
2275 * dwFlags [I] Undocumented flags for loading the module
2276 * lpszRes [I] Resource name
2277 * lpszDest [O] Destination for resulting Url
2278 * dwDestLen [I] Length of lpszDest
2281 * Success: S_OK. lpszDest constains the resource Url.
2282 * Failure: E_INVALIDARG, if any argument is invalid, or
2283 * E_FAIL if dwDestLen is too small.
2285 HRESULT WINAPI MLBuildResURLA(LPCSTR lpszLibName, HMODULE hMod, DWORD dwFlags,
2286 LPCSTR lpszRes, LPSTR lpszDest, DWORD dwDestLen)
2288 WCHAR szLibName[MAX_PATH], szRes[MAX_PATH], szDest[MAX_PATH];
2292 MultiByteToWideChar(CP_ACP, 0, lpszLibName, -1, szLibName, sizeof(szLibName)/sizeof(WCHAR));
2295 MultiByteToWideChar(CP_ACP, 0, lpszRes, -1, szRes, sizeof(szRes)/sizeof(WCHAR));
2297 if (dwDestLen > sizeof(szLibName)/sizeof(WCHAR))
2298 dwDestLen = sizeof(szLibName)/sizeof(WCHAR);
2300 hRet = MLBuildResURLW(lpszLibName ? szLibName : NULL, hMod, dwFlags,
2301 lpszRes ? szRes : NULL, lpszDest ? szDest : NULL, dwDestLen);
2302 if (SUCCEEDED(hRet) && lpszDest)
2303 WideCharToMultiByte(CP_ACP, 0, szDest, -1, lpszDest, dwDestLen, 0, 0);
2308 /*************************************************************************
2309 * MLBuildResURLA [SHLWAPI.406]
2311 * See MLBuildResURLA.
2313 HRESULT WINAPI MLBuildResURLW(LPCWSTR lpszLibName, HMODULE hMod, DWORD dwFlags,
2314 LPCWSTR lpszRes, LPWSTR lpszDest, DWORD dwDestLen)
2316 static const WCHAR szRes[] = { 'r','e','s',':','/','/','\0' };
2317 #define szResLen ((sizeof(szRes) - sizeof(WCHAR))/sizeof(WCHAR))
2318 HRESULT hRet = E_FAIL;
2320 TRACE("(%s,%p,0x%08lx,%s,%p,%ld)\n", debugstr_w(lpszLibName), hMod, dwFlags,
2321 debugstr_w(lpszRes), lpszDest, dwDestLen);
2323 if (!lpszLibName || !hMod || hMod == INVALID_HANDLE_VALUE || !lpszRes ||
2324 !lpszDest || (dwFlags && dwFlags != 2))
2325 return E_INVALIDARG;
2327 if (dwDestLen >= szResLen + 1)
2329 dwDestLen -= (szResLen + 1);
2330 memcpy(lpszDest, szRes, sizeof(szRes));
2332 hMod = MLLoadLibraryW(lpszLibName, hMod, dwFlags);
2336 WCHAR szBuff[MAX_PATH];
2339 len = GetModuleFileNameW(hMod, szBuff, sizeof(szBuff)/sizeof(WCHAR));
2340 if (len && len < sizeof(szBuff)/sizeof(WCHAR))
2342 DWORD dwPathLen = strlenW(szBuff) + 1;
2344 if (dwDestLen >= dwPathLen)
2348 dwDestLen -= dwPathLen;
2349 memcpy(lpszDest + szResLen, szBuff, dwPathLen * sizeof(WCHAR));
2351 dwResLen = strlenW(lpszRes) + 1;
2352 if (dwDestLen >= dwResLen + 1)
2354 lpszDest[szResLen + dwPathLen + dwResLen] = '/';
2355 memcpy(lpszDest + szResLen + dwPathLen, lpszRes, dwResLen * sizeof(WCHAR));
2360 MLFreeLibrary(hMod);