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;
49 WCHAR scheme_name[12];
50 } shlwapi_schemes[] = {
51 {URL_SCHEME_FTP, {'f','t','p',0}},
52 {URL_SCHEME_HTTP, {'h','t','t','p',0}},
53 {URL_SCHEME_GOPHER, {'g','o','p','h','e','r',0}},
54 {URL_SCHEME_MAILTO, {'m','a','i','l','t','o',0}},
55 {URL_SCHEME_NEWS, {'n','e','w','s',0}},
56 {URL_SCHEME_NNTP, {'n','n','t','p',0}},
57 {URL_SCHEME_TELNET, {'t','e','l','n','e','t',0}},
58 {URL_SCHEME_WAIS, {'w','a','i','s',0}},
59 {URL_SCHEME_FILE, {'f','i','l','e',0}},
60 {URL_SCHEME_MK, {'m','k',0}},
61 {URL_SCHEME_HTTPS, {'h','t','t','p','s',0}},
62 {URL_SCHEME_SHELL, {'s','h','e','l','l',0}},
63 {URL_SCHEME_SNEWS, {'s','n','e','w','s',0}},
64 {URL_SCHEME_LOCAL, {'l','o','c','a','l',0}},
65 {URL_SCHEME_JAVASCRIPT, {'j','a','v','a','s','c','r','i','p','t',0}},
66 {URL_SCHEME_VBSCRIPT, {'v','b','s','c','r','i','p','t',0}},
67 {URL_SCHEME_ABOUT, {'a','b','o','u','t',0}},
68 {URL_SCHEME_RES, {'r','e','s',0}},
72 LPCWSTR pScheme; /* [out] start of scheme */
73 DWORD szScheme; /* [out] size of scheme (until colon) */
74 LPCWSTR pUserName; /* [out] start of Username */
75 DWORD szUserName; /* [out] size of Username (until ":" or "@") */
76 LPCWSTR pPassword; /* [out] start of Password */
77 DWORD szPassword; /* [out] size of Password (until "@") */
78 LPCWSTR pHostName; /* [out] start of Hostname */
79 DWORD szHostName; /* [out] size of Hostname (until ":" or "/") */
80 LPCWSTR pPort; /* [out] start of Port */
81 DWORD szPort; /* [out] size of Port (until "/" or eos) */
82 LPCWSTR pQuery; /* [out] start of Query */
83 DWORD szQuery; /* [out] size of Query (until eos) */
93 static const CHAR hexDigits[] = "0123456789ABCDEF";
95 static const WCHAR fileW[] = {'f','i','l','e','\0'};
97 static const unsigned char HashDataLookup[256] = {
98 0x01, 0x0E, 0x6E, 0x19, 0x61, 0xAE, 0x84, 0x77, 0x8A, 0xAA, 0x7D, 0x76, 0x1B,
99 0xE9, 0x8C, 0x33, 0x57, 0xC5, 0xB1, 0x6B, 0xEA, 0xA9, 0x38, 0x44, 0x1E, 0x07,
100 0xAD, 0x49, 0xBC, 0x28, 0x24, 0x41, 0x31, 0xD5, 0x68, 0xBE, 0x39, 0xD3, 0x94,
101 0xDF, 0x30, 0x73, 0x0F, 0x02, 0x43, 0xBA, 0xD2, 0x1C, 0x0C, 0xB5, 0x67, 0x46,
102 0x16, 0x3A, 0x4B, 0x4E, 0xB7, 0xA7, 0xEE, 0x9D, 0x7C, 0x93, 0xAC, 0x90, 0xB0,
103 0xA1, 0x8D, 0x56, 0x3C, 0x42, 0x80, 0x53, 0x9C, 0xF1, 0x4F, 0x2E, 0xA8, 0xC6,
104 0x29, 0xFE, 0xB2, 0x55, 0xFD, 0xED, 0xFA, 0x9A, 0x85, 0x58, 0x23, 0xCE, 0x5F,
105 0x74, 0xFC, 0xC0, 0x36, 0xDD, 0x66, 0xDA, 0xFF, 0xF0, 0x52, 0x6A, 0x9E, 0xC9,
106 0x3D, 0x03, 0x59, 0x09, 0x2A, 0x9B, 0x9F, 0x5D, 0xA6, 0x50, 0x32, 0x22, 0xAF,
107 0xC3, 0x64, 0x63, 0x1A, 0x96, 0x10, 0x91, 0x04, 0x21, 0x08, 0xBD, 0x79, 0x40,
108 0x4D, 0x48, 0xD0, 0xF5, 0x82, 0x7A, 0x8F, 0x37, 0x69, 0x86, 0x1D, 0xA4, 0xB9,
109 0xC2, 0xC1, 0xEF, 0x65, 0xF2, 0x05, 0xAB, 0x7E, 0x0B, 0x4A, 0x3B, 0x89, 0xE4,
110 0x6C, 0xBF, 0xE8, 0x8B, 0x06, 0x18, 0x51, 0x14, 0x7F, 0x11, 0x5B, 0x5C, 0xFB,
111 0x97, 0xE1, 0xCF, 0x15, 0x62, 0x71, 0x70, 0x54, 0xE2, 0x12, 0xD6, 0xC7, 0xBB,
112 0x0D, 0x20, 0x5E, 0xDC, 0xE0, 0xD4, 0xF7, 0xCC, 0xC4, 0x2B, 0xF9, 0xEC, 0x2D,
113 0xF4, 0x6F, 0xB6, 0x99, 0x88, 0x81, 0x5A, 0xD9, 0xCA, 0x13, 0xA5, 0xE7, 0x47,
114 0xE6, 0x8E, 0x60, 0xE3, 0x3E, 0xB3, 0xF6, 0x72, 0xA2, 0x35, 0xA0, 0xD7, 0xCD,
115 0xB4, 0x2F, 0x6D, 0x2C, 0x26, 0x1F, 0x95, 0x87, 0x00, 0xD8, 0x34, 0x3F, 0x17,
116 0x25, 0x45, 0x27, 0x75, 0x92, 0xB8, 0xA3, 0xC8, 0xDE, 0xEB, 0xF8, 0xF3, 0xDB,
117 0x0A, 0x98, 0x83, 0x7B, 0xE5, 0xCB, 0x4C, 0x78, 0xD1 };
119 static DWORD get_scheme_code(LPCWSTR scheme, DWORD scheme_len)
123 for(i=0; i < sizeof(shlwapi_schemes)/sizeof(shlwapi_schemes[0]); i++) {
124 if(scheme_len == strlenW(shlwapi_schemes[i].scheme_name)
125 && !memcmp(scheme, shlwapi_schemes[i].scheme_name, scheme_len*sizeof(WCHAR)))
126 return shlwapi_schemes[i].scheme_number;
129 return URL_SCHEME_UNKNOWN;
132 static BOOL URL_JustLocation(LPCWSTR str)
134 while(*str && (*str == L'/')) str++;
136 while (*str && ((*str == L'-') ||
138 isalnumW(*str))) str++;
139 if (*str == L'/') return FALSE;
145 /*************************************************************************
148 * Parse a Url into its constituent parts.
152 * y [O] Undocumented structure holding the parsed information
155 * Success: S_OK. y contains the parsed Url details.
156 * Failure: An HRESULT error code.
158 HRESULT WINAPI ParseURLA(LPCSTR x, PARSEDURLA *y)
160 WCHAR scheme[INTERNET_MAX_SCHEME_LENGTH];
163 y->nScheme = URL_SCHEME_INVALID;
164 if (y->cbSize != sizeof(*y)) return E_INVALIDARG;
165 /* FIXME: leading white space generates error of 0x80041001 which
168 if (*x <= ' ') return 0x80041001;
174 y->cchProtocol = cnt;
183 /* check for no scheme in string start */
184 /* (apparently schemes *must* be larger than a single character) */
185 if ((*x == '\0') || (y->cchProtocol <= 1)) {
186 y->pszProtocol = NULL;
190 /* found scheme, set length of remainder */
191 y->cchSuffix = lstrlenA(y->pszSuffix);
193 len = MultiByteToWideChar(CP_ACP, 0, y->pszProtocol, y->cchProtocol,
194 scheme, sizeof(scheme));
195 y->nScheme = get_scheme_code(scheme, len);
200 /*************************************************************************
203 * Unicode version of ParseURLA.
205 HRESULT WINAPI ParseURLW(LPCWSTR x, PARSEDURLW *y)
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);
238 y->nScheme = get_scheme_code(y->pszProtocol, y->cchProtocol);
243 /*************************************************************************
244 * UrlCanonicalizeA [SHLWAPI.@]
246 * Canonicalize a Url.
249 * pszUrl [I] Url to cCanonicalize
250 * pszCanonicalized [O] Destination for converted Url.
251 * pcchCanonicalized [I/O] Length of pszUrl, destination for length of pszCanonicalized
252 * dwFlags [I] Flags controlling the conversion.
255 * Success: S_OK. The pszCanonicalized contains the converted Url.
256 * Failure: E_POINTER, if *pcchCanonicalized is too small.
258 * MSDN incorrectly describes the flags for this function. They should be:
259 *| URL_DONT_ESCAPE_EXTRA_INFO 0x02000000
260 *| URL_ESCAPE_SPACES_ONLY 0x04000000
261 *| URL_ESCAPE_PERCENT 0x00001000
262 *| URL_ESCAPE_UNSAFE 0x10000000
263 *| URL_UNESCAPE 0x10000000
264 *| URL_DONT_SIMPLIFY 0x08000000
265 *| URL_ESCAPE_SEGMENT_ONLY 0x00002000
267 HRESULT WINAPI UrlCanonicalizeA(LPCSTR pszUrl, LPSTR pszCanonicalized,
268 LPDWORD pcchCanonicalized, DWORD dwFlags)
270 LPWSTR base, canonical;
271 DWORD ret, len, len2;
273 TRACE("(%s %p %p 0x%08x) using W version\n",
274 debugstr_a(pszUrl), pszCanonicalized,
275 pcchCanonicalized, dwFlags);
277 if(!pszUrl || !pszCanonicalized || !pcchCanonicalized)
280 base = HeapAlloc(GetProcessHeap(), 0,
281 (2*INTERNET_MAX_URL_LENGTH) * sizeof(WCHAR));
282 canonical = base + INTERNET_MAX_URL_LENGTH;
284 MultiByteToWideChar(0, 0, pszUrl, -1, base, INTERNET_MAX_URL_LENGTH);
285 len = INTERNET_MAX_URL_LENGTH;
287 ret = UrlCanonicalizeW(base, canonical, &len, dwFlags);
289 HeapFree(GetProcessHeap(), 0, base);
293 len2 = WideCharToMultiByte(0, 0, canonical, len, 0, 0, 0, 0);
294 if (len2 > *pcchCanonicalized) {
295 *pcchCanonicalized = len;
296 HeapFree(GetProcessHeap(), 0, base);
299 WideCharToMultiByte(0, 0, canonical, len+1, pszCanonicalized,
300 *pcchCanonicalized, 0, 0);
301 *pcchCanonicalized = len2;
302 HeapFree(GetProcessHeap(), 0, base);
306 /*************************************************************************
307 * UrlCanonicalizeW [SHLWAPI.@]
309 * See UrlCanonicalizeA.
311 HRESULT WINAPI UrlCanonicalizeW(LPCWSTR pszUrl, LPWSTR pszCanonicalized,
312 LPDWORD pcchCanonicalized, DWORD dwFlags)
316 LPWSTR lpszUrlCpy, wk1, wk2, mp, mp2, root;
321 static const WCHAR wszFile[] = {'f','i','l','e',':'};
323 TRACE("(%s %p %p 0x%08x)\n", debugstr_w(pszUrl), pszCanonicalized,
324 pcchCanonicalized, dwFlags);
326 if(!pszUrl || !pszCanonicalized || !pcchCanonicalized)
330 *pszCanonicalized = 0;
334 nByteLen = (lstrlenW(pszUrl) + 1) * sizeof(WCHAR); /* length in bytes */
335 lpszUrlCpy = HeapAlloc(GetProcessHeap(), 0, INTERNET_MAX_URL_LENGTH);
337 if((dwFlags & URL_FILE_USE_PATHURL) && nByteLen >= sizeof(wszFile)
338 && !memcmp(wszFile, pszUrl, sizeof(wszFile)))
344 * 1 have 2[+] alnum 2,3
345 * 2 have scheme (found :) 4,6,3
346 * 3 failed (no location)
348 * 5 have 1[+] alnum 6,3
349 * 6 have location (found /) save root location
352 wk1 = (LPWSTR)pszUrl;
356 if(pszUrl[1] == ':') { /* Assume path */
357 static const WCHAR wszFilePrefix[] = {'f','i','l','e',':','/','/','/'};
359 memcpy(wk2, wszFilePrefix, sizeof(wszFilePrefix));
360 wk2 += sizeof(wszFilePrefix)/sizeof(WCHAR);
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 %d, flags %08x) 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 %d, flags %08x)\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");
632 /* mk is a special case */
633 if(base.nScheme == URL_SCHEME_MK) {
634 static const WCHAR wsz[] = {':',':',0};
636 WCHAR *ptr = strstrW(base.pszSuffix, wsz);
641 delta = ptr-base.pszSuffix;
642 base.cchProtocol += delta;
643 base.pszSuffix += delta;
644 base.cchSuffix -= delta;
648 /* get size of location field (if it exists) */
649 work = (LPWSTR)base.pszSuffix;
651 if (*work++ == L'/') {
652 if (*work++ == L'/') {
653 /* At this point have start of location and
654 * it ends at next '/' or end of string.
656 while(*work && (*work != L'/')) work++;
657 sizeloc = (DWORD)(work - base.pszSuffix);
661 /* Change .sizep2 to not have the last leaf in it,
662 * Note: we need to start after the location (if it exists)
664 work = strrchrW((base.pszSuffix+sizeloc), L'/');
666 len = (DWORD)(work - base.pszSuffix + 1);
667 base.cchSuffix = len;
672 * .pszSuffix points to location (starting with '//')
673 * .cchSuffix length of location (above) and rest less the last
675 * sizeloc length of location (above) up to but not including
679 res2 = ParseURLW(mrelative, &relative);
681 /* no scheme in pszRelative */
682 TRACE("no scheme detected in Relative\n");
683 relative.pszSuffix = mrelative; /* case 3,4,5 depends on this */
684 relative.cchSuffix = strlenW(mrelative);
685 if (*pszRelative == L':') {
686 /* case that is either left alone or uses pszBase */
687 if (dwFlags & URL_PLUGGABLE_PROTOCOL) {
694 if (isalnum(*mrelative) && (*(mrelative + 1) == L':')) {
695 /* case that becomes "file:///" */
696 strcpyW(preliminary, myfilestr);
700 if ((*mrelative == L'/') && (*(mrelative+1) == L'/')) {
701 /* pszRelative has location and rest */
705 if (*mrelative == L'/') {
706 /* case where pszRelative is root to location */
710 process_case = (*base.pszSuffix == L'/') ? 5 : 3;
714 /* handle cases where pszRelative has scheme */
715 if ((base.cchProtocol == relative.cchProtocol) &&
716 (strncmpW(base.pszProtocol, relative.pszProtocol, base.cchProtocol) == 0)) {
718 /* since the schemes are the same */
719 if ((*relative.pszSuffix == L'/') && (*(relative.pszSuffix+1) == L'/')) {
720 /* case where pszRelative replaces location and following */
724 if (*relative.pszSuffix == L'/') {
725 /* case where pszRelative is root to location */
729 /* case where scheme is followed by document path */
733 if ((*relative.pszSuffix == L'/') && (*(relative.pszSuffix+1) == L'/')) {
734 /* case where pszRelative replaces scheme, location,
735 * and following and handles PLUGGABLE
742 } while(FALSE); /* a litte trick to allow easy exit from nested if's */
746 switch (process_case) {
749 * Return pszRelative appended to what ever is in pszCombined,
750 * (which may the string "file:///"
752 strcatW(preliminary, mrelative);
756 * Same as case 1, but if URL_PLUGGABLE_PROTOCOL was specified
757 * and pszRelative starts with "//", then append a "/"
759 strcpyW(preliminary, mrelative);
760 if (!(dwFlags & URL_PLUGGABLE_PROTOCOL) &&
761 URL_JustLocation(relative.pszSuffix))
762 strcatW(preliminary, single_slash);
766 * Return the pszBase scheme with pszRelative. Basically
767 * keeps the scheme and replaces the domain and following.
769 memcpy(preliminary, base.pszProtocol, (base.cchProtocol + 1)*sizeof(WCHAR));
770 work = preliminary + base.cchProtocol + 1;
771 strcpyW(work, relative.pszSuffix);
772 if (!(dwFlags & URL_PLUGGABLE_PROTOCOL) &&
773 URL_JustLocation(relative.pszSuffix))
774 strcatW(work, single_slash);
778 * Return the pszBase scheme and location but everything
779 * after the location is pszRelative. (Replace document
782 memcpy(preliminary, base.pszProtocol, (base.cchProtocol+1+sizeloc)*sizeof(WCHAR));
783 work = preliminary + base.cchProtocol + 1 + sizeloc;
784 if (dwFlags & URL_PLUGGABLE_PROTOCOL)
786 strcpyW(work, relative.pszSuffix);
790 * Return the pszBase without its document (if any) and
791 * append pszRelative after its scheme.
793 memcpy(preliminary, base.pszProtocol,
794 (base.cchProtocol+1+base.cchSuffix)*sizeof(WCHAR));
795 work = preliminary + base.cchProtocol+1+base.cchSuffix - 1;
798 strcpyW(work, relative.pszSuffix);
802 FIXME("How did we get here????? process_case=%d\n", process_case);
807 /* Reuse mrelative as temp storage as its already allocated and not needed anymore */
808 ret = UrlCanonicalizeW(preliminary, mrelative, pcchCombined, (dwFlags & ~URL_FILE_USE_PATHURL));
809 if(SUCCEEDED(ret) && pszCombined) {
810 lstrcpyW(pszCombined, mrelative);
812 TRACE("return-%d len=%d, %s\n",
813 process_case, *pcchCombined, debugstr_w(pszCombined));
815 HeapFree(GetProcessHeap(), 0, preliminary);
819 /*************************************************************************
820 * UrlEscapeA [SHLWAPI.@]
823 HRESULT WINAPI UrlEscapeA(
829 WCHAR bufW[INTERNET_MAX_URL_LENGTH];
830 WCHAR *escapedW = bufW;
833 DWORD lenW = sizeof(bufW)/sizeof(WCHAR), lenA;
835 if(!RtlCreateUnicodeStringFromAsciiz(&urlW, pszUrl))
837 if((ret = UrlEscapeW(urlW.Buffer, escapedW, &lenW, dwFlags)) == E_POINTER) {
838 escapedW = HeapAlloc(GetProcessHeap(), 0, lenW * sizeof(WCHAR));
839 ret = UrlEscapeW(urlW.Buffer, escapedW, &lenW, dwFlags);
842 RtlUnicodeToMultiByteSize(&lenA, escapedW, lenW * sizeof(WCHAR));
843 if(pszEscaped && *pcchEscaped > lenA) {
844 RtlUnicodeToMultiByteN(pszEscaped, *pcchEscaped - 1, &lenA, escapedW, lenW * sizeof(WCHAR));
845 pszEscaped[lenA] = 0;
848 *pcchEscaped = lenA + 1;
852 if(escapedW != bufW) HeapFree(GetProcessHeap(), 0, escapedW);
853 RtlFreeUnicodeString(&urlW);
857 #define WINE_URL_BASH_AS_SLASH 0x01
858 #define WINE_URL_COLLAPSE_SLASHES 0x02
859 #define WINE_URL_ESCAPE_SLASH 0x04
860 #define WINE_URL_ESCAPE_HASH 0x08
861 #define WINE_URL_ESCAPE_QUESTION 0x10
862 #define WINE_URL_STOP_ON_HASH 0x20
863 #define WINE_URL_STOP_ON_QUESTION 0x40
865 static inline BOOL URL_NeedEscapeW(WCHAR ch, DWORD dwFlags, DWORD int_flags)
871 if(dwFlags & URL_ESCAPE_SPACES_ONLY) {
878 if ((dwFlags & URL_ESCAPE_PERCENT) && (ch == '%'))
881 if (ch <= 31 || ch >= 127)
902 if (int_flags & WINE_URL_ESCAPE_SLASH) return TRUE;
906 if (int_flags & WINE_URL_ESCAPE_QUESTION) return TRUE;
910 if (int_flags & WINE_URL_ESCAPE_HASH) return TRUE;
920 /*************************************************************************
921 * UrlEscapeW [SHLWAPI.@]
923 * Converts unsafe characters in a Url into escape sequences.
926 * pszUrl [I] Url to modify
927 * pszEscaped [O] Destination for modified Url
928 * pcchEscaped [I/O] Length of pszUrl, destination for length of pszEscaped
929 * dwFlags [I] URL_ flags from "shlwapi.h"
932 * Success: S_OK. pszEscaped contains the escaped Url, pcchEscaped
933 * contains its length.
934 * Failure: E_POINTER, if pszEscaped is not large enough. In this case
935 * pcchEscaped is set to the required length.
937 * Converts unsafe characters into their escape sequences.
940 * - By default this function stops converting at the first '?' or
942 * - If dwFlags contains URL_ESCAPE_SPACES_ONLY then only spaces are
943 * converted, but the conversion continues past a '?' or '#'.
944 * - Note that this function did not work well (or at all) in shlwapi version 4.
947 * Only the following flags are implemented:
948 *| URL_ESCAPE_SPACES_ONLY
949 *| URL_DONT_ESCAPE_EXTRA_INFO
950 *| URL_ESCAPE_SEGMENT_ONLY
951 *| URL_ESCAPE_PERCENT
953 HRESULT WINAPI UrlEscapeW(
960 DWORD needed = 0, ret;
961 BOOL stop_escaping = FALSE;
962 WCHAR next[5], *dst = pszEscaped;
964 PARSEDURLW parsed_url;
967 static const WCHAR localhost[] = {'l','o','c','a','l','h','o','s','t',0};
969 TRACE("(%s %p %p 0x%08x)\n", debugstr_w(pszUrl), pszEscaped,
970 pcchEscaped, dwFlags);
972 if(!pszUrl || !pcchEscaped)
975 if(dwFlags & ~(URL_ESCAPE_SPACES_ONLY |
976 URL_ESCAPE_SEGMENT_ONLY |
977 URL_DONT_ESCAPE_EXTRA_INFO |
979 FIXME("Unimplemented flags: %08x\n", dwFlags);
982 if (dwFlags & URL_ESCAPE_SPACES_ONLY)
983 /* if SPACES_ONLY specified, reset the other controls */
984 dwFlags &= ~(URL_DONT_ESCAPE_EXTRA_INFO |
986 URL_ESCAPE_SEGMENT_ONLY);
989 /* if SPACES_ONLY *not* specified the assume DONT_ESCAPE_EXTRA_INFO */
990 dwFlags |= URL_DONT_ESCAPE_EXTRA_INFO;
994 if(dwFlags & URL_ESCAPE_SEGMENT_ONLY) {
995 int_flags = WINE_URL_ESCAPE_QUESTION | WINE_URL_ESCAPE_HASH | WINE_URL_ESCAPE_SLASH;
997 parsed_url.cbSize = sizeof(parsed_url);
998 if(ParseURLW(pszUrl, &parsed_url) != S_OK)
999 parsed_url.nScheme = URL_SCHEME_INVALID;
1001 TRACE("scheme = %d (%s)\n", parsed_url.nScheme, debugstr_wn(parsed_url.pszProtocol, parsed_url.cchProtocol));
1003 if(dwFlags & URL_DONT_ESCAPE_EXTRA_INFO)
1004 int_flags = WINE_URL_STOP_ON_HASH | WINE_URL_STOP_ON_QUESTION;
1006 switch(parsed_url.nScheme) {
1007 case URL_SCHEME_FILE:
1008 int_flags |= WINE_URL_BASH_AS_SLASH | WINE_URL_COLLAPSE_SLASHES | WINE_URL_ESCAPE_HASH;
1009 int_flags &= ~WINE_URL_STOP_ON_HASH;
1012 case URL_SCHEME_HTTP:
1013 case URL_SCHEME_HTTPS:
1014 int_flags |= WINE_URL_BASH_AS_SLASH;
1015 if(parsed_url.pszSuffix[0] != '/' && parsed_url.pszSuffix[0] != '\\')
1016 int_flags |= WINE_URL_ESCAPE_SLASH;
1019 case URL_SCHEME_MAILTO:
1020 int_flags |= WINE_URL_ESCAPE_SLASH | WINE_URL_ESCAPE_QUESTION | WINE_URL_ESCAPE_HASH;
1021 int_flags &= ~(WINE_URL_STOP_ON_QUESTION | WINE_URL_STOP_ON_HASH);
1024 case URL_SCHEME_INVALID:
1027 case URL_SCHEME_FTP:
1029 if(parsed_url.pszSuffix[0] != '/')
1030 int_flags |= WINE_URL_ESCAPE_SLASH;
1035 for(src = pszUrl; *src; ) {
1039 if((int_flags & WINE_URL_COLLAPSE_SLASHES) && src == pszUrl + parsed_url.cchProtocol + 1) {
1040 int localhost_len = sizeof(localhost)/sizeof(WCHAR) - 1;
1041 while(cur == '/' || cur == '\\') {
1045 if(slashes == 2 && !strncmpiW(src, localhost, localhost_len)) { /* file://localhost/ -> file:/// */
1046 if(*(src + localhost_len) == '/' || *(src + localhost_len) == '\\')
1047 src += localhost_len + 1;
1054 next[0] = next[1] = next[2] = '/';
1061 next[0] = next[1] = '/';
1068 if(cur == '#' && (int_flags & WINE_URL_STOP_ON_HASH))
1069 stop_escaping = TRUE;
1071 if(cur == '?' && (int_flags & WINE_URL_STOP_ON_QUESTION))
1072 stop_escaping = TRUE;
1074 if(cur == '\\' && (int_flags & WINE_URL_BASH_AS_SLASH) && !stop_escaping) cur = '/';
1076 if(URL_NeedEscapeW(cur, dwFlags, int_flags) && stop_escaping == FALSE) {
1078 next[1] = hexDigits[(cur >> 4) & 0xf];
1079 next[2] = hexDigits[cur & 0xf];
1088 if(needed + len <= *pcchEscaped) {
1089 memcpy(dst, next, len*sizeof(WCHAR));
1095 if(needed < *pcchEscaped) {
1099 needed++; /* add one for the '\0' */
1102 *pcchEscaped = needed;
1107 /*************************************************************************
1108 * UrlUnescapeA [SHLWAPI.@]
1110 * Converts Url escape sequences back to ordinary characters.
1113 * pszUrl [I/O] Url to convert
1114 * pszUnescaped [O] Destination for converted Url
1115 * pcchUnescaped [I/O] Size of output string
1116 * dwFlags [I] URL_ESCAPE_ Flags from "shlwapi.h"
1119 * Success: S_OK. The converted value is in pszUnescaped, or in pszUrl if
1120 * dwFlags includes URL_ESCAPE_INPLACE.
1121 * Failure: E_POINTER if the converted Url is bigger than pcchUnescaped. In
1122 * this case pcchUnescaped is set to the size required.
1124 * If dwFlags includes URL_DONT_ESCAPE_EXTRA_INFO, the conversion stops at
1125 * the first occurrence of either a '?' or '#' character.
1127 HRESULT WINAPI UrlUnescapeA(
1130 LPDWORD pcchUnescaped,
1137 BOOL stop_unescaping = FALSE;
1139 TRACE("(%s, %p, %p, 0x%08x)\n", debugstr_a(pszUrl), pszUnescaped,
1140 pcchUnescaped, dwFlags);
1142 if(!pszUrl || (!pszUnescaped && !(dwFlags & URL_UNESCAPE_INPLACE)) || !pcchUnescaped)
1143 return E_INVALIDARG;
1145 if(dwFlags & URL_UNESCAPE_INPLACE)
1150 for(src = pszUrl, needed = 0; *src; src++, needed++) {
1151 if(dwFlags & URL_DONT_UNESCAPE_EXTRA_INFO &&
1152 (*src == '#' || *src == '?')) {
1153 stop_unescaping = TRUE;
1155 } else if(*src == '%' && isxdigit(*(src + 1)) && isxdigit(*(src + 2))
1156 && stop_unescaping == FALSE) {
1159 memcpy(buf, src + 1, 2);
1161 ih = strtol(buf, NULL, 16);
1163 src += 2; /* Advance to end of escape */
1167 if(dwFlags & URL_UNESCAPE_INPLACE || needed < *pcchUnescaped)
1171 if(dwFlags & URL_UNESCAPE_INPLACE || needed < *pcchUnescaped) {
1175 needed++; /* add one for the '\0' */
1178 if(!(dwFlags & URL_UNESCAPE_INPLACE))
1179 *pcchUnescaped = needed;
1182 TRACE("result %s\n", (dwFlags & URL_UNESCAPE_INPLACE) ?
1183 debugstr_a(pszUrl) : debugstr_a(pszUnescaped));
1189 /*************************************************************************
1190 * UrlUnescapeW [SHLWAPI.@]
1194 HRESULT WINAPI UrlUnescapeW(
1196 LPWSTR pszUnescaped,
1197 LPDWORD pcchUnescaped,
1204 BOOL stop_unescaping = FALSE;
1206 TRACE("(%s, %p, %p, 0x%08x)\n", debugstr_w(pszUrl), pszUnescaped,
1207 pcchUnescaped, dwFlags);
1209 if(!pszUrl || (!pszUnescaped && !(dwFlags & URL_UNESCAPE_INPLACE))|| !pcchUnescaped)
1210 return E_INVALIDARG;
1212 if(dwFlags & URL_UNESCAPE_INPLACE)
1217 for(src = pszUrl, needed = 0; *src; src++, needed++) {
1218 if(dwFlags & URL_DONT_UNESCAPE_EXTRA_INFO &&
1219 (*src == L'#' || *src == L'?')) {
1220 stop_unescaping = TRUE;
1222 } else if(*src == L'%' && isxdigitW(*(src + 1)) && isxdigitW(*(src + 2))
1223 && stop_unescaping == FALSE) {
1225 WCHAR buf[5] = {'0','x',0};
1226 memcpy(buf + 2, src + 1, 2*sizeof(WCHAR));
1228 StrToIntExW(buf, STIF_SUPPORT_HEX, &ih);
1230 src += 2; /* Advance to end of escape */
1234 if(dwFlags & URL_UNESCAPE_INPLACE || needed < *pcchUnescaped)
1238 if(dwFlags & URL_UNESCAPE_INPLACE || needed < *pcchUnescaped) {
1242 needed++; /* add one for the '\0' */
1245 if(!(dwFlags & URL_UNESCAPE_INPLACE))
1246 *pcchUnescaped = needed;
1249 TRACE("result %s\n", (dwFlags & URL_UNESCAPE_INPLACE) ?
1250 debugstr_w(pszUrl) : debugstr_w(pszUnescaped));
1256 /*************************************************************************
1257 * UrlGetLocationA [SHLWAPI.@]
1259 * Get the location from a Url.
1262 * pszUrl [I] Url to get the location from
1265 * A pointer to the start of the location in pszUrl, or NULL if there is
1269 * - MSDN erroneously states that "The location is the segment of the Url
1270 * starting with a '?' or '#' character". Neither V4 nor V5 of shlwapi.dll
1271 * stop at '?' and always return a NULL in this case.
1272 * - MSDN also erroneously states that "If a file URL has a query string,
1273 * the returned string is the query string". In all tested cases, if the
1274 * Url starts with "fi" then a NULL is returned. V5 gives the following results:
1277 *| NULL file://aa/b/cd#hohoh
1278 *| #hohoh http://aa/b/cd#hohoh
1279 *| NULL fi://aa/b/cd#hohoh
1280 *| #hohoh ff://aa/b/cd#hohoh
1282 LPCSTR WINAPI UrlGetLocationA(
1288 base.cbSize = sizeof(base);
1289 res1 = ParseURLA(pszUrl, &base);
1290 if (res1) return NULL; /* invalid scheme */
1292 /* if scheme is file: then never return pointer */
1293 if (strncmp(base.pszProtocol, "file", min(4,base.cchProtocol)) == 0) return NULL;
1295 /* Look for '#' and return its addr */
1296 return strchr(base.pszSuffix, '#');
1299 /*************************************************************************
1300 * UrlGetLocationW [SHLWAPI.@]
1302 * See UrlGetLocationA.
1304 LPCWSTR WINAPI UrlGetLocationW(
1310 base.cbSize = sizeof(base);
1311 res1 = ParseURLW(pszUrl, &base);
1312 if (res1) return NULL; /* invalid scheme */
1314 /* if scheme is file: then never return pointer */
1315 if (strncmpW(base.pszProtocol, fileW, min(4,base.cchProtocol)) == 0) return NULL;
1317 /* Look for '#' and return its addr */
1318 return strchrW(base.pszSuffix, L'#');
1321 /*************************************************************************
1322 * UrlCompareA [SHLWAPI.@]
1327 * pszUrl1 [I] First Url to compare
1328 * pszUrl2 [I] Url to compare to pszUrl1
1329 * fIgnoreSlash [I] TRUE = compare only up to a final slash
1332 * less than zero, zero, or greater than zero indicating pszUrl2 is greater
1333 * than, equal to, or less than pszUrl1 respectively.
1335 INT WINAPI UrlCompareA(
1340 INT ret, len, len1, len2;
1343 return strcmp(pszUrl1, pszUrl2);
1344 len1 = strlen(pszUrl1);
1345 if (pszUrl1[len1-1] == '/') len1--;
1346 len2 = strlen(pszUrl2);
1347 if (pszUrl2[len2-1] == '/') len2--;
1349 return strncmp(pszUrl1, pszUrl2, len1);
1350 len = min(len1, len2);
1351 ret = strncmp(pszUrl1, pszUrl2, len);
1352 if (ret) return ret;
1353 if (len1 > len2) return 1;
1357 /*************************************************************************
1358 * UrlCompareW [SHLWAPI.@]
1362 INT WINAPI UrlCompareW(
1368 size_t len, len1, len2;
1371 return strcmpW(pszUrl1, pszUrl2);
1372 len1 = strlenW(pszUrl1);
1373 if (pszUrl1[len1-1] == '/') len1--;
1374 len2 = strlenW(pszUrl2);
1375 if (pszUrl2[len2-1] == '/') len2--;
1377 return strncmpW(pszUrl1, pszUrl2, len1);
1378 len = min(len1, len2);
1379 ret = strncmpW(pszUrl1, pszUrl2, len);
1380 if (ret) return ret;
1381 if (len1 > len2) return 1;
1385 /*************************************************************************
1386 * HashData [SHLWAPI.@]
1388 * Hash an input block into a variable sized digest.
1391 * lpSrc [I] Input block
1392 * nSrcLen [I] Length of lpSrc
1393 * lpDest [I] Output for hash digest
1394 * nDestLen [I] Length of lpDest
1397 * Success: TRUE. lpDest is filled with the computed hash value.
1398 * Failure: FALSE, if any argument is invalid.
1400 HRESULT WINAPI HashData(const unsigned char *lpSrc, DWORD nSrcLen,
1401 unsigned char *lpDest, DWORD nDestLen)
1403 INT srcCount = nSrcLen - 1, destCount = nDestLen - 1;
1405 if (IsBadReadPtr(lpSrc, nSrcLen) ||
1406 IsBadWritePtr(lpDest, nDestLen))
1407 return E_INVALIDARG;
1409 while (destCount >= 0)
1411 lpDest[destCount] = (destCount & 0xff);
1415 while (srcCount >= 0)
1417 destCount = nDestLen - 1;
1418 while (destCount >= 0)
1420 lpDest[destCount] = HashDataLookup[lpSrc[srcCount] ^ lpDest[destCount]];
1428 /*************************************************************************
1429 * UrlHashA [SHLWAPI.@]
1431 * Produce a Hash from a Url.
1434 * pszUrl [I] Url to hash
1435 * lpDest [O] Destinationh for hash
1436 * nDestLen [I] Length of lpDest
1439 * Success: S_OK. lpDest is filled with the computed hash value.
1440 * Failure: E_INVALIDARG, if any argument is invalid.
1442 HRESULT WINAPI UrlHashA(LPCSTR pszUrl, unsigned char *lpDest, DWORD nDestLen)
1444 if (IsBadStringPtrA(pszUrl, -1) || IsBadWritePtr(lpDest, nDestLen))
1445 return E_INVALIDARG;
1447 HashData((const BYTE*)pszUrl, (int)strlen(pszUrl), lpDest, nDestLen);
1451 /*************************************************************************
1452 * UrlHashW [SHLWAPI.@]
1456 HRESULT WINAPI UrlHashW(LPCWSTR pszUrl, unsigned char *lpDest, DWORD nDestLen)
1458 char szUrl[MAX_PATH];
1460 TRACE("(%s,%p,%d)\n",debugstr_w(pszUrl), lpDest, nDestLen);
1462 if (IsBadStringPtrW(pszUrl, -1) || IsBadWritePtr(lpDest, nDestLen))
1463 return E_INVALIDARG;
1465 /* Win32 hashes the data as an ASCII string, presumably so that both A+W
1466 * return the same digests for the same URL.
1468 WideCharToMultiByte(0, 0, pszUrl, -1, szUrl, MAX_PATH, 0, 0);
1469 HashData((const BYTE*)szUrl, (int)strlen(szUrl), lpDest, nDestLen);
1473 /*************************************************************************
1474 * UrlApplySchemeA [SHLWAPI.@]
1476 * Apply a scheme to a Url.
1479 * pszIn [I] Url to apply scheme to
1480 * pszOut [O] Destination for modified Url
1481 * pcchOut [I/O] Length of pszOut/destination for length of pszOut
1482 * dwFlags [I] URL_ flags from "shlwapi.h"
1485 * Success: S_OK: pszOut contains the modified Url, pcchOut contains its length.
1486 * Failure: An HRESULT error code describing the error.
1488 HRESULT WINAPI UrlApplySchemeA(LPCSTR pszIn, LPSTR pszOut, LPDWORD pcchOut, DWORD dwFlags)
1491 DWORD ret, len, len2;
1493 TRACE("(in %s, out size %d, flags %08x) using W version\n",
1494 debugstr_a(pszIn), *pcchOut, dwFlags);
1496 in = HeapAlloc(GetProcessHeap(), 0,
1497 (2*INTERNET_MAX_URL_LENGTH) * sizeof(WCHAR));
1498 out = in + INTERNET_MAX_URL_LENGTH;
1500 MultiByteToWideChar(0, 0, pszIn, -1, in, INTERNET_MAX_URL_LENGTH);
1501 len = INTERNET_MAX_URL_LENGTH;
1503 ret = UrlApplySchemeW(in, out, &len, dwFlags);
1504 if ((ret != S_OK) && (ret != S_FALSE)) {
1505 HeapFree(GetProcessHeap(), 0, in);
1509 len2 = WideCharToMultiByte(0, 0, out, len+1, 0, 0, 0, 0);
1510 if (len2 > *pcchOut) {
1512 HeapFree(GetProcessHeap(), 0, in);
1515 WideCharToMultiByte(0, 0, out, len+1, pszOut, *pcchOut, 0, 0);
1517 HeapFree(GetProcessHeap(), 0, in);
1521 static HRESULT URL_GuessScheme(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut)
1526 DWORD value_len, data_len, dwType, i;
1527 WCHAR reg_path[MAX_PATH];
1528 WCHAR value[MAX_PATH], data[MAX_PATH];
1531 MultiByteToWideChar(0, 0,
1532 "Software\\Microsoft\\Windows\\CurrentVersion\\URL\\Prefixes",
1533 -1, reg_path, MAX_PATH);
1534 RegOpenKeyExW(HKEY_LOCAL_MACHINE, reg_path, 0, 1, &newkey);
1536 while(value_len = data_len = MAX_PATH,
1537 RegEnumValueW(newkey, index, value, &value_len,
1538 0, &dwType, (LPVOID)data, &data_len) == 0) {
1539 TRACE("guess %d %s is %s\n",
1540 index, debugstr_w(value), debugstr_w(data));
1543 for(i=0; i<value_len; i++) {
1546 /* remember that TRUE is not-equal */
1547 j = ChrCmpIW(Wxx, Wyy);
1550 if ((i == value_len) && !j) {
1551 if (strlenW(data) + strlenW(pszIn) + 1 > *pcchOut) {
1552 *pcchOut = strlenW(data) + strlenW(pszIn) + 1;
1553 RegCloseKey(newkey);
1556 strcpyW(pszOut, data);
1557 strcatW(pszOut, pszIn);
1558 *pcchOut = strlenW(pszOut);
1559 TRACE("matched and set to %s\n", debugstr_w(pszOut));
1560 RegCloseKey(newkey);
1565 RegCloseKey(newkey);
1569 static HRESULT URL_ApplyDefault(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut)
1572 DWORD data_len, dwType;
1573 WCHAR reg_path[MAX_PATH];
1574 WCHAR value[MAX_PATH], data[MAX_PATH];
1576 /* get and prepend default */
1577 MultiByteToWideChar(0, 0,
1578 "Software\\Microsoft\\Windows\\CurrentVersion\\URL\\DefaultPrefix",
1579 -1, reg_path, MAX_PATH);
1580 RegOpenKeyExW(HKEY_LOCAL_MACHINE, reg_path, 0, 1, &newkey);
1581 data_len = MAX_PATH;
1584 RegQueryValueExW(newkey, value, 0, &dwType, (LPBYTE)data, &data_len);
1585 RegCloseKey(newkey);
1586 if (strlenW(data) + strlenW(pszIn) + 1 > *pcchOut) {
1587 *pcchOut = strlenW(data) + strlenW(pszIn) + 1;
1590 strcpyW(pszOut, data);
1591 strcatW(pszOut, pszIn);
1592 *pcchOut = strlenW(pszOut);
1593 TRACE("used default %s\n", debugstr_w(pszOut));
1597 /*************************************************************************
1598 * UrlApplySchemeW [SHLWAPI.@]
1600 * See UrlApplySchemeA.
1602 HRESULT WINAPI UrlApplySchemeW(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut, DWORD dwFlags)
1604 PARSEDURLW in_scheme;
1608 TRACE("(in %s, out size %d, flags %08x)\n",
1609 debugstr_w(pszIn), *pcchOut, dwFlags);
1611 if (dwFlags & URL_APPLY_GUESSFILE) {
1612 FIXME("(%s %p %p(%d) 0x%08x): stub URL_APPLY_GUESSFILE not implemented\n",
1613 debugstr_w(pszIn), pszOut, pcchOut, *pcchOut, dwFlags);
1614 strcpyW(pszOut, pszIn);
1615 *pcchOut = strlenW(pszOut);
1619 in_scheme.cbSize = sizeof(in_scheme);
1620 /* See if the base has a scheme */
1621 res1 = ParseURLW(pszIn, &in_scheme);
1623 /* no scheme in input, need to see if we need to guess */
1624 if (dwFlags & URL_APPLY_GUESSSCHEME) {
1625 if ((ret = URL_GuessScheme(pszIn, pszOut, pcchOut)) != -1)
1630 /* we have a scheme, see if valid (known scheme) */
1631 if (in_scheme.nScheme) {
1632 /* have valid scheme, so just copy and exit */
1633 if (strlenW(pszIn) + 1 > *pcchOut) {
1634 *pcchOut = strlenW(pszIn) + 1;
1637 strcpyW(pszOut, pszIn);
1638 *pcchOut = strlenW(pszOut);
1639 TRACE("valid scheme, returning copy\n");
1644 /* If we are here, then either invalid scheme,
1645 * or no scheme and can't/failed guess.
1647 if ( ( ((res1 == 0) && (dwFlags & URL_APPLY_FORCEAPPLY)) ||
1649 (dwFlags & URL_APPLY_DEFAULT)) {
1650 /* find and apply default scheme */
1651 return URL_ApplyDefault(pszIn, pszOut, pcchOut);
1654 /* just copy and give proper return code */
1655 if (strlenW(pszIn) + 1 > *pcchOut) {
1656 *pcchOut = strlenW(pszIn) + 1;
1659 strcpyW(pszOut, pszIn);
1660 *pcchOut = strlenW(pszOut);
1661 TRACE("returning copy, left alone\n");
1665 /*************************************************************************
1666 * UrlIsA [SHLWAPI.@]
1668 * Determine if a Url is of a certain class.
1671 * pszUrl [I] Url to check
1672 * Urlis [I] URLIS_ constant from "shlwapi.h"
1675 * TRUE if pszUrl belongs to the class type in Urlis.
1678 BOOL WINAPI UrlIsA(LPCSTR pszUrl, URLIS Urlis)
1684 TRACE("(%s %d)\n", debugstr_a(pszUrl), Urlis);
1689 base.cbSize = sizeof(base);
1690 res1 = ParseURLA(pszUrl, &base);
1691 if (res1) return FALSE; /* invalid scheme */
1692 switch (base.nScheme)
1694 case URL_SCHEME_MAILTO:
1695 case URL_SCHEME_SHELL:
1696 case URL_SCHEME_JAVASCRIPT:
1697 case URL_SCHEME_VBSCRIPT:
1698 case URL_SCHEME_ABOUT:
1704 return !StrCmpNA("file:", pszUrl, 5);
1706 case URLIS_DIRECTORY:
1707 last = pszUrl + strlen(pszUrl) - 1;
1708 return (last >= pszUrl && (*last == '/' || *last == '\\' ));
1711 return PathIsURLA(pszUrl);
1713 case URLIS_NOHISTORY:
1714 case URLIS_APPLIABLE:
1715 case URLIS_HASQUERY:
1717 FIXME("(%s %d): stub\n", debugstr_a(pszUrl), Urlis);
1722 /*************************************************************************
1723 * UrlIsW [SHLWAPI.@]
1727 BOOL WINAPI UrlIsW(LPCWSTR pszUrl, URLIS Urlis)
1729 static const WCHAR stemp[] = { 'f','i','l','e',':',0 };
1734 TRACE("(%s %d)\n", debugstr_w(pszUrl), Urlis);
1739 base.cbSize = sizeof(base);
1740 res1 = ParseURLW(pszUrl, &base);
1741 if (res1) return FALSE; /* invalid scheme */
1742 switch (base.nScheme)
1744 case URL_SCHEME_MAILTO:
1745 case URL_SCHEME_SHELL:
1746 case URL_SCHEME_JAVASCRIPT:
1747 case URL_SCHEME_VBSCRIPT:
1748 case URL_SCHEME_ABOUT:
1754 return !strncmpW(stemp, pszUrl, 5);
1756 case URLIS_DIRECTORY:
1757 last = pszUrl + strlenW(pszUrl) - 1;
1758 return (last >= pszUrl && (*last == '/' || *last == '\\'));
1761 return PathIsURLW(pszUrl);
1763 case URLIS_NOHISTORY:
1764 case URLIS_APPLIABLE:
1765 case URLIS_HASQUERY:
1767 FIXME("(%s %d): stub\n", debugstr_w(pszUrl), Urlis);
1772 /*************************************************************************
1773 * UrlIsNoHistoryA [SHLWAPI.@]
1775 * Determine if a Url should not be stored in the users history list.
1778 * pszUrl [I] Url to check
1781 * TRUE, if pszUrl should be excluded from the history list,
1784 BOOL WINAPI UrlIsNoHistoryA(LPCSTR pszUrl)
1786 return UrlIsA(pszUrl, URLIS_NOHISTORY);
1789 /*************************************************************************
1790 * UrlIsNoHistoryW [SHLWAPI.@]
1792 * See UrlIsNoHistoryA.
1794 BOOL WINAPI UrlIsNoHistoryW(LPCWSTR pszUrl)
1796 return UrlIsW(pszUrl, URLIS_NOHISTORY);
1799 /*************************************************************************
1800 * UrlIsOpaqueA [SHLWAPI.@]
1802 * Determine if a Url is opaque.
1805 * pszUrl [I] Url to check
1808 * TRUE if pszUrl is opaque,
1812 * An opaque Url is one that does not start with "<protocol>://".
1814 BOOL WINAPI UrlIsOpaqueA(LPCSTR pszUrl)
1816 return UrlIsA(pszUrl, URLIS_OPAQUE);
1819 /*************************************************************************
1820 * UrlIsOpaqueW [SHLWAPI.@]
1824 BOOL WINAPI UrlIsOpaqueW(LPCWSTR pszUrl)
1826 return UrlIsW(pszUrl, URLIS_OPAQUE);
1829 /*************************************************************************
1830 * Scans for characters of type "type" and when not matching found,
1831 * returns pointer to it and length in size.
1833 * Characters tested based on RFC 1738
1835 static LPCWSTR URL_ScanID(LPCWSTR start, LPDWORD size, WINE_URL_SCAN_TYPE type)
1837 static DWORD alwayszero = 0;
1846 if ( (islowerW(*start) && isalphaW(*start)) ||
1861 if ( isalphaW(*start) ||
1863 /* user/password only characters */
1868 /* *extra* characters */
1871 (*start == L'\'') ||
1875 /* *safe* characters */
1883 } else if (*start == L'%') {
1884 if (isxdigitW(*(start+1)) &&
1885 isxdigitW(*(start+2))) {
1897 if (isdigitW(*start)) {
1908 if (isalnumW(*start) ||
1910 (*start == L'.') ) {
1919 FIXME("unknown type %d\n", type);
1920 return (LPWSTR)&alwayszero;
1922 /* TRACE("scanned %d characters next char %p<%c>\n",
1923 *size, start, *start); */
1927 /*************************************************************************
1928 * Attempt to parse URL into pieces.
1930 static LONG URL_ParseUrl(LPCWSTR pszUrl, WINE_PARSE_URL *pl)
1934 memset(pl, 0, sizeof(WINE_PARSE_URL));
1935 pl->pScheme = pszUrl;
1936 work = URL_ScanID(pl->pScheme, &pl->szScheme, SCHEME);
1937 if (!*work || (*work != L':')) goto ErrorExit;
1939 if ((*work != L'/') || (*(work+1) != L'/')) goto ErrorExit;
1940 pl->pUserName = work + 2;
1941 work = URL_ScanID(pl->pUserName, &pl->szUserName, USERPASS);
1942 if (*work == L':' ) {
1943 /* parse password */
1945 pl->pPassword = work;
1946 work = URL_ScanID(pl->pPassword, &pl->szPassword, USERPASS);
1947 if (*work != L'@') {
1948 /* what we just parsed must be the hostname and port
1949 * so reset pointers and clear then let it parse */
1950 pl->szUserName = pl->szPassword = 0;
1951 work = pl->pUserName - 1;
1952 pl->pUserName = pl->pPassword = 0;
1954 } else if (*work == L'@') {
1958 } else if (!*work || (*work == L'/') || (*work == L'.')) {
1959 /* what was parsed was hostname, so reset pointers and let it parse */
1960 pl->szUserName = pl->szPassword = 0;
1961 work = pl->pUserName - 1;
1962 pl->pUserName = pl->pPassword = 0;
1963 } else goto ErrorExit;
1965 /* now start parsing hostname or hostnumber */
1967 pl->pHostName = work;
1968 work = URL_ScanID(pl->pHostName, &pl->szHostName, HOST);
1969 if (*work == L':') {
1973 work = URL_ScanID(pl->pPort, &pl->szPort, PORT);
1975 if (*work == L'/') {
1976 /* see if query string */
1977 pl->pQuery = strchrW(work, L'?');
1978 if (pl->pQuery) pl->szQuery = strlenW(pl->pQuery);
1980 TRACE("parse successful: scheme=%p(%d), user=%p(%d), pass=%p(%d), host=%p(%d), port=%p(%d), query=%p(%d)\n",
1981 pl->pScheme, pl->szScheme,
1982 pl->pUserName, pl->szUserName,
1983 pl->pPassword, pl->szPassword,
1984 pl->pHostName, pl->szHostName,
1985 pl->pPort, pl->szPort,
1986 pl->pQuery, pl->szQuery);
1989 FIXME("failed to parse %s\n", debugstr_w(pszUrl));
1990 return E_INVALIDARG;
1993 /*************************************************************************
1994 * UrlGetPartA [SHLWAPI.@]
1996 * Retrieve part of a Url.
1999 * pszIn [I] Url to parse
2000 * pszOut [O] Destination for part of pszIn requested
2001 * pcchOut [I] Size of pszOut
2002 * [O] length of pszOut string EXLUDING '\0' if S_OK, otherwise
2003 * needed size of pszOut INCLUDING '\0'.
2004 * dwPart [I] URL_PART_ enum from "shlwapi.h"
2005 * dwFlags [I] URL_ flags from "shlwapi.h"
2008 * Success: S_OK. pszOut contains the part requested, pcchOut contains its length.
2009 * Failure: An HRESULT error code describing the error.
2011 HRESULT WINAPI UrlGetPartA(LPCSTR pszIn, LPSTR pszOut, LPDWORD pcchOut,
2012 DWORD dwPart, DWORD dwFlags)
2015 DWORD ret, len, len2;
2017 in = HeapAlloc(GetProcessHeap(), 0,
2018 (2*INTERNET_MAX_URL_LENGTH) * sizeof(WCHAR));
2019 out = in + INTERNET_MAX_URL_LENGTH;
2021 MultiByteToWideChar(0, 0, pszIn, -1, in, INTERNET_MAX_URL_LENGTH);
2023 len = INTERNET_MAX_URL_LENGTH;
2024 ret = UrlGetPartW(in, out, &len, dwPart, dwFlags);
2027 HeapFree(GetProcessHeap(), 0, in);
2031 len2 = WideCharToMultiByte(0, 0, out, len, 0, 0, 0, 0);
2032 if (len2 > *pcchOut) {
2034 HeapFree(GetProcessHeap(), 0, in);
2037 WideCharToMultiByte(0, 0, out, len+1, pszOut, *pcchOut, 0, 0);
2039 HeapFree(GetProcessHeap(), 0, in);
2043 /*************************************************************************
2044 * UrlGetPartW [SHLWAPI.@]
2048 HRESULT WINAPI UrlGetPartW(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut,
2049 DWORD dwPart, DWORD dwFlags)
2053 DWORD size, schsize;
2054 LPCWSTR addr, schaddr;
2056 TRACE("(%s %p %p(%d) %08x %08x)\n",
2057 debugstr_w(pszIn), pszOut, pcchOut, *pcchOut, dwPart, dwFlags);
2059 ret = URL_ParseUrl(pszIn, &pl);
2061 schaddr = pl.pScheme;
2062 schsize = pl.szScheme;
2065 case URL_PART_SCHEME:
2066 if (!pl.szScheme) return E_INVALIDARG;
2071 case URL_PART_HOSTNAME:
2072 if (!pl.szHostName) return E_INVALIDARG;
2073 addr = pl.pHostName;
2074 size = pl.szHostName;
2077 case URL_PART_USERNAME:
2078 if (!pl.szUserName) return E_INVALIDARG;
2079 addr = pl.pUserName;
2080 size = pl.szUserName;
2083 case URL_PART_PASSWORD:
2084 if (!pl.szPassword) return E_INVALIDARG;
2085 addr = pl.pPassword;
2086 size = pl.szPassword;
2090 if (!pl.szPort) return E_INVALIDARG;
2095 case URL_PART_QUERY:
2096 if (!pl.szQuery) return E_INVALIDARG;
2102 return E_INVALIDARG;
2105 if (dwFlags == URL_PARTFLAG_KEEPSCHEME) {
2106 if (*pcchOut < schsize + size + 2) {
2107 *pcchOut = schsize + size + 2;
2110 memcpy(pszOut, schaddr, schsize*sizeof(WCHAR));
2111 pszOut[schsize] = ':';
2112 memcpy(pszOut+schsize+1, addr, size*sizeof(WCHAR));
2113 pszOut[schsize+1+size] = 0;
2114 *pcchOut = schsize + 1 + size;
2117 if (*pcchOut < size + 1) {*pcchOut = size+1; return E_POINTER;}
2118 memcpy(pszOut, addr, size*sizeof(WCHAR));
2122 TRACE("len=%d %s\n", *pcchOut, debugstr_w(pszOut));
2127 /*************************************************************************
2128 * PathIsURLA [SHLWAPI.@]
2130 * Check if the given path is a Url.
2133 * lpszPath [I] Path to check.
2136 * TRUE if lpszPath is a Url.
2137 * FALSE if lpszPath is NULL or not a Url.
2139 BOOL WINAPI PathIsURLA(LPCSTR lpstrPath)
2144 if (!lpstrPath || !*lpstrPath) return FALSE;
2147 base.cbSize = sizeof(base);
2148 res1 = ParseURLA(lpstrPath, &base);
2149 return (base.nScheme != URL_SCHEME_INVALID);
2152 /*************************************************************************
2153 * PathIsURLW [SHLWAPI.@]
2157 BOOL WINAPI PathIsURLW(LPCWSTR lpstrPath)
2162 if (!lpstrPath || !*lpstrPath) return FALSE;
2165 base.cbSize = sizeof(base);
2166 res1 = ParseURLW(lpstrPath, &base);
2167 return (base.nScheme != URL_SCHEME_INVALID);
2170 /*************************************************************************
2171 * UrlCreateFromPathA [SHLWAPI.@]
2173 * See UrlCreateFromPathW
2175 HRESULT WINAPI UrlCreateFromPathA(LPCSTR pszPath, LPSTR pszUrl, LPDWORD pcchUrl, DWORD dwReserved)
2177 WCHAR bufW[INTERNET_MAX_URL_LENGTH];
2179 UNICODE_STRING pathW;
2181 DWORD lenW = sizeof(bufW)/sizeof(WCHAR), lenA;
2183 if(!RtlCreateUnicodeStringFromAsciiz(&pathW, pszPath))
2184 return E_INVALIDARG;
2185 if((ret = UrlCreateFromPathW(pathW.Buffer, urlW, &lenW, dwReserved)) == E_POINTER) {
2186 urlW = HeapAlloc(GetProcessHeap(), 0, lenW * sizeof(WCHAR));
2187 ret = UrlCreateFromPathW(pathW.Buffer, urlW, &lenW, dwReserved);
2189 if(ret == S_OK || ret == S_FALSE) {
2190 RtlUnicodeToMultiByteSize(&lenA, urlW, lenW * sizeof(WCHAR));
2191 if(*pcchUrl > lenA) {
2192 RtlUnicodeToMultiByteN(pszUrl, *pcchUrl - 1, &lenA, urlW, lenW * sizeof(WCHAR));
2196 *pcchUrl = lenA + 1;
2200 if(urlW != bufW) HeapFree(GetProcessHeap(), 0, urlW);
2201 RtlFreeUnicodeString(&pathW);
2205 /*************************************************************************
2206 * UrlCreateFromPathW [SHLWAPI.@]
2208 * Create a Url from a file path.
2211 * pszPath [I] Path to convert
2212 * pszUrl [O] Destination for the converted Url
2213 * pcchUrl [I/O] Length of pszUrl
2214 * dwReserved [I] Reserved, must be 0
2217 * Success: S_OK pszUrl contains the converted path, S_FALSE if the path is already a Url
2218 * Failure: An HRESULT error code.
2220 HRESULT WINAPI UrlCreateFromPathW(LPCWSTR pszPath, LPWSTR pszUrl, LPDWORD pcchUrl, DWORD dwReserved)
2225 WCHAR file_colonW[] = {'f','i','l','e',':',0};
2226 WCHAR three_slashesW[] = {'/','/','/',0};
2227 PARSEDURLW parsed_url;
2229 TRACE("(%s, %p, %p, 0x%08x)\n", debugstr_w(pszPath), pszUrl, pcchUrl, dwReserved);
2231 /* Validate arguments */
2232 if (dwReserved != 0)
2233 return E_INVALIDARG;
2234 if (!pszUrl || !pcchUrl)
2235 return E_INVALIDARG;
2238 parsed_url.cbSize = sizeof(parsed_url);
2239 if(ParseURLW(pszPath, &parsed_url) == S_OK) {
2240 if(parsed_url.nScheme != URL_SCHEME_INVALID && parsed_url.cchProtocol > 1) {
2241 needed = strlenW(pszPath);
2242 if (needed >= *pcchUrl) {
2243 *pcchUrl = needed + 1;
2247 strcpyW(pszUrl, pszPath);
2253 pszNewUrl = HeapAlloc(GetProcessHeap(), 0, (strlenW(pszPath) + 9) * sizeof(WCHAR)); /* "file:///" + pszPath_len + 1 */
2254 strcpyW(pszNewUrl, file_colonW);
2255 if(isalphaW(pszPath[0]) && pszPath[1] == ':')
2256 strcatW(pszNewUrl, three_slashesW);
2257 strcatW(pszNewUrl, pszPath);
2258 ret = UrlEscapeW(pszNewUrl, pszUrl, pcchUrl, URL_ESCAPE_PERCENT);
2260 HeapFree(GetProcessHeap(), 0, pszNewUrl);
2264 /*************************************************************************
2265 * SHAutoComplete [SHLWAPI.@]
2267 * Enable auto-completion for an edit control.
2270 * hwndEdit [I] Handle of control to enable auto-completion for
2271 * dwFlags [I] SHACF_ flags from "shlwapi.h"
2274 * Success: S_OK. Auto-completion is enabled for the control.
2275 * Failure: An HRESULT error code indicating the error.
2277 HRESULT WINAPI SHAutoComplete(HWND hwndEdit, DWORD dwFlags)
2279 FIXME("SHAutoComplete stub\n");
2283 /*************************************************************************
2284 * MLBuildResURLA [SHLWAPI.405]
2286 * Create a Url pointing to a resource in a module.
2289 * lpszLibName [I] Name of the module containing the resource
2290 * hMod [I] Callers module handle
2291 * dwFlags [I] Undocumented flags for loading the module
2292 * lpszRes [I] Resource name
2293 * lpszDest [O] Destination for resulting Url
2294 * dwDestLen [I] Length of lpszDest
2297 * Success: S_OK. lpszDest constains the resource Url.
2298 * Failure: E_INVALIDARG, if any argument is invalid, or
2299 * E_FAIL if dwDestLen is too small.
2301 HRESULT WINAPI MLBuildResURLA(LPCSTR lpszLibName, HMODULE hMod, DWORD dwFlags,
2302 LPCSTR lpszRes, LPSTR lpszDest, DWORD dwDestLen)
2304 WCHAR szLibName[MAX_PATH], szRes[MAX_PATH], szDest[MAX_PATH];
2308 MultiByteToWideChar(CP_ACP, 0, lpszLibName, -1, szLibName, sizeof(szLibName)/sizeof(WCHAR));
2311 MultiByteToWideChar(CP_ACP, 0, lpszRes, -1, szRes, sizeof(szRes)/sizeof(WCHAR));
2313 if (dwDestLen > sizeof(szLibName)/sizeof(WCHAR))
2314 dwDestLen = sizeof(szLibName)/sizeof(WCHAR);
2316 hRet = MLBuildResURLW(lpszLibName ? szLibName : NULL, hMod, dwFlags,
2317 lpszRes ? szRes : NULL, lpszDest ? szDest : NULL, dwDestLen);
2318 if (SUCCEEDED(hRet) && lpszDest)
2319 WideCharToMultiByte(CP_ACP, 0, szDest, -1, lpszDest, dwDestLen, 0, 0);
2324 /*************************************************************************
2325 * MLBuildResURLA [SHLWAPI.406]
2327 * See MLBuildResURLA.
2329 HRESULT WINAPI MLBuildResURLW(LPCWSTR lpszLibName, HMODULE hMod, DWORD dwFlags,
2330 LPCWSTR lpszRes, LPWSTR lpszDest, DWORD dwDestLen)
2332 static const WCHAR szRes[] = { 'r','e','s',':','/','/','\0' };
2333 #define szResLen ((sizeof(szRes) - sizeof(WCHAR))/sizeof(WCHAR))
2334 HRESULT hRet = E_FAIL;
2336 TRACE("(%s,%p,0x%08x,%s,%p,%d)\n", debugstr_w(lpszLibName), hMod, dwFlags,
2337 debugstr_w(lpszRes), lpszDest, dwDestLen);
2339 if (!lpszLibName || !hMod || hMod == INVALID_HANDLE_VALUE || !lpszRes ||
2340 !lpszDest || (dwFlags && dwFlags != 2))
2341 return E_INVALIDARG;
2343 if (dwDestLen >= szResLen + 1)
2345 dwDestLen -= (szResLen + 1);
2346 memcpy(lpszDest, szRes, sizeof(szRes));
2348 hMod = MLLoadLibraryW(lpszLibName, hMod, dwFlags);
2352 WCHAR szBuff[MAX_PATH];
2355 len = GetModuleFileNameW(hMod, szBuff, sizeof(szBuff)/sizeof(WCHAR));
2356 if (len && len < sizeof(szBuff)/sizeof(WCHAR))
2358 DWORD dwPathLen = strlenW(szBuff) + 1;
2360 if (dwDestLen >= dwPathLen)
2364 dwDestLen -= dwPathLen;
2365 memcpy(lpszDest + szResLen, szBuff, dwPathLen * sizeof(WCHAR));
2367 dwResLen = strlenW(lpszRes) + 1;
2368 if (dwDestLen >= dwResLen + 1)
2370 lpszDest[szResLen + dwPathLen + dwResLen] = '/';
2371 memcpy(lpszDest + szResLen + dwPathLen, lpszRes, dwResLen * sizeof(WCHAR));
2376 MLFreeLibrary(hMod);