4 * Copyright 2000 Huw D M Davies for CodeWeavers.
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 #include "wine/port.h"
30 #include "wine/unicode.h"
34 #define NO_SHLWAPI_STREAM
36 #include "wine/debug.h"
38 HMODULE WINAPI MLLoadLibraryW(LPCWSTR,HMODULE,DWORD);
39 BOOL WINAPI MLFreeLibrary(HMODULE);
40 HRESULT WINAPI MLBuildResURLW(LPCWSTR,HMODULE,DWORD,LPCWSTR,LPWSTR,DWORD);
42 WINE_DEFAULT_DEBUG_CHANNEL(shell);
44 /* The following schemes were identified in the native version of
45 * SHLWAPI.DLL version 5.50
48 URL_SCHEME scheme_number;
52 static const SHL_2_inet_scheme shlwapi_schemes[] = {
53 {URL_SCHEME_FTP, "ftp"},
54 {URL_SCHEME_HTTP, "http"},
55 {URL_SCHEME_GOPHER, "gopher"},
56 {URL_SCHEME_MAILTO, "mailto"},
57 {URL_SCHEME_NEWS, "news"},
58 {URL_SCHEME_NNTP, "nntp"},
59 {URL_SCHEME_TELNET, "telnet"},
60 {URL_SCHEME_WAIS, "wais"},
61 {URL_SCHEME_FILE, "file"},
62 {URL_SCHEME_MK, "mk"},
63 {URL_SCHEME_HTTPS, "https"},
64 {URL_SCHEME_SHELL, "shell"},
65 {URL_SCHEME_SNEWS, "snews"},
66 {URL_SCHEME_LOCAL, "local"},
67 {URL_SCHEME_JAVASCRIPT, "javascript"},
68 {URL_SCHEME_VBSCRIPT, "vbscript"},
69 {URL_SCHEME_ABOUT, "about"},
70 {URL_SCHEME_RES, "res"},
75 LPCWSTR pScheme; /* [out] start of scheme */
76 DWORD szScheme; /* [out] size of scheme (until colon) */
77 LPCWSTR pUserName; /* [out] start of Username */
78 DWORD szUserName; /* [out] size of Username (until ":" or "@") */
79 LPCWSTR pPassword; /* [out] start of Password */
80 DWORD szPassword; /* [out] size of Password (until "@") */
81 LPCWSTR pHostName; /* [out] start of Hostname */
82 DWORD szHostName; /* [out] size of Hostname (until ":" or "/") */
83 LPCWSTR pPort; /* [out] start of Port */
84 DWORD szPort; /* [out] size of Port (until "/" or eos) */
85 LPCWSTR pQuery; /* [out] start of Query */
86 DWORD szQuery; /* [out] size of Query (until eos) */
96 static const CHAR hexDigits[] = "0123456789ABCDEF";
98 static const WCHAR fileW[] = {'f','i','l','e','\0'};
100 static const unsigned char HashDataLookup[256] = {
101 0x01, 0x0E, 0x6E, 0x19, 0x61, 0xAE, 0x84, 0x77, 0x8A, 0xAA, 0x7D, 0x76, 0x1B,
102 0xE9, 0x8C, 0x33, 0x57, 0xC5, 0xB1, 0x6B, 0xEA, 0xA9, 0x38, 0x44, 0x1E, 0x07,
103 0xAD, 0x49, 0xBC, 0x28, 0x24, 0x41, 0x31, 0xD5, 0x68, 0xBE, 0x39, 0xD3, 0x94,
104 0xDF, 0x30, 0x73, 0x0F, 0x02, 0x43, 0xBA, 0xD2, 0x1C, 0x0C, 0xB5, 0x67, 0x46,
105 0x16, 0x3A, 0x4B, 0x4E, 0xB7, 0xA7, 0xEE, 0x9D, 0x7C, 0x93, 0xAC, 0x90, 0xB0,
106 0xA1, 0x8D, 0x56, 0x3C, 0x42, 0x80, 0x53, 0x9C, 0xF1, 0x4F, 0x2E, 0xA8, 0xC6,
107 0x29, 0xFE, 0xB2, 0x55, 0xFD, 0xED, 0xFA, 0x9A, 0x85, 0x58, 0x23, 0xCE, 0x5F,
108 0x74, 0xFC, 0xC0, 0x36, 0xDD, 0x66, 0xDA, 0xFF, 0xF0, 0x52, 0x6A, 0x9E, 0xC9,
109 0x3D, 0x03, 0x59, 0x09, 0x2A, 0x9B, 0x9F, 0x5D, 0xA6, 0x50, 0x32, 0x22, 0xAF,
110 0xC3, 0x64, 0x63, 0x1A, 0x96, 0x10, 0x91, 0x04, 0x21, 0x08, 0xBD, 0x79, 0x40,
111 0x4D, 0x48, 0xD0, 0xF5, 0x82, 0x7A, 0x8F, 0x37, 0x69, 0x86, 0x1D, 0xA4, 0xB9,
112 0xC2, 0xC1, 0xEF, 0x65, 0xF2, 0x05, 0xAB, 0x7E, 0x0B, 0x4A, 0x3B, 0x89, 0xE4,
113 0x6C, 0xBF, 0xE8, 0x8B, 0x06, 0x18, 0x51, 0x14, 0x7F, 0x11, 0x5B, 0x5C, 0xFB,
114 0x97, 0xE1, 0xCF, 0x15, 0x62, 0x71, 0x70, 0x54, 0xE2, 0x12, 0xD6, 0xC7, 0xBB,
115 0x0D, 0x20, 0x5E, 0xDC, 0xE0, 0xD4, 0xF7, 0xCC, 0xC4, 0x2B, 0xF9, 0xEC, 0x2D,
116 0xF4, 0x6F, 0xB6, 0x99, 0x88, 0x81, 0x5A, 0xD9, 0xCA, 0x13, 0xA5, 0xE7, 0x47,
117 0xE6, 0x8E, 0x60, 0xE3, 0x3E, 0xB3, 0xF6, 0x72, 0xA2, 0x35, 0xA0, 0xD7, 0xCD,
118 0xB4, 0x2F, 0x6D, 0x2C, 0x26, 0x1F, 0x95, 0x87, 0x00, 0xD8, 0x34, 0x3F, 0x17,
119 0x25, 0x45, 0x27, 0x75, 0x92, 0xB8, 0xA3, 0xC8, 0xDE, 0xEB, 0xF8, 0xF3, 0xDB,
120 0x0A, 0x98, 0x83, 0x7B, 0xE5, 0xCB, 0x4C, 0x78, 0xD1 };
122 static BOOL URL_JustLocation(LPCWSTR str)
124 while(*str && (*str == L'/')) str++;
126 while (*str && ((*str == L'-') ||
128 isalnumW(*str))) str++;
129 if (*str == L'/') return FALSE;
135 /*************************************************************************
138 * Parse a Url into its constituent parts.
142 * y [O] Undocumented structure holding the parsed information
145 * Success: S_OK. y contains the parsed Url details.
146 * Failure: An HRESULT error code.
148 HRESULT WINAPI ParseURLA(LPCSTR x, PARSEDURLA *y)
151 const SHL_2_inet_scheme *inet_pro;
153 y->nScheme = URL_SCHEME_INVALID;
154 if (y->cbSize != sizeof(*y)) return E_INVALIDARG;
155 /* FIXME: leading white space generates error of 0x80041001 which
158 if (*x <= ' ') return 0x80041001;
164 y->cchProtocol = cnt;
173 /* check for no scheme in string start */
174 /* (apparently schemes *must* be larger than a single character) */
175 if ((*x == '\0') || (y->cchProtocol <= 1)) {
176 y->pszProtocol = NULL;
180 /* found scheme, set length of remainder */
181 y->cchSuffix = lstrlenA(y->pszSuffix);
183 /* see if known scheme and return indicator number */
184 y->nScheme = URL_SCHEME_UNKNOWN;
185 inet_pro = shlwapi_schemes;
186 while (inet_pro->scheme_name) {
187 if (!strncasecmp(inet_pro->scheme_name, y->pszProtocol,
188 min(y->cchProtocol, lstrlenA(inet_pro->scheme_name)))) {
189 y->nScheme = inet_pro->scheme_number;
197 /*************************************************************************
200 * Unicode version of ParseURLA.
202 HRESULT WINAPI ParseURLW(LPCWSTR x, PARSEDURLW *y)
205 const SHL_2_inet_scheme *inet_pro;
209 y->nScheme = URL_SCHEME_INVALID;
210 if (y->cbSize != sizeof(*y)) return E_INVALIDARG;
211 /* FIXME: leading white space generates error of 0x80041001 which
214 if (*x <= L' ') return 0x80041001;
220 y->cchProtocol = cnt;
229 /* check for no scheme in string start */
230 /* (apparently schemes *must* be larger than a single character) */
231 if ((*x == L'\0') || (y->cchProtocol <= 1)) {
232 y->pszProtocol = NULL;
236 /* found scheme, set length of remainder */
237 y->cchSuffix = lstrlenW(y->pszSuffix);
239 /* see if known scheme and return indicator number */
240 len = WideCharToMultiByte(0, 0, y->pszProtocol, y->cchProtocol, 0, 0, 0, 0);
241 cmpstr = 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;
333 WCHAR slash = dwFlags & URL_FILE_USE_PATHURL ? '\\' : '/';
335 TRACE("(%s %p %p 0x%08lx)\n", debugstr_w(pszUrl), pszCanonicalized,
336 pcchCanonicalized, dwFlags);
338 if(!pszUrl || !pszCanonicalized || !pcchCanonicalized)
341 nByteLen = (lstrlenW(pszUrl) + 1) * sizeof(WCHAR); /* length in bytes */
342 lpszUrlCpy = HeapAlloc(GetProcessHeap(), 0, nByteLen);
347 * 1 have 2[+] alnum 2,3
348 * 2 have scheme (found :) 4,6,3
349 * 3 failed (no location)
351 * 5 have 1[+] alnum 6,3
352 * 6 have location (found /) save root location
355 wk1 = (LPWSTR)pszUrl;
361 if (!isalnumW(*wk1)) {state = 3; break;}
363 if (!isalnumW(*wk1)) {state = 3; break;}
369 if (*wk1++ == L':') state = 2;
372 if (*wk1 != L'/') {state = 3; break;}
374 if (*wk1 != L'/') {state = 6; break;}
376 if(*wk1 == '/' && (dwFlags & URL_FILE_USE_PATHURL))
381 nWkLen = strlenW(wk1);
382 memcpy(wk2, wk1, (nWkLen + 1) * sizeof(WCHAR));
388 if(*mp == '/' || *mp == '\\')
394 if (!isalnumW(*wk1) && (*wk1 != L'-') && (*wk1 != L'.') && (*wk1 != ':'))
396 while(isalnumW(*wk1) || (*wk1 == L'-') || (*wk1 == L'.') || (*wk1 == ':'))
401 if (*wk1 != '/' && *wk1 != '\\') {state = 3; break;}
402 while(*wk1 == '/' || *wk1 == '\\') {
409 if(dwFlags & URL_DONT_SIMPLIFY) {
414 /* Now at root location, cannot back up any more. */
415 /* "root" will point at the '/' */
419 mp = strchrW(wk1, '/');
420 mp2 = strchrW(wk1, '\\');
421 if(mp2 && (!mp || mp2 < mp))
424 nWkLen = strlenW(wk1);
425 memcpy(wk2, wk1, (nWkLen + 1) * sizeof(WCHAR));
432 memcpy(wk2, wk1, nLen * sizeof(WCHAR));
440 TRACE("found '/.'\n");
441 if (wk1[1] == '/' || wk1[1] == '\\') {
442 /* case of /./ -> skip the ./ */
445 else if (wk1[1] == '.') {
446 /* found /.. look for next / */
447 TRACE("found '/..'\n");
448 if (wk1[2] == '/' || wk1[2] == '\\' ||wk1[2] == '?'
449 || wk1[2] == '#' || !wk1[2]) {
450 /* case /../ -> need to backup wk2 */
451 TRACE("found '/../'\n");
452 *(wk2-1) = L'\0'; /* set end of string */
453 mp = strrchrW(root, slash);
454 if (mp && (mp >= root)) {
455 /* found valid backup point */
457 if(wk1[2] != '/' && wk1[2] != '\\')
463 /* did not find point, restore '/' */
473 FIXME("how did we get here - state=%d\n", state);
474 HeapFree(GetProcessHeap(), 0, lpszUrlCpy);
478 TRACE("Simplified, orig <%s>, simple <%s>\n",
479 debugstr_w(pszUrl), debugstr_w(lpszUrlCpy));
481 nLen = lstrlenW(lpszUrlCpy);
482 while ((nLen > 0) && ((lpszUrlCpy[nLen-1] == '\r')||(lpszUrlCpy[nLen-1] == '\n')))
483 lpszUrlCpy[--nLen]=0;
485 if(dwFlags & (URL_UNESCAPE | URL_FILE_USE_PATHURL))
486 UrlUnescapeW(lpszUrlCpy, NULL, &nLen, URL_UNESCAPE_INPLACE);
488 if((EscapeFlags = dwFlags & (URL_ESCAPE_UNSAFE |
489 URL_ESCAPE_SPACES_ONLY |
491 URL_DONT_ESCAPE_EXTRA_INFO |
492 URL_ESCAPE_SEGMENT_ONLY ))) {
493 EscapeFlags &= ~URL_ESCAPE_UNSAFE;
494 hr = UrlEscapeW(lpszUrlCpy, pszCanonicalized, pcchCanonicalized,
496 } else { /* No escaping needed, just copy the string */
497 nLen = lstrlenW(lpszUrlCpy);
498 if(nLen < *pcchCanonicalized)
499 memcpy(pszCanonicalized, lpszUrlCpy, (nLen + 1)*sizeof(WCHAR));
504 *pcchCanonicalized = nLen;
507 HeapFree(GetProcessHeap(), 0, lpszUrlCpy);
510 TRACE("result %s\n", debugstr_w(pszCanonicalized));
515 /*************************************************************************
516 * UrlCombineA [SHLWAPI.@]
521 * pszBase [I] Base Url
522 * pszRelative [I] Url to combine with pszBase
523 * pszCombined [O] Destination for combined Url
524 * pcchCombined [O] Destination for length of pszCombined
525 * dwFlags [I] URL_ flags from "shlwapi.h"
528 * Success: S_OK. pszCombined contains the combined Url, pcchCombined
529 * contains its length.
530 * Failure: An HRESULT error code indicating the error.
532 HRESULT WINAPI UrlCombineA(LPCSTR pszBase, LPCSTR pszRelative,
533 LPSTR pszCombined, LPDWORD pcchCombined,
536 LPWSTR base, relative, combined;
537 DWORD ret, len, len2;
539 TRACE("(base %s, Relative %s, Combine size %ld, flags %08lx) using W version\n",
540 debugstr_a(pszBase),debugstr_a(pszRelative),
541 pcchCombined?*pcchCombined:0,dwFlags);
543 if(!pszBase || !pszRelative || !pcchCombined)
546 base = HeapAlloc(GetProcessHeap(), 0,
547 (3*INTERNET_MAX_URL_LENGTH) * sizeof(WCHAR));
548 relative = base + INTERNET_MAX_URL_LENGTH;
549 combined = relative + INTERNET_MAX_URL_LENGTH;
551 MultiByteToWideChar(0, 0, pszBase, -1, base, INTERNET_MAX_URL_LENGTH);
552 MultiByteToWideChar(0, 0, pszRelative, -1, relative, INTERNET_MAX_URL_LENGTH);
555 ret = UrlCombineW(base, relative, pszCombined?combined:NULL, &len, dwFlags);
558 HeapFree(GetProcessHeap(), 0, base);
562 len2 = WideCharToMultiByte(0, 0, combined, len, 0, 0, 0, 0);
563 if (len2 > *pcchCombined) {
564 *pcchCombined = len2;
565 HeapFree(GetProcessHeap(), 0, base);
568 WideCharToMultiByte(0, 0, combined, len+1, pszCombined, (*pcchCombined)+1,
570 *pcchCombined = len2;
571 HeapFree(GetProcessHeap(), 0, base);
575 /*************************************************************************
576 * UrlCombineW [SHLWAPI.@]
580 HRESULT WINAPI UrlCombineW(LPCWSTR pszBase, LPCWSTR pszRelative,
581 LPWSTR pszCombined, LPDWORD pcchCombined,
584 PARSEDURLW base, relative;
585 DWORD myflags, sizeloc = 0;
586 DWORD len, res1, res2, process_case = 0;
587 LPWSTR work, preliminary, mbase, mrelative;
588 static const WCHAR myfilestr[] = {'f','i','l','e',':','/','/','/','\0'};
589 static const WCHAR single_slash[] = {'/','\0'};
592 TRACE("(base %s, Relative %s, Combine size %ld, flags %08lx)\n",
593 debugstr_w(pszBase),debugstr_w(pszRelative),
594 pcchCombined?*pcchCombined:0,dwFlags);
596 if(!pszBase || !pszRelative || !pcchCombined)
599 base.cbSize = sizeof(base);
600 relative.cbSize = sizeof(relative);
602 /* Get space for duplicates of the input and the output */
603 preliminary = HeapAlloc(GetProcessHeap(), 0, (3*INTERNET_MAX_URL_LENGTH) *
605 mbase = preliminary + INTERNET_MAX_URL_LENGTH;
606 mrelative = mbase + INTERNET_MAX_URL_LENGTH;
607 *preliminary = L'\0';
609 /* Canonicalize the base input prior to looking for the scheme */
610 myflags = dwFlags & (URL_DONT_SIMPLIFY | URL_UNESCAPE);
611 len = INTERNET_MAX_URL_LENGTH;
612 ret = UrlCanonicalizeW(pszBase, mbase, &len, myflags);
614 /* Canonicalize the relative input prior to looking for the scheme */
615 len = INTERNET_MAX_URL_LENGTH;
616 ret = UrlCanonicalizeW(pszRelative, mrelative, &len, myflags);
618 /* See if the base has a scheme */
619 res1 = ParseURLW(mbase, &base);
621 /* if pszBase has no scheme, then return pszRelative */
622 TRACE("no scheme detected in Base\n");
627 /* get size of location field (if it exists) */
628 work = (LPWSTR)base.pszSuffix;
630 if (*work++ == L'/') {
631 if (*work++ == L'/') {
632 /* At this point have start of location and
633 * it ends at next '/' or end of string.
635 while(*work && (*work != L'/')) work++;
636 sizeloc = (DWORD)(work - base.pszSuffix);
640 /* Change .sizep2 to not have the last leaf in it,
641 * Note: we need to start after the location (if it exists)
643 work = strrchrW((base.pszSuffix+sizeloc), L'/');
645 len = (DWORD)(work - base.pszSuffix + 1);
646 base.cchSuffix = len;
650 * .pszSuffix points to location (starting with '//')
651 * .cchSuffix length of location (above) and rest less the last
653 * sizeloc length of location (above) up to but not including
657 res2 = ParseURLW(mrelative, &relative);
659 /* no scheme in pszRelative */
660 TRACE("no scheme detected in Relative\n");
661 relative.pszSuffix = mrelative; /* case 3,4,5 depends on this */
662 relative.cchSuffix = strlenW(mrelative);
663 if (*pszRelative == L':') {
664 /* case that is either left alone or uses pszBase */
665 if (dwFlags & URL_PLUGGABLE_PROTOCOL) {
672 if (isalnum(*mrelative) && (*(mrelative + 1) == L':')) {
673 /* case that becomes "file:///" */
674 strcpyW(preliminary, myfilestr);
678 if ((*mrelative == L'/') && (*(mrelative+1) == L'/')) {
679 /* pszRelative has location and rest */
683 if (*mrelative == L'/') {
684 /* case where pszRelative is root to location */
688 process_case = (*base.pszSuffix == L'/') ? 5 : 3;
692 /* handle cases where pszRelative has scheme */
693 if ((base.cchProtocol == relative.cchProtocol) &&
694 (strncmpW(base.pszProtocol, relative.pszProtocol, base.cchProtocol) == 0)) {
696 /* since the schemes are the same */
697 if ((*relative.pszSuffix == L'/') && (*(relative.pszSuffix+1) == L'/')) {
698 /* case where pszRelative replaces location and following */
702 if (*relative.pszSuffix == L'/') {
703 /* case where pszRelative is root to location */
707 /* case where scheme is followed by document path */
711 if ((*relative.pszSuffix == L'/') && (*(relative.pszSuffix+1) == L'/')) {
712 /* case where pszRelative replaces scheme, location,
713 * and following and handles PLUGGABLE
720 } while(FALSE); /* a litte trick to allow easy exit from nested if's */
724 switch (process_case) {
727 * Return pszRelative appended to what ever is in pszCombined,
728 * (which may the string "file:///"
730 strcatW(preliminary, mrelative);
734 * Same as case 1, but if URL_PLUGGABLE_PROTOCOL was specified
735 * and pszRelative starts with "//", then append a "/"
737 strcpyW(preliminary, mrelative);
738 if (!(dwFlags & URL_PLUGGABLE_PROTOCOL) &&
739 URL_JustLocation(relative.pszSuffix))
740 strcatW(preliminary, single_slash);
744 * Return the pszBase scheme with pszRelative. Basically
745 * keeps the scheme and replaces the domain and following.
747 memcpy(preliminary, base.pszProtocol, (base.cchProtocol + 1)*sizeof(WCHAR));
748 work = preliminary + base.cchProtocol + 1;
749 strcpyW(work, relative.pszSuffix);
750 if (!(dwFlags & URL_PLUGGABLE_PROTOCOL) &&
751 URL_JustLocation(relative.pszSuffix))
752 strcatW(work, single_slash);
756 * Return the pszBase scheme and location but everything
757 * after the location is pszRelative. (Replace document
760 memcpy(preliminary, base.pszProtocol, (base.cchProtocol+1+sizeloc)*sizeof(WCHAR));
761 work = preliminary + base.cchProtocol + 1 + sizeloc;
762 if (dwFlags & URL_PLUGGABLE_PROTOCOL)
764 strcpyW(work, relative.pszSuffix);
768 * Return the pszBase without its document (if any) and
769 * append pszRelative after its scheme.
771 memcpy(preliminary, base.pszProtocol,
772 (base.cchProtocol+1+base.cchSuffix)*sizeof(WCHAR));
773 work = preliminary + base.cchProtocol+1+base.cchSuffix - 1;
776 strcpyW(work, relative.pszSuffix);
780 FIXME("How did we get here????? process_case=%ld\n", process_case);
785 /* Reuse mrelative as temp storage as its already allocated and not needed anymore */
786 ret = UrlCanonicalizeW(preliminary, mrelative, pcchCombined, dwFlags);
787 if(SUCCEEDED(ret) && pszCombined) {
788 lstrcpyW(pszCombined, mrelative);
790 TRACE("return-%ld len=%ld, %s\n",
791 process_case, *pcchCombined, debugstr_w(pszCombined));
793 HeapFree(GetProcessHeap(), 0, preliminary);
797 /*************************************************************************
798 * UrlEscapeA [SHLWAPI.@]
801 HRESULT WINAPI UrlEscapeA(
807 WCHAR bufW[INTERNET_MAX_URL_LENGTH];
808 WCHAR *escapedW = bufW;
811 DWORD lenW = sizeof(bufW)/sizeof(WCHAR), lenA;
813 if(!RtlCreateUnicodeStringFromAsciiz(&urlW, pszUrl))
815 if((ret = UrlEscapeW(urlW.Buffer, escapedW, &lenW, dwFlags)) == E_POINTER) {
816 escapedW = HeapAlloc(GetProcessHeap(), 0, lenW * sizeof(WCHAR));
817 ret = UrlEscapeW(urlW.Buffer, escapedW, &lenW, dwFlags);
820 RtlUnicodeToMultiByteSize(&lenA, escapedW, lenW * sizeof(WCHAR));
821 if(*pcchEscaped > lenA) {
822 RtlUnicodeToMultiByteN(pszEscaped, *pcchEscaped - 1, &lenA, escapedW, lenW * sizeof(WCHAR));
823 pszEscaped[lenA] = 0;
826 *pcchEscaped = lenA + 1;
830 if(escapedW != bufW) HeapFree(GetProcessHeap(), 0, escapedW);
831 RtlFreeUnicodeString(&urlW);
835 #define WINE_URL_BASH_AS_SLASH 0x01
836 #define WINE_URL_COLLAPSE_SLASHES 0x02
837 #define WINE_URL_ESCAPE_SLASH 0x04
838 #define WINE_URL_ESCAPE_HASH 0x08
839 #define WINE_URL_ESCAPE_QUESTION 0x10
840 #define WINE_URL_STOP_ON_HASH 0x20
841 #define WINE_URL_STOP_ON_QUESTION 0x40
843 static inline BOOL URL_NeedEscapeW(WCHAR ch, DWORD dwFlags, DWORD int_flags)
849 if(dwFlags & URL_ESCAPE_SPACES_ONLY) {
856 if ((dwFlags & URL_ESCAPE_PERCENT) && (ch == '%'))
859 if (ch <= 31 || ch >= 127)
880 if (int_flags & WINE_URL_ESCAPE_SLASH) return TRUE;
884 if (int_flags & WINE_URL_ESCAPE_QUESTION) return TRUE;
888 if (int_flags & WINE_URL_ESCAPE_HASH) return TRUE;
898 /*************************************************************************
899 * UrlEscapeW [SHLWAPI.@]
901 * Converts unsafe characters in a Url into escape sequences.
904 * pszUrl [I] Url to modify
905 * pszEscaped [O] Destination for modified Url
906 * pcchEscaped [I/O] Length of pszUrl, destination for length of pszEscaped
907 * dwFlags [I] URL_ flags from "shlwapi.h"
910 * Success: S_OK. pszEscaped contains the escaped Url, pcchEscaped
911 * contains its length.
912 * Failure: E_POINTER, if pszEscaped is not large enough. In this case
913 * pcchEscaped is set to the required length.
915 * Converts unsafe characters into their escape sequences.
918 * - By default this function stops converting at the first '?' or
920 * - If dwFlags contains URL_ESCAPE_SPACES_ONLY then only spaces are
921 * converted, but the conversion continues past a '?' or '#'.
922 * - Note that this function did not work well (or at all) in shlwapi version 4.
925 * Only the following flags are implemented:
926 *| URL_ESCAPE_SPACES_ONLY
927 *| URL_DONT_ESCAPE_EXTRA_INFO
928 *| URL_ESCAPE_SEGMENT_ONLY
929 *| URL_ESCAPE_PERCENT
931 HRESULT WINAPI UrlEscapeW(
938 DWORD needed = 0, ret;
939 BOOL stop_escaping = FALSE;
940 WCHAR next[5], *dst = pszEscaped;
942 PARSEDURLW parsed_url;
945 static const WCHAR localhost[] = {'l','o','c','a','l','h','o','s','t',0};
947 TRACE("(%s %p %p 0x%08lx)\n", debugstr_w(pszUrl), pszEscaped,
948 pcchEscaped, dwFlags);
950 if(!pszUrl || !pszEscaped || !pcchEscaped)
953 if(dwFlags & ~(URL_ESCAPE_SPACES_ONLY |
954 URL_ESCAPE_SEGMENT_ONLY |
955 URL_DONT_ESCAPE_EXTRA_INFO |
957 FIXME("Unimplemented flags: %08lx\n", dwFlags);
960 if (dwFlags & URL_ESCAPE_SPACES_ONLY)
961 /* if SPACES_ONLY specified, reset the other controls */
962 dwFlags &= ~(URL_DONT_ESCAPE_EXTRA_INFO |
964 URL_ESCAPE_SEGMENT_ONLY);
967 /* if SPACES_ONLY *not* specified the assume DONT_ESCAPE_EXTRA_INFO */
968 dwFlags |= URL_DONT_ESCAPE_EXTRA_INFO;
972 if(dwFlags & URL_ESCAPE_SEGMENT_ONLY) {
973 int_flags = WINE_URL_ESCAPE_QUESTION | WINE_URL_ESCAPE_HASH | WINE_URL_ESCAPE_SLASH;
975 parsed_url.cbSize = sizeof(parsed_url);
976 if(ParseURLW(pszUrl, &parsed_url) != S_OK)
977 parsed_url.nScheme = URL_SCHEME_INVALID;
979 TRACE("scheme = %d (%s)\n", parsed_url.nScheme, debugstr_wn(parsed_url.pszProtocol, parsed_url.cchProtocol));
981 if(dwFlags & URL_DONT_ESCAPE_EXTRA_INFO)
982 int_flags = WINE_URL_STOP_ON_HASH | WINE_URL_STOP_ON_QUESTION;
984 switch(parsed_url.nScheme) {
985 case URL_SCHEME_FILE:
986 int_flags |= WINE_URL_BASH_AS_SLASH | WINE_URL_COLLAPSE_SLASHES | WINE_URL_ESCAPE_HASH;
987 int_flags &= ~WINE_URL_STOP_ON_HASH;
990 case URL_SCHEME_HTTP:
991 case URL_SCHEME_HTTPS:
992 int_flags |= WINE_URL_BASH_AS_SLASH;
993 if(parsed_url.pszSuffix[0] != '/' && parsed_url.pszSuffix[0] != '\\')
994 int_flags |= WINE_URL_ESCAPE_SLASH;
997 case URL_SCHEME_MAILTO:
998 int_flags |= WINE_URL_ESCAPE_SLASH | WINE_URL_ESCAPE_QUESTION | WINE_URL_ESCAPE_HASH;
999 int_flags &= ~(WINE_URL_STOP_ON_QUESTION | WINE_URL_STOP_ON_HASH);
1002 case URL_SCHEME_INVALID:
1005 case URL_SCHEME_FTP:
1007 if(parsed_url.pszSuffix[0] != '/')
1008 int_flags |= WINE_URL_ESCAPE_SLASH;
1013 for(src = pszUrl; *src; ) {
1017 if((int_flags & WINE_URL_COLLAPSE_SLASHES) && src == pszUrl + parsed_url.cchProtocol + 1) {
1018 int localhost_len = sizeof(localhost)/sizeof(WCHAR) - 1;
1019 while(cur == '/' || cur == '\\') {
1023 if(slashes == 2 && !strncmpiW(src, localhost, localhost_len)) { /* file://localhost/ -> file:/// */
1024 if(*(src + localhost_len) == '/' || *(src + localhost_len) == '\\')
1025 src += localhost_len + 1;
1032 next[0] = next[1] = next[2] = '/';
1039 next[0] = next[1] = '/';
1046 if(cur == '#' && (int_flags & WINE_URL_STOP_ON_HASH))
1047 stop_escaping = TRUE;
1049 if(cur == '?' && (int_flags & WINE_URL_STOP_ON_QUESTION))
1050 stop_escaping = TRUE;
1052 if(cur == '\\' && (int_flags & WINE_URL_BASH_AS_SLASH) && !stop_escaping) cur = '/';
1054 if(URL_NeedEscapeW(cur, dwFlags, int_flags) && stop_escaping == FALSE) {
1056 next[1] = hexDigits[(cur >> 4) & 0xf];
1057 next[2] = hexDigits[cur & 0xf];
1066 if(needed + len <= *pcchEscaped) {
1067 memcpy(dst, next, len*sizeof(WCHAR));
1073 if(needed < *pcchEscaped) {
1077 needed++; /* add one for the '\0' */
1080 *pcchEscaped = needed;
1085 /*************************************************************************
1086 * UrlUnescapeA [SHLWAPI.@]
1088 * Converts Url escape sequences back to ordinary characters.
1091 * pszUrl [I/O] Url to convert
1092 * pszUnescaped [O] Destination for converted Url
1093 * pcchUnescaped [I/O] Size of output string
1094 * dwFlags [I] URL_ESCAPE_ Flags from "shlwapi.h"
1097 * Success: S_OK. The converted value is in pszUnescaped, or in pszUrl if
1098 * dwFlags includes URL_ESCAPE_INPLACE.
1099 * Failure: E_POINTER if the converted Url is bigger than pcchUnescaped. In
1100 * this case pcchUnescaped is set to the size required.
1102 * If dwFlags includes URL_DONT_ESCAPE_EXTRA_INFO, the conversion stops at
1103 * the first occurrence of either a '?' or '#' character.
1105 HRESULT WINAPI UrlUnescapeA(
1108 LPDWORD pcchUnescaped,
1115 BOOL stop_unescaping = FALSE;
1117 TRACE("(%s, %p, %p, 0x%08lx)\n", debugstr_a(pszUrl), pszUnescaped,
1118 pcchUnescaped, dwFlags);
1120 if(!pszUrl || (!pszUnescaped && !(dwFlags & URL_UNESCAPE_INPLACE)) || !pcchUnescaped)
1121 return E_INVALIDARG;
1123 if(dwFlags & URL_UNESCAPE_INPLACE)
1128 for(src = pszUrl, needed = 0; *src; src++, needed++) {
1129 if(dwFlags & URL_DONT_UNESCAPE_EXTRA_INFO &&
1130 (*src == '#' || *src == '?')) {
1131 stop_unescaping = TRUE;
1133 } else if(*src == '%' && isxdigit(*(src + 1)) && isxdigit(*(src + 2))
1134 && stop_unescaping == FALSE) {
1137 memcpy(buf, src + 1, 2);
1139 ih = strtol(buf, NULL, 16);
1141 src += 2; /* Advance to end of escape */
1145 if(dwFlags & URL_UNESCAPE_INPLACE || needed < *pcchUnescaped)
1149 if(dwFlags & URL_UNESCAPE_INPLACE || needed < *pcchUnescaped) {
1153 needed++; /* add one for the '\0' */
1156 if(!(dwFlags & URL_UNESCAPE_INPLACE))
1157 *pcchUnescaped = needed;
1160 TRACE("result %s\n", (dwFlags & URL_UNESCAPE_INPLACE) ?
1161 debugstr_a(pszUrl) : debugstr_a(pszUnescaped));
1167 /*************************************************************************
1168 * UrlUnescapeW [SHLWAPI.@]
1172 HRESULT WINAPI UrlUnescapeW(
1174 LPWSTR pszUnescaped,
1175 LPDWORD pcchUnescaped,
1182 BOOL stop_unescaping = FALSE;
1184 TRACE("(%s, %p, %p, 0x%08lx)\n", debugstr_w(pszUrl), pszUnescaped,
1185 pcchUnescaped, dwFlags);
1187 if(!pszUrl || (!pszUnescaped && !(dwFlags & URL_UNESCAPE_INPLACE))|| !pcchUnescaped)
1188 return E_INVALIDARG;
1190 if(dwFlags & URL_UNESCAPE_INPLACE)
1195 for(src = pszUrl, needed = 0; *src; src++, needed++) {
1196 if(dwFlags & URL_DONT_UNESCAPE_EXTRA_INFO &&
1197 (*src == L'#' || *src == L'?')) {
1198 stop_unescaping = TRUE;
1200 } else if(*src == L'%' && isxdigitW(*(src + 1)) && isxdigitW(*(src + 2))
1201 && stop_unescaping == FALSE) {
1203 WCHAR buf[5] = {'0','x',0};
1204 memcpy(buf + 2, src + 1, 2*sizeof(WCHAR));
1206 StrToIntExW(buf, STIF_SUPPORT_HEX, &ih);
1208 src += 2; /* Advance to end of escape */
1212 if(dwFlags & URL_UNESCAPE_INPLACE || needed < *pcchUnescaped)
1216 if(dwFlags & URL_UNESCAPE_INPLACE || needed < *pcchUnescaped) {
1220 needed++; /* add one for the '\0' */
1223 if(!(dwFlags & URL_UNESCAPE_INPLACE))
1224 *pcchUnescaped = needed;
1227 TRACE("result %s\n", (dwFlags & URL_UNESCAPE_INPLACE) ?
1228 debugstr_w(pszUrl) : debugstr_w(pszUnescaped));
1234 /*************************************************************************
1235 * UrlGetLocationA [SHLWAPI.@]
1237 * Get the location from a Url.
1240 * pszUrl [I] Url to get the location from
1243 * A pointer to the start of the location in pszUrl, or NULL if there is
1247 * - MSDN erroneously states that "The location is the segment of the Url
1248 * starting with a '?' or '#' character". Neither V4 nor V5 of shlwapi.dll
1249 * stop at '?' and always return a NULL in this case.
1250 * - MSDN also erroneously states that "If a file URL has a query string,
1251 * the returned string is the query string". In all tested cases, if the
1252 * Url starts with "fi" then a NULL is returned. V5 gives the following results:
1255 *| NULL file://aa/b/cd#hohoh
1256 *| #hohoh http://aa/b/cd#hohoh
1257 *| NULL fi://aa/b/cd#hohoh
1258 *| #hohoh ff://aa/b/cd#hohoh
1260 LPCSTR WINAPI UrlGetLocationA(
1266 base.cbSize = sizeof(base);
1267 res1 = ParseURLA(pszUrl, &base);
1268 if (res1) return NULL; /* invalid scheme */
1270 /* if scheme is file: then never return pointer */
1271 if (strncmp(base.pszProtocol, "file", min(4,base.cchProtocol)) == 0) return NULL;
1273 /* Look for '#' and return its addr */
1274 return strchr(base.pszSuffix, '#');
1277 /*************************************************************************
1278 * UrlGetLocationW [SHLWAPI.@]
1280 * See UrlGetLocationA.
1282 LPCWSTR WINAPI UrlGetLocationW(
1288 base.cbSize = sizeof(base);
1289 res1 = ParseURLW(pszUrl, &base);
1290 if (res1) return NULL; /* invalid scheme */
1292 /* if scheme is file: then never return pointer */
1293 if (strncmpW(base.pszProtocol, fileW, min(4,base.cchProtocol)) == 0) return NULL;
1295 /* Look for '#' and return its addr */
1296 return strchrW(base.pszSuffix, L'#');
1299 /*************************************************************************
1300 * UrlCompareA [SHLWAPI.@]
1305 * pszUrl1 [I] First Url to compare
1306 * pszUrl2 [I] Url to compare to pszUrl1
1307 * fIgnoreSlash [I] TRUE = compare only up to a final slash
1310 * less than zero, zero, or greater than zero indicating pszUrl2 is greater
1311 * than, equal to, or less than pszUrl1 respectively.
1313 INT WINAPI UrlCompareA(
1318 INT ret, len, len1, len2;
1321 return strcmp(pszUrl1, pszUrl2);
1322 len1 = strlen(pszUrl1);
1323 if (pszUrl1[len1-1] == '/') len1--;
1324 len2 = strlen(pszUrl2);
1325 if (pszUrl2[len2-1] == '/') len2--;
1327 return strncmp(pszUrl1, pszUrl2, len1);
1328 len = min(len1, len2);
1329 ret = strncmp(pszUrl1, pszUrl2, len);
1330 if (ret) return ret;
1331 if (len1 > len2) return 1;
1335 /*************************************************************************
1336 * UrlCompareW [SHLWAPI.@]
1340 INT WINAPI UrlCompareW(
1346 size_t len, len1, len2;
1349 return strcmpW(pszUrl1, pszUrl2);
1350 len1 = strlenW(pszUrl1);
1351 if (pszUrl1[len1-1] == '/') len1--;
1352 len2 = strlenW(pszUrl2);
1353 if (pszUrl2[len2-1] == '/') len2--;
1355 return strncmpW(pszUrl1, pszUrl2, len1);
1356 len = min(len1, len2);
1357 ret = strncmpW(pszUrl1, pszUrl2, len);
1358 if (ret) return ret;
1359 if (len1 > len2) return 1;
1363 /*************************************************************************
1364 * HashData [SHLWAPI.@]
1366 * Hash an input block into a variable sized digest.
1369 * lpSrc [I] Input block
1370 * nSrcLen [I] Length of lpSrc
1371 * lpDest [I] Output for hash digest
1372 * nDestLen [I] Length of lpDest
1375 * Success: TRUE. lpDest is filled with the computed hash value.
1376 * Failure: FALSE, if any argument is invalid.
1378 HRESULT WINAPI HashData(const unsigned char *lpSrc, DWORD nSrcLen,
1379 unsigned char *lpDest, DWORD nDestLen)
1381 INT srcCount = nSrcLen - 1, destCount = nDestLen - 1;
1383 if (IsBadReadPtr(lpSrc, nSrcLen) ||
1384 IsBadWritePtr(lpDest, nDestLen))
1385 return E_INVALIDARG;
1387 while (destCount >= 0)
1389 lpDest[destCount] = (destCount & 0xff);
1393 while (srcCount >= 0)
1395 destCount = nDestLen - 1;
1396 while (destCount >= 0)
1398 lpDest[destCount] = HashDataLookup[lpSrc[srcCount] ^ lpDest[destCount]];
1406 /*************************************************************************
1407 * UrlHashA [SHLWAPI.@]
1409 * Produce a Hash from a Url.
1412 * pszUrl [I] Url to hash
1413 * lpDest [O] Destinationh for hash
1414 * nDestLen [I] Length of lpDest
1417 * Success: S_OK. lpDest is filled with the computed hash value.
1418 * Failure: E_INVALIDARG, if any argument is invalid.
1420 HRESULT WINAPI UrlHashA(LPCSTR pszUrl, unsigned char *lpDest, DWORD nDestLen)
1422 if (IsBadStringPtrA(pszUrl, -1) || IsBadWritePtr(lpDest, nDestLen))
1423 return E_INVALIDARG;
1425 HashData((const BYTE*)pszUrl, (int)strlen(pszUrl), lpDest, nDestLen);
1429 /*************************************************************************
1430 * UrlHashW [SHLWAPI.@]
1434 HRESULT WINAPI UrlHashW(LPCWSTR pszUrl, unsigned char *lpDest, DWORD nDestLen)
1436 char szUrl[MAX_PATH];
1438 TRACE("(%s,%p,%ld)\n",debugstr_w(pszUrl), lpDest, nDestLen);
1440 if (IsBadStringPtrW(pszUrl, -1) || IsBadWritePtr(lpDest, nDestLen))
1441 return E_INVALIDARG;
1443 /* Win32 hashes the data as an ASCII string, presumably so that both A+W
1444 * return the same digests for the same URL.
1446 WideCharToMultiByte(0, 0, pszUrl, -1, szUrl, MAX_PATH, 0, 0);
1447 HashData((const BYTE*)szUrl, (int)strlen(szUrl), lpDest, nDestLen);
1451 /*************************************************************************
1452 * UrlApplySchemeA [SHLWAPI.@]
1454 * Apply a scheme to a Url.
1457 * pszIn [I] Url to apply scheme to
1458 * pszOut [O] Destination for modified Url
1459 * pcchOut [I/O] Length of pszOut/destination for length of pszOut
1460 * dwFlags [I] URL_ flags from "shlwapi.h"
1463 * Success: S_OK: pszOut contains the modified Url, pcchOut contains its length.
1464 * Failure: An HRESULT error code describing the error.
1466 HRESULT WINAPI UrlApplySchemeA(LPCSTR pszIn, LPSTR pszOut, LPDWORD pcchOut, DWORD dwFlags)
1469 DWORD ret, len, len2;
1471 TRACE("(in %s, out size %ld, flags %08lx) using W version\n",
1472 debugstr_a(pszIn), *pcchOut, dwFlags);
1474 in = HeapAlloc(GetProcessHeap(), 0,
1475 (2*INTERNET_MAX_URL_LENGTH) * sizeof(WCHAR));
1476 out = in + INTERNET_MAX_URL_LENGTH;
1478 MultiByteToWideChar(0, 0, pszIn, -1, in, INTERNET_MAX_URL_LENGTH);
1479 len = INTERNET_MAX_URL_LENGTH;
1481 ret = UrlApplySchemeW(in, out, &len, dwFlags);
1482 if ((ret != S_OK) && (ret != S_FALSE)) {
1483 HeapFree(GetProcessHeap(), 0, in);
1487 len2 = WideCharToMultiByte(0, 0, out, len+1, 0, 0, 0, 0);
1488 if (len2 > *pcchOut) {
1490 HeapFree(GetProcessHeap(), 0, in);
1493 WideCharToMultiByte(0, 0, out, len+1, pszOut, *pcchOut, 0, 0);
1495 HeapFree(GetProcessHeap(), 0, in);
1499 static HRESULT URL_GuessScheme(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut)
1504 DWORD value_len, data_len, dwType, i;
1505 WCHAR reg_path[MAX_PATH];
1506 WCHAR value[MAX_PATH], data[MAX_PATH];
1509 MultiByteToWideChar(0, 0,
1510 "Software\\Microsoft\\Windows\\CurrentVersion\\URL\\Prefixes",
1511 -1, reg_path, MAX_PATH);
1512 RegOpenKeyExW(HKEY_LOCAL_MACHINE, reg_path, 0, 1, &newkey);
1514 while(value_len = data_len = MAX_PATH,
1515 RegEnumValueW(newkey, index, value, &value_len,
1516 0, &dwType, (LPVOID)data, &data_len) == 0) {
1517 TRACE("guess %d %s is %s\n",
1518 index, debugstr_w(value), debugstr_w(data));
1521 for(i=0; i<value_len; i++) {
1524 /* remember that TRUE is not-equal */
1525 j = ChrCmpIW(Wxx, Wyy);
1528 if ((i == value_len) && !j) {
1529 if (strlenW(data) + strlenW(pszIn) + 1 > *pcchOut) {
1530 *pcchOut = strlenW(data) + strlenW(pszIn) + 1;
1531 RegCloseKey(newkey);
1534 strcpyW(pszOut, data);
1535 strcatW(pszOut, pszIn);
1536 *pcchOut = strlenW(pszOut);
1537 TRACE("matched and set to %s\n", debugstr_w(pszOut));
1538 RegCloseKey(newkey);
1543 RegCloseKey(newkey);
1547 static HRESULT URL_ApplyDefault(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut)
1550 DWORD data_len, dwType;
1551 WCHAR reg_path[MAX_PATH];
1552 WCHAR value[MAX_PATH], data[MAX_PATH];
1554 /* get and prepend default */
1555 MultiByteToWideChar(0, 0,
1556 "Software\\Microsoft\\Windows\\CurrentVersion\\URL\\DefaultPrefix",
1557 -1, reg_path, MAX_PATH);
1558 RegOpenKeyExW(HKEY_LOCAL_MACHINE, reg_path, 0, 1, &newkey);
1559 data_len = MAX_PATH;
1562 RegQueryValueExW(newkey, value, 0, &dwType, (LPBYTE)data, &data_len);
1563 RegCloseKey(newkey);
1564 if (strlenW(data) + strlenW(pszIn) + 1 > *pcchOut) {
1565 *pcchOut = strlenW(data) + strlenW(pszIn) + 1;
1568 strcpyW(pszOut, data);
1569 strcatW(pszOut, pszIn);
1570 *pcchOut = strlenW(pszOut);
1571 TRACE("used default %s\n", debugstr_w(pszOut));
1575 /*************************************************************************
1576 * UrlApplySchemeW [SHLWAPI.@]
1578 * See UrlApplySchemeA.
1580 HRESULT WINAPI UrlApplySchemeW(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut, DWORD dwFlags)
1582 PARSEDURLW in_scheme;
1586 TRACE("(in %s, out size %ld, flags %08lx)\n",
1587 debugstr_w(pszIn), *pcchOut, dwFlags);
1589 if (dwFlags & URL_APPLY_GUESSFILE) {
1590 FIXME("(%s %p %p(%ld) 0x%08lx): stub URL_APPLY_GUESSFILE not implemented\n",
1591 debugstr_w(pszIn), pszOut, pcchOut, *pcchOut, dwFlags);
1592 strcpyW(pszOut, pszIn);
1593 *pcchOut = strlenW(pszOut);
1597 in_scheme.cbSize = sizeof(in_scheme);
1598 /* See if the base has a scheme */
1599 res1 = ParseURLW(pszIn, &in_scheme);
1601 /* no scheme in input, need to see if we need to guess */
1602 if (dwFlags & URL_APPLY_GUESSSCHEME) {
1603 if ((ret = URL_GuessScheme(pszIn, pszOut, pcchOut)) != -1)
1608 /* we have a scheme, see if valid (known scheme) */
1609 if (in_scheme.nScheme) {
1610 /* have valid scheme, so just copy and exit */
1611 if (strlenW(pszIn) + 1 > *pcchOut) {
1612 *pcchOut = strlenW(pszIn) + 1;
1615 strcpyW(pszOut, pszIn);
1616 *pcchOut = strlenW(pszOut);
1617 TRACE("valid scheme, returing copy\n");
1622 /* If we are here, then either invalid scheme,
1623 * or no scheme and can't/failed guess.
1625 if ( ( ((res1 == 0) && (dwFlags & URL_APPLY_FORCEAPPLY)) ||
1627 (dwFlags & URL_APPLY_DEFAULT)) {
1628 /* find and apply default scheme */
1629 return URL_ApplyDefault(pszIn, pszOut, pcchOut);
1632 /* just copy and give proper return code */
1633 if (strlenW(pszIn) + 1 > *pcchOut) {
1634 *pcchOut = strlenW(pszIn) + 1;
1637 strcpyW(pszOut, pszIn);
1638 *pcchOut = strlenW(pszOut);
1639 TRACE("returning copy, left alone\n");
1643 /*************************************************************************
1644 * UrlIsA [SHLWAPI.@]
1646 * Determine if a Url is of a certain class.
1649 * pszUrl [I] Url to check
1650 * Urlis [I] URLIS_ constant from "shlwapi.h"
1653 * TRUE if pszUrl belongs to the class type in Urlis.
1656 BOOL WINAPI UrlIsA(LPCSTR pszUrl, URLIS Urlis)
1662 TRACE("(%s %d)\n", debugstr_a(pszUrl), Urlis);
1667 base.cbSize = sizeof(base);
1668 res1 = ParseURLA(pszUrl, &base);
1669 if (res1) return FALSE; /* invalid scheme */
1670 switch (base.nScheme)
1672 case URL_SCHEME_MAILTO:
1673 case URL_SCHEME_SHELL:
1674 case URL_SCHEME_JAVASCRIPT:
1675 case URL_SCHEME_VBSCRIPT:
1676 case URL_SCHEME_ABOUT:
1682 return !StrCmpNA("file:", pszUrl, 5);
1684 case URLIS_DIRECTORY:
1685 last = pszUrl + strlen(pszUrl) - 1;
1686 return (last >= pszUrl && (*last == '/' || *last == '\\' ));
1689 return PathIsURLA(pszUrl);
1691 case URLIS_NOHISTORY:
1692 case URLIS_APPLIABLE:
1693 case URLIS_HASQUERY:
1695 FIXME("(%s %d): stub\n", debugstr_a(pszUrl), Urlis);
1700 /*************************************************************************
1701 * UrlIsW [SHLWAPI.@]
1705 BOOL WINAPI UrlIsW(LPCWSTR pszUrl, URLIS Urlis)
1707 static const WCHAR stemp[] = { 'f','i','l','e',':',0 };
1712 TRACE("(%s %d)\n", debugstr_w(pszUrl), Urlis);
1717 base.cbSize = sizeof(base);
1718 res1 = ParseURLW(pszUrl, &base);
1719 if (res1) return FALSE; /* invalid scheme */
1720 switch (base.nScheme)
1722 case URL_SCHEME_MAILTO:
1723 case URL_SCHEME_SHELL:
1724 case URL_SCHEME_JAVASCRIPT:
1725 case URL_SCHEME_VBSCRIPT:
1726 case URL_SCHEME_ABOUT:
1732 return !strncmpW(stemp, pszUrl, 5);
1734 case URLIS_DIRECTORY:
1735 last = pszUrl + strlenW(pszUrl) - 1;
1736 return (last >= pszUrl && (*last == '/' || *last == '\\'));
1739 return PathIsURLW(pszUrl);
1741 case URLIS_NOHISTORY:
1742 case URLIS_APPLIABLE:
1743 case URLIS_HASQUERY:
1745 FIXME("(%s %d): stub\n", debugstr_w(pszUrl), Urlis);
1750 /*************************************************************************
1751 * UrlIsNoHistoryA [SHLWAPI.@]
1753 * Determine if a Url should not be stored in the users history list.
1756 * pszUrl [I] Url to check
1759 * TRUE, if pszUrl should be excluded from the history list,
1762 BOOL WINAPI UrlIsNoHistoryA(LPCSTR pszUrl)
1764 return UrlIsA(pszUrl, URLIS_NOHISTORY);
1767 /*************************************************************************
1768 * UrlIsNoHistoryW [SHLWAPI.@]
1770 * See UrlIsNoHistoryA.
1772 BOOL WINAPI UrlIsNoHistoryW(LPCWSTR pszUrl)
1774 return UrlIsW(pszUrl, URLIS_NOHISTORY);
1777 /*************************************************************************
1778 * UrlIsOpaqueA [SHLWAPI.@]
1780 * Determine if a Url is opaque.
1783 * pszUrl [I] Url to check
1786 * TRUE if pszUrl is opaque,
1790 * An opaque Url is one that does not start with "<protocol>://".
1792 BOOL WINAPI UrlIsOpaqueA(LPCSTR pszUrl)
1794 return UrlIsA(pszUrl, URLIS_OPAQUE);
1797 /*************************************************************************
1798 * UrlIsOpaqueW [SHLWAPI.@]
1802 BOOL WINAPI UrlIsOpaqueW(LPCWSTR pszUrl)
1804 return UrlIsW(pszUrl, URLIS_OPAQUE);
1807 /*************************************************************************
1808 * Scans for characters of type "type" and when not matching found,
1809 * returns pointer to it and length in size.
1811 * Characters tested based on RFC 1738
1813 static LPCWSTR URL_ScanID(LPCWSTR start, LPDWORD size, WINE_URL_SCAN_TYPE type)
1815 static DWORD alwayszero = 0;
1824 if ( (islowerW(*start) && isalphaW(*start)) ||
1839 if ( isalphaW(*start) ||
1841 /* user/password only characters */
1846 /* *extra* characters */
1849 (*start == L'\'') ||
1853 /* *safe* characters */
1861 } else if (*start == L'%') {
1862 if (isxdigitW(*(start+1)) &&
1863 isxdigitW(*(start+2))) {
1875 if (isdigitW(*start)) {
1886 if (isalnumW(*start) ||
1888 (*start == L'.') ) {
1897 FIXME("unknown type %d\n", type);
1898 return (LPWSTR)&alwayszero;
1900 /* TRACE("scanned %ld characters next char %p<%c>\n",
1901 *size, start, *start); */
1905 /*************************************************************************
1906 * Attempt to parse URL into pieces.
1908 static LONG URL_ParseUrl(LPCWSTR pszUrl, WINE_PARSE_URL *pl)
1912 memset(pl, 0, sizeof(WINE_PARSE_URL));
1913 pl->pScheme = pszUrl;
1914 work = URL_ScanID(pl->pScheme, &pl->szScheme, SCHEME);
1915 if (!*work || (*work != L':')) goto ErrorExit;
1917 if ((*work != L'/') || (*(work+1) != L'/')) goto ErrorExit;
1918 pl->pUserName = work + 2;
1919 work = URL_ScanID(pl->pUserName, &pl->szUserName, USERPASS);
1920 if (*work == L':' ) {
1921 /* parse password */
1923 pl->pPassword = work;
1924 work = URL_ScanID(pl->pPassword, &pl->szPassword, USERPASS);
1925 if (*work != L'@') {
1926 /* what we just parsed must be the hostname and port
1927 * so reset pointers and clear then let it parse */
1928 pl->szUserName = pl->szPassword = 0;
1929 work = pl->pUserName - 1;
1930 pl->pUserName = pl->pPassword = 0;
1932 } else if (*work == L'@') {
1936 } else if (!*work || (*work == L'/') || (*work == L'.')) {
1937 /* what was parsed was hostname, so reset pointers and let it parse */
1938 pl->szUserName = pl->szPassword = 0;
1939 work = pl->pUserName - 1;
1940 pl->pUserName = pl->pPassword = 0;
1941 } else goto ErrorExit;
1943 /* now start parsing hostname or hostnumber */
1945 pl->pHostName = work;
1946 work = URL_ScanID(pl->pHostName, &pl->szHostName, HOST);
1947 if (*work == L':') {
1951 work = URL_ScanID(pl->pPort, &pl->szPort, PORT);
1953 if (*work == L'/') {
1954 /* see if query string */
1955 pl->pQuery = strchrW(work, L'?');
1956 if (pl->pQuery) pl->szQuery = strlenW(pl->pQuery);
1958 TRACE("parse successful: scheme=%p(%ld), user=%p(%ld), pass=%p(%ld), host=%p(%ld), port=%p(%ld), query=%p(%ld)\n",
1959 pl->pScheme, pl->szScheme,
1960 pl->pUserName, pl->szUserName,
1961 pl->pPassword, pl->szPassword,
1962 pl->pHostName, pl->szHostName,
1963 pl->pPort, pl->szPort,
1964 pl->pQuery, pl->szQuery);
1967 FIXME("failed to parse %s\n", debugstr_w(pszUrl));
1968 return E_INVALIDARG;
1971 /*************************************************************************
1972 * UrlGetPartA [SHLWAPI.@]
1974 * Retrieve part of a Url.
1977 * pszIn [I] Url to parse
1978 * pszOut [O] Destination for part of pszIn requested
1979 * pcchOut [I] Size of pszOut
1980 * [O] length of pszOut string EXLUDING '\0' if S_OK, otherwise
1981 * needed size of pszOut INCLUDING '\0'.
1982 * dwPart [I] URL_PART_ enum from "shlwapi.h"
1983 * dwFlags [I] URL_ flags from "shlwapi.h"
1986 * Success: S_OK. pszOut contains the part requested, pcchOut contains its length.
1987 * Failure: An HRESULT error code describing the error.
1989 HRESULT WINAPI UrlGetPartA(LPCSTR pszIn, LPSTR pszOut, LPDWORD pcchOut,
1990 DWORD dwPart, DWORD dwFlags)
1993 DWORD ret, len, len2;
1995 in = HeapAlloc(GetProcessHeap(), 0,
1996 (2*INTERNET_MAX_URL_LENGTH) * sizeof(WCHAR));
1997 out = in + INTERNET_MAX_URL_LENGTH;
1999 MultiByteToWideChar(0, 0, pszIn, -1, in, INTERNET_MAX_URL_LENGTH);
2001 len = INTERNET_MAX_URL_LENGTH;
2002 ret = UrlGetPartW(in, out, &len, dwPart, dwFlags);
2005 HeapFree(GetProcessHeap(), 0, in);
2009 len2 = WideCharToMultiByte(0, 0, out, len, 0, 0, 0, 0);
2010 if (len2 > *pcchOut) {
2012 HeapFree(GetProcessHeap(), 0, in);
2015 WideCharToMultiByte(0, 0, out, len+1, pszOut, *pcchOut, 0, 0);
2017 HeapFree(GetProcessHeap(), 0, in);
2021 /*************************************************************************
2022 * UrlGetPartW [SHLWAPI.@]
2026 HRESULT WINAPI UrlGetPartW(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut,
2027 DWORD dwPart, DWORD dwFlags)
2031 DWORD size, schsize;
2032 LPCWSTR addr, schaddr;
2034 TRACE("(%s %p %p(%ld) %08lx %08lx)\n",
2035 debugstr_w(pszIn), pszOut, pcchOut, *pcchOut, dwPart, dwFlags);
2037 ret = URL_ParseUrl(pszIn, &pl);
2039 schaddr = pl.pScheme;
2040 schsize = pl.szScheme;
2043 case URL_PART_SCHEME:
2044 if (!pl.szScheme) return E_INVALIDARG;
2049 case URL_PART_HOSTNAME:
2050 if (!pl.szHostName) return E_INVALIDARG;
2051 addr = pl.pHostName;
2052 size = pl.szHostName;
2055 case URL_PART_USERNAME:
2056 if (!pl.szUserName) return E_INVALIDARG;
2057 addr = pl.pUserName;
2058 size = pl.szUserName;
2061 case URL_PART_PASSWORD:
2062 if (!pl.szPassword) return E_INVALIDARG;
2063 addr = pl.pPassword;
2064 size = pl.szPassword;
2068 if (!pl.szPort) return E_INVALIDARG;
2073 case URL_PART_QUERY:
2074 if (!pl.szQuery) return E_INVALIDARG;
2080 return E_INVALIDARG;
2083 if (dwFlags == URL_PARTFLAG_KEEPSCHEME) {
2084 if (*pcchOut < schsize + size + 2) {
2085 *pcchOut = schsize + size + 2;
2088 memcpy(pszOut, schaddr, schsize*sizeof(WCHAR));
2089 pszOut[schsize] = ':';
2090 memcpy(pszOut+schsize+1, addr, size*sizeof(WCHAR));
2091 pszOut[schsize+1+size] = 0;
2092 *pcchOut = schsize + 1 + size;
2095 if (*pcchOut < size + 1) {*pcchOut = size+1; return E_POINTER;}
2096 memcpy(pszOut, addr, size*sizeof(WCHAR));
2100 TRACE("len=%ld %s\n", *pcchOut, debugstr_w(pszOut));
2105 /*************************************************************************
2106 * PathIsURLA [SHLWAPI.@]
2108 * Check if the given path is a Url.
2111 * lpszPath [I] Path to check.
2114 * TRUE if lpszPath is a Url.
2115 * FALSE if lpszPath is NULL or not a Url.
2117 BOOL WINAPI PathIsURLA(LPCSTR lpstrPath)
2122 if (!lpstrPath || !*lpstrPath) return FALSE;
2125 base.cbSize = sizeof(base);
2126 res1 = ParseURLA(lpstrPath, &base);
2127 return (base.nScheme != URL_SCHEME_INVALID);
2130 /*************************************************************************
2131 * PathIsURLW [SHLWAPI.@]
2135 BOOL WINAPI PathIsURLW(LPCWSTR lpstrPath)
2140 if (!lpstrPath || !*lpstrPath) return FALSE;
2143 base.cbSize = sizeof(base);
2144 res1 = ParseURLW(lpstrPath, &base);
2145 return (base.nScheme != URL_SCHEME_INVALID);
2148 /*************************************************************************
2149 * UrlCreateFromPathA [SHLWAPI.@]
2151 * See UrlCreateFromPathW
2153 HRESULT WINAPI UrlCreateFromPathA(LPCSTR pszPath, LPSTR pszUrl, LPDWORD pcchUrl, DWORD dwReserved)
2155 WCHAR bufW[INTERNET_MAX_URL_LENGTH];
2157 UNICODE_STRING pathW;
2159 DWORD lenW = sizeof(bufW)/sizeof(WCHAR), lenA;
2161 if(!RtlCreateUnicodeStringFromAsciiz(&pathW, pszPath))
2162 return E_INVALIDARG;
2163 if((ret = UrlCreateFromPathW(pathW.Buffer, urlW, &lenW, dwReserved)) == E_POINTER) {
2164 urlW = HeapAlloc(GetProcessHeap(), 0, lenW * sizeof(WCHAR));
2165 ret = UrlCreateFromPathW(pathW.Buffer, urlW, &lenW, dwReserved);
2167 if(ret == S_OK || ret == S_FALSE) {
2168 RtlUnicodeToMultiByteSize(&lenA, urlW, lenW * sizeof(WCHAR));
2169 if(*pcchUrl > lenA) {
2170 RtlUnicodeToMultiByteN(pszUrl, *pcchUrl - 1, &lenA, urlW, lenW * sizeof(WCHAR));
2174 *pcchUrl = lenA + 1;
2178 if(urlW != bufW) HeapFree(GetProcessHeap(), 0, urlW);
2179 RtlFreeUnicodeString(&pathW);
2183 /*************************************************************************
2184 * UrlCreateFromPathW [SHLWAPI.@]
2186 * Create a Url from a file path.
2189 * pszPath [I] Path to convert
2190 * pszUrl [O] Destination for the converted Url
2191 * pcchUrl [I/O] Length of pszUrl
2192 * dwReserved [I] Reserved, must be 0
2195 * Success: S_OK pszUrl contains the converted path, S_FALSE if the path is already a Url
2196 * Failure: An HRESULT error code.
2198 HRESULT WINAPI UrlCreateFromPathW(LPCWSTR pszPath, LPWSTR pszUrl, LPDWORD pcchUrl, DWORD dwReserved)
2203 WCHAR file_colonW[] = {'f','i','l','e',':',0};
2204 WCHAR three_slashesW[] = {'/','/','/',0};
2205 PARSEDURLW parsed_url;
2207 TRACE("(%s, %p, %p, 0x%08lx)\n", debugstr_w(pszPath), pszUrl, pcchUrl, dwReserved);
2209 /* Validate arguments */
2210 if (dwReserved != 0)
2211 return E_INVALIDARG;
2212 if (!pszUrl || !pcchUrl)
2213 return E_INVALIDARG;
2216 parsed_url.cbSize = sizeof(parsed_url);
2217 if(ParseURLW(pszPath, &parsed_url) == S_OK) {
2218 if(parsed_url.nScheme != URL_SCHEME_INVALID && parsed_url.cchProtocol > 1) {
2219 needed = strlenW(pszPath);
2220 if (needed >= *pcchUrl) {
2221 *pcchUrl = needed + 1;
2225 strcpyW(pszUrl, pszPath);
2231 pszNewUrl = HeapAlloc(GetProcessHeap(), 0, (strlenW(pszPath) + 9) * sizeof(WCHAR)); /* "file:///" + pszPath_len + 1 */
2232 strcpyW(pszNewUrl, file_colonW);
2233 if(isalphaW(pszPath[0]) && pszPath[1] == ':')
2234 strcatW(pszNewUrl, three_slashesW);
2235 strcatW(pszNewUrl, pszPath);
2236 ret = UrlEscapeW(pszNewUrl, pszUrl, pcchUrl, URL_ESCAPE_PERCENT);
2238 HeapFree(GetProcessHeap(), 0, pszNewUrl);
2242 /*************************************************************************
2243 * SHAutoComplete [SHLWAPI.@]
2245 * Enable auto-completion for an edit control.
2248 * hwndEdit [I] Handle of control to enable auto-completion for
2249 * dwFlags [I] SHACF_ flags from "shlwapi.h"
2252 * Success: S_OK. Auto-completion is enabled for the control.
2253 * Failure: An HRESULT error code indicating the error.
2255 HRESULT WINAPI SHAutoComplete(HWND hwndEdit, DWORD dwFlags)
2257 FIXME("SHAutoComplete stub\n");
2261 /*************************************************************************
2262 * MLBuildResURLA [SHLWAPI.405]
2264 * Create a Url pointing to a resource in a module.
2267 * lpszLibName [I] Name of the module containing the resource
2268 * hMod [I] Callers module handle
2269 * dwFlags [I] Undocumented flags for loading the module
2270 * lpszRes [I] Resource name
2271 * lpszDest [O] Destination for resulting Url
2272 * dwDestLen [I] Length of lpszDest
2275 * Success: S_OK. lpszDest constains the resource Url.
2276 * Failure: E_INVALIDARG, if any argument is invalid, or
2277 * E_FAIL if dwDestLen is too small.
2279 HRESULT WINAPI MLBuildResURLA(LPCSTR lpszLibName, HMODULE hMod, DWORD dwFlags,
2280 LPCSTR lpszRes, LPSTR lpszDest, DWORD dwDestLen)
2282 WCHAR szLibName[MAX_PATH], szRes[MAX_PATH], szDest[MAX_PATH];
2286 MultiByteToWideChar(CP_ACP, 0, lpszLibName, -1, szLibName, sizeof(szLibName)/sizeof(WCHAR));
2289 MultiByteToWideChar(CP_ACP, 0, lpszRes, -1, szRes, sizeof(szRes)/sizeof(WCHAR));
2291 if (dwDestLen > sizeof(szLibName)/sizeof(WCHAR))
2292 dwDestLen = sizeof(szLibName)/sizeof(WCHAR);
2294 hRet = MLBuildResURLW(lpszLibName ? szLibName : NULL, hMod, dwFlags,
2295 lpszRes ? szRes : NULL, lpszDest ? szDest : NULL, dwDestLen);
2296 if (SUCCEEDED(hRet) && lpszDest)
2297 WideCharToMultiByte(CP_ACP, 0, szDest, -1, lpszDest, dwDestLen, 0, 0);
2302 /*************************************************************************
2303 * MLBuildResURLA [SHLWAPI.406]
2305 * See MLBuildResURLA.
2307 HRESULT WINAPI MLBuildResURLW(LPCWSTR lpszLibName, HMODULE hMod, DWORD dwFlags,
2308 LPCWSTR lpszRes, LPWSTR lpszDest, DWORD dwDestLen)
2310 static const WCHAR szRes[] = { 'r','e','s',':','/','/','\0' };
2311 #define szResLen ((sizeof(szRes) - sizeof(WCHAR))/sizeof(WCHAR))
2312 HRESULT hRet = E_FAIL;
2314 TRACE("(%s,%p,0x%08lx,%s,%p,%ld)\n", debugstr_w(lpszLibName), hMod, dwFlags,
2315 debugstr_w(lpszRes), lpszDest, dwDestLen);
2317 if (!lpszLibName || !hMod || hMod == INVALID_HANDLE_VALUE || !lpszRes ||
2318 !lpszDest || (dwFlags && dwFlags != 2))
2319 return E_INVALIDARG;
2321 if (dwDestLen >= szResLen + 1)
2323 dwDestLen -= (szResLen + 1);
2324 memcpy(lpszDest, szRes, sizeof(szRes));
2326 hMod = MLLoadLibraryW(lpszLibName, hMod, dwFlags);
2330 WCHAR szBuff[MAX_PATH];
2333 len = GetModuleFileNameW(hMod, szBuff, sizeof(szBuff)/sizeof(WCHAR));
2334 if (len && len < sizeof(szBuff)/sizeof(WCHAR))
2336 DWORD dwPathLen = strlenW(szBuff) + 1;
2338 if (dwDestLen >= dwPathLen)
2342 dwDestLen -= dwPathLen;
2343 memcpy(lpszDest + szResLen, szBuff, dwPathLen * sizeof(WCHAR));
2345 dwResLen = strlenW(lpszRes) + 1;
2346 if (dwDestLen >= dwResLen + 1)
2348 lpszDest[szResLen + dwPathLen + dwResLen] = '/';
2349 memcpy(lpszDest + szResLen + dwPathLen, lpszRes, dwResLen * sizeof(WCHAR));
2354 MLFreeLibrary(hMod);