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 /*************************************************************************
135 * Parse a Url into its constituent parts.
139 * y [O] Undocumented structure holding the parsed information
142 * Success: S_OK. y contains the parsed Url details.
143 * Failure: An HRESULT error code.
145 HRESULT WINAPI ParseURLA(LPCSTR x, PARSEDURLA *y)
147 WCHAR scheme[INTERNET_MAX_SCHEME_LENGTH];
150 y->nScheme = URL_SCHEME_INVALID;
151 if (y->cbSize != sizeof(*y)) return E_INVALIDARG;
152 /* FIXME: leading white space generates error of 0x80041001 which
155 if (*x <= ' ') return 0x80041001;
161 y->cchProtocol = cnt;
170 /* check for no scheme in string start */
171 /* (apparently schemes *must* be larger than a single character) */
172 if ((*x == '\0') || (y->cchProtocol <= 1)) {
173 y->pszProtocol = NULL;
177 /* found scheme, set length of remainder */
178 y->cchSuffix = lstrlenA(y->pszSuffix);
180 len = MultiByteToWideChar(CP_ACP, 0, y->pszProtocol, y->cchProtocol,
181 scheme, sizeof(scheme)/sizeof(WCHAR));
182 y->nScheme = get_scheme_code(scheme, len);
187 /*************************************************************************
190 * Unicode version of ParseURLA.
192 HRESULT WINAPI ParseURLW(LPCWSTR x, PARSEDURLW *y)
196 y->nScheme = URL_SCHEME_INVALID;
197 if (y->cbSize != sizeof(*y)) return E_INVALIDARG;
198 /* FIXME: leading white space generates error of 0x80041001 which
201 if (*x <= ' ') return 0x80041001;
207 y->cchProtocol = cnt;
216 /* check for no scheme in string start */
217 /* (apparently schemes *must* be larger than a single character) */
218 if ((*x == '\0') || (y->cchProtocol <= 1)) {
219 y->pszProtocol = NULL;
223 /* found scheme, set length of remainder */
224 y->cchSuffix = lstrlenW(y->pszSuffix);
225 y->nScheme = get_scheme_code(y->pszProtocol, y->cchProtocol);
230 /*************************************************************************
231 * UrlCanonicalizeA [SHLWAPI.@]
233 * Canonicalize a Url.
236 * pszUrl [I] Url to cCanonicalize
237 * pszCanonicalized [O] Destination for converted Url.
238 * pcchCanonicalized [I/O] Length of pszUrl, destination for length of pszCanonicalized
239 * dwFlags [I] Flags controlling the conversion.
242 * Success: S_OK. The pszCanonicalized contains the converted Url.
243 * Failure: E_POINTER, if *pcchCanonicalized is too small.
245 * MSDN incorrectly describes the flags for this function. They should be:
246 *| URL_DONT_ESCAPE_EXTRA_INFO 0x02000000
247 *| URL_ESCAPE_SPACES_ONLY 0x04000000
248 *| URL_ESCAPE_PERCENT 0x00001000
249 *| URL_ESCAPE_UNSAFE 0x10000000
250 *| URL_UNESCAPE 0x10000000
251 *| URL_DONT_SIMPLIFY 0x08000000
252 *| URL_ESCAPE_SEGMENT_ONLY 0x00002000
254 HRESULT WINAPI UrlCanonicalizeA(LPCSTR pszUrl, LPSTR pszCanonicalized,
255 LPDWORD pcchCanonicalized, DWORD dwFlags)
257 LPWSTR base, canonical;
261 TRACE("(%s, %p, %p, 0x%08x) *pcchCanonicalized: %d\n", debugstr_a(pszUrl), pszCanonicalized,
262 pcchCanonicalized, dwFlags, pcchCanonicalized ? *pcchCanonicalized : -1);
264 if(!pszUrl || !pszCanonicalized || !pcchCanonicalized)
267 base = HeapAlloc(GetProcessHeap(), 0,
268 (2*INTERNET_MAX_URL_LENGTH) * sizeof(WCHAR));
269 canonical = base + INTERNET_MAX_URL_LENGTH;
271 MultiByteToWideChar(0, 0, pszUrl, -1, base, INTERNET_MAX_URL_LENGTH);
272 len = INTERNET_MAX_URL_LENGTH;
274 ret = UrlCanonicalizeW(base, canonical, &len, dwFlags);
276 *pcchCanonicalized = len * 2;
277 HeapFree(GetProcessHeap(), 0, base);
281 len2 = WideCharToMultiByte(0, 0, canonical, -1, 0, 0, 0, 0);
282 if (len2 > *pcchCanonicalized) {
283 *pcchCanonicalized = len2;
284 HeapFree(GetProcessHeap(), 0, base);
287 WideCharToMultiByte(0, 0, canonical, -1, pszCanonicalized, *pcchCanonicalized, 0, 0);
288 *pcchCanonicalized = len;
289 HeapFree(GetProcessHeap(), 0, base);
293 /*************************************************************************
294 * UrlCanonicalizeW [SHLWAPI.@]
296 * See UrlCanonicalizeA.
298 HRESULT WINAPI UrlCanonicalizeW(LPCWSTR pszUrl, LPWSTR pszCanonicalized,
299 LPDWORD pcchCanonicalized, DWORD dwFlags)
303 LPWSTR lpszUrlCpy, wk1, wk2, mp, mp2, root;
305 DWORD nByteLen, nLen, nWkLen;
308 static const WCHAR wszFile[] = {'f','i','l','e',':'};
309 static const WCHAR wszLocalhost[] = {'l','o','c','a','l','h','o','s','t'};
311 TRACE("(%s, %p, %p, 0x%08x) *pcchCanonicalized: %d\n", debugstr_w(pszUrl), pszCanonicalized,
312 pcchCanonicalized, dwFlags, pcchCanonicalized ? *pcchCanonicalized : -1);
314 if(!pszUrl || !pszCanonicalized || !pcchCanonicalized)
318 *pszCanonicalized = 0;
322 nByteLen = (strlenW(pszUrl) + 1) * sizeof(WCHAR); /* length in bytes */
323 lpszUrlCpy = HeapAlloc(GetProcessHeap(), 0,
324 INTERNET_MAX_URL_LENGTH * sizeof(WCHAR));
326 if((dwFlags & URL_FILE_USE_PATHURL) && nByteLen >= sizeof(wszFile)
327 && !memcmp(wszFile, pszUrl, sizeof(wszFile)))
333 * 1 have 2[+] alnum 2,3
334 * 2 have scheme (found :) 4,6,3
335 * 3 failed (no location)
337 * 5 have 1[+] alnum 6,3
338 * 6 have location (found /) save root location
341 wk1 = (LPWSTR)pszUrl;
345 if(pszUrl[1] == ':') { /* Assume path */
346 static const WCHAR wszFilePrefix[] = {'f','i','l','e',':','/','/','/'};
348 memcpy(wk2, wszFilePrefix, sizeof(wszFilePrefix));
349 wk2 += sizeof(wszFilePrefix)/sizeof(WCHAR);
350 if (dwFlags & URL_FILE_USE_PATHURL)
356 dwFlags |= URL_ESCAPE_UNSAFE;
363 if (!isalnumW(*wk1)) {state = 3; break;}
365 if (!isalnumW(*wk1)) {state = 3; break;}
371 if (*wk1++ == ':') state = 2;
375 if (*wk1 != '/') {state = 6; break;}
377 if((dwFlags & URL_FILE_USE_PATHURL) && nByteLen >= sizeof(wszLocalhost)
378 && !memcmp(wszLocalhost, wk1, sizeof(wszLocalhost))){
379 wk1 += sizeof(wszLocalhost)/sizeof(WCHAR);
380 while(*wk1 == '\\' && (dwFlags & URL_FILE_USE_PATHURL))
383 if(*wk1 == '/' && (dwFlags & URL_FILE_USE_PATHURL))
388 nWkLen = strlenW(wk1);
389 memcpy(wk2, wk1, (nWkLen + 1) * sizeof(WCHAR));
395 if(*mp == '/' || *mp == '\\')
401 if (!isalnumW(*wk1) && (*wk1 != '-') && (*wk1 != '.') && (*wk1 != ':'))
403 while(isalnumW(*wk1) || (*wk1 == '-') || (*wk1 == '.') || (*wk1 == ':'))
410 if (*wk1 != '/' && *wk1 != '\\') {state = 3; break;}
411 while(*wk1 == '/' || *wk1 == '\\') {
418 if(dwFlags & URL_DONT_SIMPLIFY) {
423 /* Now at root location, cannot back up any more. */
424 /* "root" will point at the '/' */
428 mp = strchrW(wk1, '/');
429 mp2 = strchrW(wk1, '\\');
430 if(mp2 && (!mp || mp2 < mp))
433 nWkLen = strlenW(wk1);
434 memcpy(wk2, wk1, (nWkLen + 1) * sizeof(WCHAR));
441 memcpy(wk2, wk1, nLen * sizeof(WCHAR));
449 TRACE("found '/.'\n");
450 if (wk1[1] == '/' || wk1[1] == '\\') {
451 /* case of /./ -> skip the ./ */
454 else if (wk1[1] == '.') {
455 /* found /.. look for next / */
456 TRACE("found '/..'\n");
457 if (wk1[2] == '/' || wk1[2] == '\\' ||wk1[2] == '?'
458 || wk1[2] == '#' || !wk1[2]) {
459 /* case /../ -> need to backup wk2 */
460 TRACE("found '/../'\n");
461 *(wk2-1) = '\0'; /* set end of string */
462 mp = strrchrW(root, slash);
463 if (mp && (mp >= root)) {
464 /* found valid backup point */
466 if(wk1[2] != '/' && wk1[2] != '\\')
472 /* did not find point, restore '/' */
482 FIXME("how did we get here - state=%d\n", state);
483 HeapFree(GetProcessHeap(), 0, lpszUrlCpy);
487 TRACE("Simplified, orig <%s>, simple <%s>\n",
488 debugstr_w(pszUrl), debugstr_w(lpszUrlCpy));
490 nLen = lstrlenW(lpszUrlCpy);
491 while ((nLen > 0) && ((lpszUrlCpy[nLen-1] <= ' ')))
492 lpszUrlCpy[--nLen]=0;
494 if(dwFlags & (URL_UNESCAPE | URL_FILE_USE_PATHURL))
495 UrlUnescapeW(lpszUrlCpy, NULL, &nLen, URL_UNESCAPE_INPLACE);
497 if((EscapeFlags = dwFlags & (URL_ESCAPE_UNSAFE |
498 URL_ESCAPE_SPACES_ONLY |
500 URL_DONT_ESCAPE_EXTRA_INFO |
501 URL_ESCAPE_SEGMENT_ONLY ))) {
502 EscapeFlags &= ~URL_ESCAPE_UNSAFE;
503 hr = UrlEscapeW(lpszUrlCpy, pszCanonicalized, pcchCanonicalized,
505 } else { /* No escaping needed, just copy the string */
506 nLen = lstrlenW(lpszUrlCpy);
507 if(nLen < *pcchCanonicalized)
508 memcpy(pszCanonicalized, lpszUrlCpy, (nLen + 1)*sizeof(WCHAR));
513 *pcchCanonicalized = nLen;
516 HeapFree(GetProcessHeap(), 0, lpszUrlCpy);
519 TRACE("result %s\n", debugstr_w(pszCanonicalized));
524 /*************************************************************************
525 * UrlCombineA [SHLWAPI.@]
530 * pszBase [I] Base Url
531 * pszRelative [I] Url to combine with pszBase
532 * pszCombined [O] Destination for combined Url
533 * pcchCombined [O] Destination for length of pszCombined
534 * dwFlags [I] URL_ flags from "shlwapi.h"
537 * Success: S_OK. pszCombined contains the combined Url, pcchCombined
538 * contains its length.
539 * Failure: An HRESULT error code indicating the error.
541 HRESULT WINAPI UrlCombineA(LPCSTR pszBase, LPCSTR pszRelative,
542 LPSTR pszCombined, LPDWORD pcchCombined,
545 LPWSTR base, relative, combined;
546 DWORD ret, len, len2;
548 TRACE("(base %s, Relative %s, Combine size %d, flags %08x) using W version\n",
549 debugstr_a(pszBase),debugstr_a(pszRelative),
550 pcchCombined?*pcchCombined:0,dwFlags);
552 if(!pszBase || !pszRelative || !pcchCombined)
555 base = HeapAlloc(GetProcessHeap(), 0,
556 (3*INTERNET_MAX_URL_LENGTH) * sizeof(WCHAR));
557 relative = base + INTERNET_MAX_URL_LENGTH;
558 combined = relative + INTERNET_MAX_URL_LENGTH;
560 MultiByteToWideChar(0, 0, pszBase, -1, base, INTERNET_MAX_URL_LENGTH);
561 MultiByteToWideChar(0, 0, pszRelative, -1, relative, INTERNET_MAX_URL_LENGTH);
564 ret = UrlCombineW(base, relative, pszCombined?combined:NULL, &len, dwFlags);
567 HeapFree(GetProcessHeap(), 0, base);
571 len2 = WideCharToMultiByte(0, 0, combined, len, 0, 0, 0, 0);
572 if (len2 > *pcchCombined) {
573 *pcchCombined = len2;
574 HeapFree(GetProcessHeap(), 0, base);
577 WideCharToMultiByte(0, 0, combined, len+1, pszCombined, (*pcchCombined)+1,
579 *pcchCombined = len2;
580 HeapFree(GetProcessHeap(), 0, base);
584 /*************************************************************************
585 * UrlCombineW [SHLWAPI.@]
589 HRESULT WINAPI UrlCombineW(LPCWSTR pszBase, LPCWSTR pszRelative,
590 LPWSTR pszCombined, LPDWORD pcchCombined,
593 PARSEDURLW base, relative;
594 DWORD myflags, sizeloc = 0;
595 DWORD len, res1, res2, process_case = 0;
596 LPWSTR work, preliminary, mbase, mrelative;
597 static const WCHAR myfilestr[] = {'f','i','l','e',':','/','/','/','\0'};
600 TRACE("(base %s, Relative %s, Combine size %d, flags %08x)\n",
601 debugstr_w(pszBase),debugstr_w(pszRelative),
602 pcchCombined?*pcchCombined:0,dwFlags);
604 if(!pszBase || !pszRelative || !pcchCombined)
607 base.cbSize = sizeof(base);
608 relative.cbSize = sizeof(relative);
610 /* Get space for duplicates of the input and the output */
611 preliminary = HeapAlloc(GetProcessHeap(), 0, (3*INTERNET_MAX_URL_LENGTH) *
613 mbase = preliminary + INTERNET_MAX_URL_LENGTH;
614 mrelative = mbase + INTERNET_MAX_URL_LENGTH;
617 /* Canonicalize the base input prior to looking for the scheme */
618 myflags = dwFlags & (URL_DONT_SIMPLIFY | URL_UNESCAPE);
619 len = INTERNET_MAX_URL_LENGTH;
620 ret = UrlCanonicalizeW(pszBase, mbase, &len, myflags);
622 /* Canonicalize the relative input prior to looking for the scheme */
623 len = INTERNET_MAX_URL_LENGTH;
624 ret = UrlCanonicalizeW(pszRelative, mrelative, &len, myflags);
626 /* See if the base has a scheme */
627 res1 = ParseURLW(mbase, &base);
629 /* if pszBase has no scheme, then return pszRelative */
630 TRACE("no scheme detected in Base\n");
634 /* mk is a special case */
635 if(base.nScheme == URL_SCHEME_MK) {
636 static const WCHAR wsz[] = {':',':',0};
638 WCHAR *ptr = strstrW(base.pszSuffix, wsz);
643 delta = ptr-base.pszSuffix;
644 base.cchProtocol += delta;
645 base.pszSuffix += delta;
646 base.cchSuffix -= delta;
649 /* get size of location field (if it exists) */
650 work = (LPWSTR)base.pszSuffix;
652 if (*work++ == '/') {
653 if (*work++ == '/') {
654 /* At this point have start of location and
655 * it ends at next '/' or end of string.
657 while(*work && (*work != '/')) work++;
658 sizeloc = (DWORD)(work - base.pszSuffix);
663 /* Change .sizep2 to not have the last leaf in it,
664 * Note: we need to start after the location (if it exists)
666 work = strrchrW((base.pszSuffix+sizeloc), '/');
668 len = (DWORD)(work - base.pszSuffix + 1);
669 base.cchSuffix = len;
674 * .pszSuffix points to location (starting with '//')
675 * .cchSuffix length of location (above) and rest less the last
677 * sizeloc length of location (above) up to but not including
681 res2 = ParseURLW(mrelative, &relative);
683 /* no scheme in pszRelative */
684 TRACE("no scheme detected in Relative\n");
685 relative.pszSuffix = mrelative; /* case 3,4,5 depends on this */
686 relative.cchSuffix = strlenW(mrelative);
687 if (*pszRelative == ':') {
688 /* case that is either left alone or uses pszBase */
689 if (dwFlags & URL_PLUGGABLE_PROTOCOL) {
696 if (isalnum(*mrelative) && (*(mrelative + 1) == ':')) {
697 /* case that becomes "file:///" */
698 strcpyW(preliminary, myfilestr);
702 if ((*mrelative == '/') && (*(mrelative+1) == '/')) {
703 /* pszRelative has location and rest */
707 if (*mrelative == '/') {
708 /* case where pszRelative is root to location */
712 process_case = (*base.pszSuffix == '/' || base.nScheme == URL_SCHEME_MK) ? 5 : 3;
716 /* handle cases where pszRelative has scheme */
717 if ((base.cchProtocol == relative.cchProtocol) &&
718 (strncmpW(base.pszProtocol, relative.pszProtocol, base.cchProtocol) == 0)) {
720 /* since the schemes are the same */
721 if ((*relative.pszSuffix == '/') && (*(relative.pszSuffix+1) == '/')) {
722 /* case where pszRelative replaces location and following */
726 if (*relative.pszSuffix == '/') {
727 /* case where pszRelative is root to location */
731 /* replace either just location if base's location starts with a
732 * slash or otherwise everything */
733 process_case = (*base.pszSuffix == '/') ? 5 : 1;
736 if ((*relative.pszSuffix == '/') && (*(relative.pszSuffix+1) == '/')) {
737 /* case where pszRelative replaces scheme, location,
738 * and following and handles PLUGGABLE
745 } while(FALSE); /* a little trick to allow easy exit from nested if's */
748 switch (process_case) {
751 * Return pszRelative appended to what ever is in pszCombined,
752 * (which may the string "file:///"
754 strcatW(preliminary, mrelative);
757 case 2: /* case where pszRelative replaces scheme, and location */
758 strcpyW(preliminary, mrelative);
762 * Return the pszBase scheme with pszRelative. Basically
763 * keeps the scheme and replaces the domain and following.
765 memcpy(preliminary, base.pszProtocol, (base.cchProtocol + 1)*sizeof(WCHAR));
766 work = preliminary + base.cchProtocol + 1;
767 strcpyW(work, relative.pszSuffix);
771 * Return the pszBase scheme and location but everything
772 * after the location is pszRelative. (Replace document
775 memcpy(preliminary, base.pszProtocol, (base.cchProtocol+1+sizeloc)*sizeof(WCHAR));
776 work = preliminary + base.cchProtocol + 1 + sizeloc;
777 if (dwFlags & URL_PLUGGABLE_PROTOCOL)
779 strcpyW(work, relative.pszSuffix);
783 * Return the pszBase without its document (if any) and
784 * append pszRelative after its scheme.
786 memcpy(preliminary, base.pszProtocol,
787 (base.cchProtocol+1+base.cchSuffix)*sizeof(WCHAR));
788 work = preliminary + base.cchProtocol+1+base.cchSuffix - 1;
791 strcpyW(work, relative.pszSuffix);
795 FIXME("How did we get here????? process_case=%d\n", process_case);
800 /* Reuse mrelative as temp storage as its already allocated and not needed anymore */
801 ret = UrlCanonicalizeW(preliminary, mrelative, pcchCombined, (dwFlags & ~URL_FILE_USE_PATHURL));
802 if(SUCCEEDED(ret) && pszCombined) {
803 lstrcpyW(pszCombined, mrelative);
805 TRACE("return-%d len=%d, %s\n",
806 process_case, *pcchCombined, debugstr_w(pszCombined));
808 HeapFree(GetProcessHeap(), 0, preliminary);
812 /*************************************************************************
813 * UrlEscapeA [SHLWAPI.@]
816 HRESULT WINAPI UrlEscapeA(
822 WCHAR bufW[INTERNET_MAX_URL_LENGTH];
823 WCHAR *escapedW = bufW;
826 DWORD lenW = sizeof(bufW)/sizeof(WCHAR), lenA;
828 if (!pszEscaped || !pcchEscaped || !*pcchEscaped)
831 if(!RtlCreateUnicodeStringFromAsciiz(&urlW, pszUrl))
833 if((ret = UrlEscapeW(urlW.Buffer, escapedW, &lenW, dwFlags)) == E_POINTER) {
834 escapedW = HeapAlloc(GetProcessHeap(), 0, lenW * sizeof(WCHAR));
835 ret = UrlEscapeW(urlW.Buffer, escapedW, &lenW, dwFlags);
838 RtlUnicodeToMultiByteSize(&lenA, escapedW, lenW * sizeof(WCHAR));
839 if(*pcchEscaped > lenA) {
840 RtlUnicodeToMultiByteN(pszEscaped, *pcchEscaped - 1, &lenA, escapedW, lenW * sizeof(WCHAR));
841 pszEscaped[lenA] = 0;
844 *pcchEscaped = lenA + 1;
848 if(escapedW != bufW) HeapFree(GetProcessHeap(), 0, escapedW);
849 RtlFreeUnicodeString(&urlW);
853 #define WINE_URL_BASH_AS_SLASH 0x01
854 #define WINE_URL_COLLAPSE_SLASHES 0x02
855 #define WINE_URL_ESCAPE_SLASH 0x04
856 #define WINE_URL_ESCAPE_HASH 0x08
857 #define WINE_URL_ESCAPE_QUESTION 0x10
858 #define WINE_URL_STOP_ON_HASH 0x20
859 #define WINE_URL_STOP_ON_QUESTION 0x40
861 static inline BOOL URL_NeedEscapeW(WCHAR ch, DWORD dwFlags, DWORD int_flags)
867 if(dwFlags & URL_ESCAPE_SPACES_ONLY) {
874 if ((dwFlags & URL_ESCAPE_PERCENT) && (ch == '%'))
877 if (ch <= 31 || ch >= 127)
898 if (int_flags & WINE_URL_ESCAPE_SLASH) return TRUE;
902 if (int_flags & WINE_URL_ESCAPE_QUESTION) return TRUE;
906 if (int_flags & WINE_URL_ESCAPE_HASH) return TRUE;
916 /*************************************************************************
917 * UrlEscapeW [SHLWAPI.@]
919 * Converts unsafe characters in a Url into escape sequences.
922 * pszUrl [I] Url to modify
923 * pszEscaped [O] Destination for modified Url
924 * pcchEscaped [I/O] Length of pszUrl, destination for length of pszEscaped
925 * dwFlags [I] URL_ flags from "shlwapi.h"
928 * Success: S_OK. pszEscaped contains the escaped Url, pcchEscaped
929 * contains its length.
930 * Failure: E_POINTER, if pszEscaped is not large enough. In this case
931 * pcchEscaped is set to the required length.
933 * Converts unsafe characters into their escape sequences.
936 * - By default this function stops converting at the first '?' or
938 * - If dwFlags contains URL_ESCAPE_SPACES_ONLY then only spaces are
939 * converted, but the conversion continues past a '?' or '#'.
940 * - Note that this function did not work well (or at all) in shlwapi version 4.
943 * Only the following flags are implemented:
944 *| URL_ESCAPE_SPACES_ONLY
945 *| URL_DONT_ESCAPE_EXTRA_INFO
946 *| URL_ESCAPE_SEGMENT_ONLY
947 *| URL_ESCAPE_PERCENT
949 HRESULT WINAPI UrlEscapeW(
956 DWORD needed = 0, ret;
957 BOOL stop_escaping = FALSE;
958 WCHAR next[5], *dst = pszEscaped;
960 PARSEDURLW parsed_url;
963 static const WCHAR localhost[] = {'l','o','c','a','l','h','o','s','t',0};
965 TRACE("(%s %p %p 0x%08x)\n", debugstr_w(pszUrl), pszEscaped,
966 pcchEscaped, dwFlags);
968 if(!pszUrl || !pcchEscaped)
971 if(dwFlags & ~(URL_ESCAPE_SPACES_ONLY |
972 URL_ESCAPE_SEGMENT_ONLY |
973 URL_DONT_ESCAPE_EXTRA_INFO |
975 FIXME("Unimplemented flags: %08x\n", dwFlags);
978 if (dwFlags & URL_ESCAPE_SPACES_ONLY)
979 /* if SPACES_ONLY specified, reset the other controls */
980 dwFlags &= ~(URL_DONT_ESCAPE_EXTRA_INFO |
982 URL_ESCAPE_SEGMENT_ONLY);
985 /* if SPACES_ONLY *not* specified the assume DONT_ESCAPE_EXTRA_INFO */
986 dwFlags |= URL_DONT_ESCAPE_EXTRA_INFO;
990 if(dwFlags & URL_ESCAPE_SEGMENT_ONLY) {
991 int_flags = WINE_URL_ESCAPE_QUESTION | WINE_URL_ESCAPE_HASH | WINE_URL_ESCAPE_SLASH;
993 parsed_url.cbSize = sizeof(parsed_url);
994 if(ParseURLW(pszUrl, &parsed_url) != S_OK)
995 parsed_url.nScheme = URL_SCHEME_INVALID;
997 TRACE("scheme = %d (%s)\n", parsed_url.nScheme, debugstr_wn(parsed_url.pszProtocol, parsed_url.cchProtocol));
999 if(dwFlags & URL_DONT_ESCAPE_EXTRA_INFO)
1000 int_flags = WINE_URL_STOP_ON_HASH | WINE_URL_STOP_ON_QUESTION;
1002 switch(parsed_url.nScheme) {
1003 case URL_SCHEME_FILE:
1004 int_flags |= WINE_URL_BASH_AS_SLASH | WINE_URL_COLLAPSE_SLASHES | WINE_URL_ESCAPE_HASH;
1005 int_flags &= ~WINE_URL_STOP_ON_HASH;
1008 case URL_SCHEME_HTTP:
1009 case URL_SCHEME_HTTPS:
1010 int_flags |= WINE_URL_BASH_AS_SLASH;
1011 if(parsed_url.pszSuffix[0] != '/' && parsed_url.pszSuffix[0] != '\\')
1012 int_flags |= WINE_URL_ESCAPE_SLASH;
1015 case URL_SCHEME_MAILTO:
1016 int_flags |= WINE_URL_ESCAPE_SLASH | WINE_URL_ESCAPE_QUESTION | WINE_URL_ESCAPE_HASH;
1017 int_flags &= ~(WINE_URL_STOP_ON_QUESTION | WINE_URL_STOP_ON_HASH);
1020 case URL_SCHEME_INVALID:
1023 case URL_SCHEME_FTP:
1025 if(parsed_url.pszSuffix[0] != '/')
1026 int_flags |= WINE_URL_ESCAPE_SLASH;
1031 for(src = pszUrl; *src; ) {
1035 if((int_flags & WINE_URL_COLLAPSE_SLASHES) && src == pszUrl + parsed_url.cchProtocol + 1) {
1036 int localhost_len = sizeof(localhost)/sizeof(WCHAR) - 1;
1037 while(cur == '/' || cur == '\\') {
1041 if(slashes == 2 && !strncmpiW(src, localhost, localhost_len)) { /* file://localhost/ -> file:/// */
1042 if(*(src + localhost_len) == '/' || *(src + localhost_len) == '\\')
1043 src += localhost_len + 1;
1050 next[0] = next[1] = next[2] = '/';
1057 next[0] = next[1] = '/';
1064 if(cur == '#' && (int_flags & WINE_URL_STOP_ON_HASH))
1065 stop_escaping = TRUE;
1067 if(cur == '?' && (int_flags & WINE_URL_STOP_ON_QUESTION))
1068 stop_escaping = TRUE;
1070 if(cur == '\\' && (int_flags & WINE_URL_BASH_AS_SLASH) && !stop_escaping) cur = '/';
1072 if(URL_NeedEscapeW(cur, dwFlags, int_flags) && stop_escaping == FALSE) {
1074 next[1] = hexDigits[(cur >> 4) & 0xf];
1075 next[2] = hexDigits[cur & 0xf];
1084 if(needed + len <= *pcchEscaped) {
1085 memcpy(dst, next, len*sizeof(WCHAR));
1091 if(needed < *pcchEscaped) {
1095 needed++; /* add one for the '\0' */
1098 *pcchEscaped = needed;
1103 /*************************************************************************
1104 * UrlUnescapeA [SHLWAPI.@]
1106 * Converts Url escape sequences back to ordinary characters.
1109 * pszUrl [I/O] Url to convert
1110 * pszUnescaped [O] Destination for converted Url
1111 * pcchUnescaped [I/O] Size of output string
1112 * dwFlags [I] URL_ESCAPE_ Flags from "shlwapi.h"
1115 * Success: S_OK. The converted value is in pszUnescaped, or in pszUrl if
1116 * dwFlags includes URL_ESCAPE_INPLACE.
1117 * Failure: E_POINTER if the converted Url is bigger than pcchUnescaped. In
1118 * this case pcchUnescaped is set to the size required.
1120 * If dwFlags includes URL_DONT_ESCAPE_EXTRA_INFO, the conversion stops at
1121 * the first occurrence of either a '?' or '#' character.
1123 HRESULT WINAPI UrlUnescapeA(
1126 LPDWORD pcchUnescaped,
1133 BOOL stop_unescaping = FALSE;
1135 TRACE("(%s, %p, %p, 0x%08x)\n", debugstr_a(pszUrl), pszUnescaped,
1136 pcchUnescaped, dwFlags);
1138 if (!pszUrl) return E_INVALIDARG;
1140 if(dwFlags & URL_UNESCAPE_INPLACE)
1144 if (!pszUnescaped || !pcchUnescaped) return E_INVALIDARG;
1148 for(src = pszUrl, needed = 0; *src; src++, needed++) {
1149 if(dwFlags & URL_DONT_UNESCAPE_EXTRA_INFO &&
1150 (*src == '#' || *src == '?')) {
1151 stop_unescaping = TRUE;
1153 } else if(*src == '%' && isxdigit(*(src + 1)) && isxdigit(*(src + 2))
1154 && stop_unescaping == FALSE) {
1157 memcpy(buf, src + 1, 2);
1159 ih = strtol(buf, NULL, 16);
1161 src += 2; /* Advance to end of escape */
1165 if(dwFlags & URL_UNESCAPE_INPLACE || needed < *pcchUnescaped)
1169 if(dwFlags & URL_UNESCAPE_INPLACE || needed < *pcchUnescaped) {
1173 needed++; /* add one for the '\0' */
1176 if(!(dwFlags & URL_UNESCAPE_INPLACE))
1177 *pcchUnescaped = needed;
1180 TRACE("result %s\n", (dwFlags & URL_UNESCAPE_INPLACE) ?
1181 debugstr_a(pszUrl) : debugstr_a(pszUnescaped));
1187 /*************************************************************************
1188 * UrlUnescapeW [SHLWAPI.@]
1192 HRESULT WINAPI UrlUnescapeW(
1194 LPWSTR pszUnescaped,
1195 LPDWORD pcchUnescaped,
1202 BOOL stop_unescaping = FALSE;
1204 TRACE("(%s, %p, %p, 0x%08x)\n", debugstr_w(pszUrl), pszUnescaped,
1205 pcchUnescaped, dwFlags);
1207 if(!pszUrl) return E_INVALIDARG;
1209 if(dwFlags & URL_UNESCAPE_INPLACE)
1213 if (!pszUnescaped || !pcchUnescaped) return E_INVALIDARG;
1217 for(src = pszUrl, needed = 0; *src; src++, needed++) {
1218 if(dwFlags & URL_DONT_UNESCAPE_EXTRA_INFO &&
1219 (*src == '#' || *src == '?')) {
1220 stop_unescaping = TRUE;
1222 } else if(*src == '%' && 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, '#');
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)
1494 TRACE("(%s, %p, %p:out size %d, 0x%08x)\n", debugstr_a(pszIn),
1495 pszOut, pcchOut, pcchOut ? *pcchOut : 0, dwFlags);
1497 if (!pszIn || !pszOut || !pcchOut) return E_INVALIDARG;
1499 in = HeapAlloc(GetProcessHeap(), 0,
1500 (2*INTERNET_MAX_URL_LENGTH) * sizeof(WCHAR));
1501 out = in + INTERNET_MAX_URL_LENGTH;
1503 MultiByteToWideChar(CP_ACP, 0, pszIn, -1, in, INTERNET_MAX_URL_LENGTH);
1504 len = INTERNET_MAX_URL_LENGTH;
1506 ret = UrlApplySchemeW(in, out, &len, dwFlags);
1508 HeapFree(GetProcessHeap(), 0, in);
1512 len = WideCharToMultiByte(CP_ACP, 0, out, -1, NULL, 0, NULL, NULL);
1513 if (len > *pcchOut) {
1518 WideCharToMultiByte(CP_ACP, 0, out, -1, pszOut, *pcchOut, NULL, NULL);
1523 HeapFree(GetProcessHeap(), 0, in);
1527 static HRESULT URL_GuessScheme(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut)
1532 DWORD value_len, data_len, dwType, i;
1533 WCHAR reg_path[MAX_PATH];
1534 WCHAR value[MAX_PATH], data[MAX_PATH];
1537 MultiByteToWideChar(0, 0,
1538 "Software\\Microsoft\\Windows\\CurrentVersion\\URL\\Prefixes",
1539 -1, reg_path, MAX_PATH);
1540 RegOpenKeyExW(HKEY_LOCAL_MACHINE, reg_path, 0, 1, &newkey);
1542 while(value_len = data_len = MAX_PATH,
1543 RegEnumValueW(newkey, index, value, &value_len,
1544 0, &dwType, (LPVOID)data, &data_len) == 0) {
1545 TRACE("guess %d %s is %s\n",
1546 index, debugstr_w(value), debugstr_w(data));
1549 for(i=0; i<value_len; i++) {
1552 /* remember that TRUE is not-equal */
1553 j = ChrCmpIW(Wxx, Wyy);
1556 if ((i == value_len) && !j) {
1557 if (strlenW(data) + strlenW(pszIn) + 1 > *pcchOut) {
1558 *pcchOut = strlenW(data) + strlenW(pszIn) + 1;
1559 RegCloseKey(newkey);
1562 strcpyW(pszOut, data);
1563 strcatW(pszOut, pszIn);
1564 *pcchOut = strlenW(pszOut);
1565 TRACE("matched and set to %s\n", debugstr_w(pszOut));
1566 RegCloseKey(newkey);
1571 RegCloseKey(newkey);
1575 static HRESULT URL_ApplyDefault(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut)
1578 DWORD data_len, dwType;
1579 WCHAR data[MAX_PATH];
1581 static const WCHAR prefix_keyW[] =
1582 {'S','o','f','t','w','a','r','e',
1583 '\\','M','i','c','r','o','s','o','f','t',
1584 '\\','W','i','n','d','o','w','s',
1585 '\\','C','u','r','r','e','n','t','V','e','r','s','i','o','n',
1587 '\\','D','e','f','a','u','l','t','P','r','e','f','i','x',0};
1589 /* get and prepend default */
1590 RegOpenKeyExW(HKEY_LOCAL_MACHINE, prefix_keyW, 0, 1, &newkey);
1591 data_len = sizeof(data);
1592 RegQueryValueExW(newkey, NULL, 0, &dwType, (LPBYTE)data, &data_len);
1593 RegCloseKey(newkey);
1594 if (strlenW(data) + strlenW(pszIn) + 1 > *pcchOut) {
1595 *pcchOut = strlenW(data) + strlenW(pszIn) + 1;
1598 strcpyW(pszOut, data);
1599 strcatW(pszOut, pszIn);
1600 *pcchOut = strlenW(pszOut);
1601 TRACE("used default %s\n", debugstr_w(pszOut));
1605 /*************************************************************************
1606 * UrlApplySchemeW [SHLWAPI.@]
1608 * See UrlApplySchemeA.
1610 HRESULT WINAPI UrlApplySchemeW(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut, DWORD dwFlags)
1612 PARSEDURLW in_scheme;
1616 TRACE("(%s, %p, %p:out size %d, 0x%08x)\n", debugstr_w(pszIn),
1617 pszOut, pcchOut, pcchOut ? *pcchOut : 0, dwFlags);
1619 if (!pszIn || !pszOut || !pcchOut) return E_INVALIDARG;
1621 if (dwFlags & URL_APPLY_GUESSFILE) {
1622 FIXME("(%s %p %p(%d) 0x%08x): stub URL_APPLY_GUESSFILE not implemented\n",
1623 debugstr_w(pszIn), pszOut, pcchOut, *pcchOut, dwFlags);
1624 strcpyW(pszOut, pszIn);
1625 *pcchOut = strlenW(pszOut);
1629 in_scheme.cbSize = sizeof(in_scheme);
1630 /* See if the base has a scheme */
1631 res1 = ParseURLW(pszIn, &in_scheme);
1633 /* no scheme in input, need to see if we need to guess */
1634 if (dwFlags & URL_APPLY_GUESSSCHEME) {
1635 if ((ret = URL_GuessScheme(pszIn, pszOut, pcchOut)) != E_FAIL)
1640 /* we have a scheme, see if valid (known scheme) */
1641 if (in_scheme.nScheme) {
1642 /* have valid scheme, so just copy and exit */
1643 if (strlenW(pszIn) + 1 > *pcchOut) {
1644 *pcchOut = strlenW(pszIn) + 1;
1647 strcpyW(pszOut, pszIn);
1648 *pcchOut = strlenW(pszOut);
1649 TRACE("valid scheme, returning copy\n");
1654 /* If we are here, then either invalid scheme,
1655 * or no scheme and can't/failed guess.
1657 if ( ( ((res1 == 0) && (dwFlags & URL_APPLY_FORCEAPPLY)) ||
1659 (dwFlags & URL_APPLY_DEFAULT)) {
1660 /* find and apply default scheme */
1661 return URL_ApplyDefault(pszIn, pszOut, pcchOut);
1667 /*************************************************************************
1668 * UrlIsA [SHLWAPI.@]
1670 * Determine if a Url is of a certain class.
1673 * pszUrl [I] Url to check
1674 * Urlis [I] URLIS_ constant from "shlwapi.h"
1677 * TRUE if pszUrl belongs to the class type in Urlis.
1680 BOOL WINAPI UrlIsA(LPCSTR pszUrl, URLIS Urlis)
1686 TRACE("(%s %d)\n", debugstr_a(pszUrl), Urlis);
1691 base.cbSize = sizeof(base);
1692 res1 = ParseURLA(pszUrl, &base);
1693 if (res1) return FALSE; /* invalid scheme */
1694 switch (base.nScheme)
1696 case URL_SCHEME_MAILTO:
1697 case URL_SCHEME_SHELL:
1698 case URL_SCHEME_JAVASCRIPT:
1699 case URL_SCHEME_VBSCRIPT:
1700 case URL_SCHEME_ABOUT:
1706 return !StrCmpNA("file:", pszUrl, 5);
1708 case URLIS_DIRECTORY:
1709 last = pszUrl + strlen(pszUrl) - 1;
1710 return (last >= pszUrl && (*last == '/' || *last == '\\' ));
1713 return PathIsURLA(pszUrl);
1715 case URLIS_NOHISTORY:
1716 case URLIS_APPLIABLE:
1717 case URLIS_HASQUERY:
1719 FIXME("(%s %d): stub\n", debugstr_a(pszUrl), Urlis);
1724 /*************************************************************************
1725 * UrlIsW [SHLWAPI.@]
1729 BOOL WINAPI UrlIsW(LPCWSTR pszUrl, URLIS Urlis)
1731 static const WCHAR stemp[] = { 'f','i','l','e',':',0 };
1736 TRACE("(%s %d)\n", debugstr_w(pszUrl), Urlis);
1741 base.cbSize = sizeof(base);
1742 res1 = ParseURLW(pszUrl, &base);
1743 if (res1) return FALSE; /* invalid scheme */
1744 switch (base.nScheme)
1746 case URL_SCHEME_MAILTO:
1747 case URL_SCHEME_SHELL:
1748 case URL_SCHEME_JAVASCRIPT:
1749 case URL_SCHEME_VBSCRIPT:
1750 case URL_SCHEME_ABOUT:
1756 return !strncmpW(stemp, pszUrl, 5);
1758 case URLIS_DIRECTORY:
1759 last = pszUrl + strlenW(pszUrl) - 1;
1760 return (last >= pszUrl && (*last == '/' || *last == '\\'));
1763 return PathIsURLW(pszUrl);
1765 case URLIS_NOHISTORY:
1766 case URLIS_APPLIABLE:
1767 case URLIS_HASQUERY:
1769 FIXME("(%s %d): stub\n", debugstr_w(pszUrl), Urlis);
1774 /*************************************************************************
1775 * UrlIsNoHistoryA [SHLWAPI.@]
1777 * Determine if a Url should not be stored in the users history list.
1780 * pszUrl [I] Url to check
1783 * TRUE, if pszUrl should be excluded from the history list,
1786 BOOL WINAPI UrlIsNoHistoryA(LPCSTR pszUrl)
1788 return UrlIsA(pszUrl, URLIS_NOHISTORY);
1791 /*************************************************************************
1792 * UrlIsNoHistoryW [SHLWAPI.@]
1794 * See UrlIsNoHistoryA.
1796 BOOL WINAPI UrlIsNoHistoryW(LPCWSTR pszUrl)
1798 return UrlIsW(pszUrl, URLIS_NOHISTORY);
1801 /*************************************************************************
1802 * UrlIsOpaqueA [SHLWAPI.@]
1804 * Determine if a Url is opaque.
1807 * pszUrl [I] Url to check
1810 * TRUE if pszUrl is opaque,
1814 * An opaque Url is one that does not start with "<protocol>://".
1816 BOOL WINAPI UrlIsOpaqueA(LPCSTR pszUrl)
1818 return UrlIsA(pszUrl, URLIS_OPAQUE);
1821 /*************************************************************************
1822 * UrlIsOpaqueW [SHLWAPI.@]
1826 BOOL WINAPI UrlIsOpaqueW(LPCWSTR pszUrl)
1828 return UrlIsW(pszUrl, URLIS_OPAQUE);
1831 /*************************************************************************
1832 * Scans for characters of type "type" and when not matching found,
1833 * returns pointer to it and length in size.
1835 * Characters tested based on RFC 1738
1837 static LPCWSTR URL_ScanID(LPCWSTR start, LPDWORD size, WINE_URL_SCAN_TYPE type)
1839 static DWORD alwayszero = 0;
1848 if ( (islowerW(*start) && isalphaW(*start)) ||
1863 if ( isalphaW(*start) ||
1865 /* user/password only characters */
1870 /* *extra* characters */
1877 /* *safe* characters */
1885 } else if (*start == '%') {
1886 if (isxdigitW(*(start+1)) &&
1887 isxdigitW(*(start+2))) {
1899 if (isdigitW(*start)) {
1910 if (isalnumW(*start) ||
1921 FIXME("unknown type %d\n", type);
1922 return (LPWSTR)&alwayszero;
1924 /* TRACE("scanned %d characters next char %p<%c>\n",
1925 *size, start, *start); */
1929 /*************************************************************************
1930 * Attempt to parse URL into pieces.
1932 static LONG URL_ParseUrl(LPCWSTR pszUrl, WINE_PARSE_URL *pl)
1936 memset(pl, 0, sizeof(WINE_PARSE_URL));
1937 pl->pScheme = pszUrl;
1938 work = URL_ScanID(pl->pScheme, &pl->szScheme, SCHEME);
1939 if (!*work || (*work != ':')) goto ErrorExit;
1941 if ((*work != '/') || (*(work+1) != '/')) goto ErrorExit;
1942 pl->pUserName = work + 2;
1943 work = URL_ScanID(pl->pUserName, &pl->szUserName, USERPASS);
1944 if (*work == ':' ) {
1945 /* parse password */
1947 pl->pPassword = work;
1948 work = URL_ScanID(pl->pPassword, &pl->szPassword, USERPASS);
1950 /* what we just parsed must be the hostname and port
1951 * so reset pointers and clear then let it parse */
1952 pl->szUserName = pl->szPassword = 0;
1953 work = pl->pUserName - 1;
1954 pl->pUserName = pl->pPassword = 0;
1956 } else if (*work == '@') {
1960 } else if (!*work || (*work == '/') || (*work == '.')) {
1961 /* what was parsed was hostname, so reset pointers and let it parse */
1962 pl->szUserName = pl->szPassword = 0;
1963 work = pl->pUserName - 1;
1964 pl->pUserName = pl->pPassword = 0;
1965 } else goto ErrorExit;
1967 /* now start parsing hostname or hostnumber */
1969 pl->pHostName = work;
1970 work = URL_ScanID(pl->pHostName, &pl->szHostName, HOST);
1975 work = URL_ScanID(pl->pPort, &pl->szPort, PORT);
1978 /* see if query string */
1979 pl->pQuery = strchrW(work, '?');
1980 if (pl->pQuery) pl->szQuery = strlenW(pl->pQuery);
1982 TRACE("parse successful: scheme=%p(%d), user=%p(%d), pass=%p(%d), host=%p(%d), port=%p(%d), query=%p(%d)\n",
1983 pl->pScheme, pl->szScheme,
1984 pl->pUserName, pl->szUserName,
1985 pl->pPassword, pl->szPassword,
1986 pl->pHostName, pl->szHostName,
1987 pl->pPort, pl->szPort,
1988 pl->pQuery, pl->szQuery);
1991 FIXME("failed to parse %s\n", debugstr_w(pszUrl));
1992 return E_INVALIDARG;
1995 /*************************************************************************
1996 * UrlGetPartA [SHLWAPI.@]
1998 * Retrieve part of a Url.
2001 * pszIn [I] Url to parse
2002 * pszOut [O] Destination for part of pszIn requested
2003 * pcchOut [I] Size of pszOut
2004 * [O] length of pszOut string EXCLUDING '\0' if S_OK, otherwise
2005 * needed size of pszOut INCLUDING '\0'.
2006 * dwPart [I] URL_PART_ enum from "shlwapi.h"
2007 * dwFlags [I] URL_ flags from "shlwapi.h"
2010 * Success: S_OK. pszOut contains the part requested, pcchOut contains its length.
2011 * Failure: An HRESULT error code describing the error.
2013 HRESULT WINAPI UrlGetPartA(LPCSTR pszIn, LPSTR pszOut, LPDWORD pcchOut,
2014 DWORD dwPart, DWORD dwFlags)
2017 DWORD ret, len, len2;
2019 in = HeapAlloc(GetProcessHeap(), 0,
2020 (2*INTERNET_MAX_URL_LENGTH) * sizeof(WCHAR));
2021 out = in + INTERNET_MAX_URL_LENGTH;
2023 MultiByteToWideChar(0, 0, pszIn, -1, in, INTERNET_MAX_URL_LENGTH);
2025 len = INTERNET_MAX_URL_LENGTH;
2026 ret = UrlGetPartW(in, out, &len, dwPart, dwFlags);
2029 HeapFree(GetProcessHeap(), 0, in);
2033 len2 = WideCharToMultiByte(0, 0, out, len, 0, 0, 0, 0);
2034 if (len2 > *pcchOut) {
2036 HeapFree(GetProcessHeap(), 0, in);
2039 WideCharToMultiByte(0, 0, out, len+1, pszOut, *pcchOut, 0, 0);
2041 HeapFree(GetProcessHeap(), 0, in);
2045 /*************************************************************************
2046 * UrlGetPartW [SHLWAPI.@]
2050 HRESULT WINAPI UrlGetPartW(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut,
2051 DWORD dwPart, DWORD dwFlags)
2055 DWORD size, schsize;
2056 LPCWSTR addr, schaddr;
2058 TRACE("(%s %p %p(%d) %08x %08x)\n",
2059 debugstr_w(pszIn), pszOut, pcchOut, *pcchOut, dwPart, dwFlags);
2061 ret = URL_ParseUrl(pszIn, &pl);
2063 schaddr = pl.pScheme;
2064 schsize = pl.szScheme;
2067 case URL_PART_SCHEME:
2068 if (!pl.szScheme) return E_INVALIDARG;
2073 case URL_PART_HOSTNAME:
2074 if (!pl.szHostName) return E_INVALIDARG;
2075 addr = pl.pHostName;
2076 size = pl.szHostName;
2079 case URL_PART_USERNAME:
2080 if (!pl.szUserName) return E_INVALIDARG;
2081 addr = pl.pUserName;
2082 size = pl.szUserName;
2085 case URL_PART_PASSWORD:
2086 if (!pl.szPassword) return E_INVALIDARG;
2087 addr = pl.pPassword;
2088 size = pl.szPassword;
2092 if (!pl.szPort) return E_INVALIDARG;
2097 case URL_PART_QUERY:
2098 if (!pl.szQuery) return E_INVALIDARG;
2104 return E_INVALIDARG;
2107 if (dwFlags == URL_PARTFLAG_KEEPSCHEME) {
2108 if (*pcchOut < schsize + size + 2) {
2109 *pcchOut = schsize + size + 2;
2112 memcpy(pszOut, schaddr, schsize*sizeof(WCHAR));
2113 pszOut[schsize] = ':';
2114 memcpy(pszOut+schsize+1, addr, size*sizeof(WCHAR));
2115 pszOut[schsize+1+size] = 0;
2116 *pcchOut = schsize + 1 + size;
2119 if (*pcchOut < size + 1) {*pcchOut = size+1; return E_POINTER;}
2120 memcpy(pszOut, addr, size*sizeof(WCHAR));
2124 TRACE("len=%d %s\n", *pcchOut, debugstr_w(pszOut));
2129 /*************************************************************************
2130 * PathIsURLA [SHLWAPI.@]
2132 * Check if the given path is a Url.
2135 * lpszPath [I] Path to check.
2138 * TRUE if lpszPath is a Url.
2139 * FALSE if lpszPath is NULL or not a Url.
2141 BOOL WINAPI PathIsURLA(LPCSTR lpstrPath)
2145 TRACE("%s\n", debugstr_a(lpstrPath));
2147 if (!lpstrPath || !*lpstrPath) return FALSE;
2150 base.cbSize = sizeof(base);
2151 ParseURLA(lpstrPath, &base);
2152 return (base.nScheme != URL_SCHEME_INVALID);
2155 /*************************************************************************
2156 * PathIsURLW [SHLWAPI.@]
2160 BOOL WINAPI PathIsURLW(LPCWSTR lpstrPath)
2164 TRACE("%s\n", debugstr_w(lpstrPath));
2166 if (!lpstrPath || !*lpstrPath) return FALSE;
2169 base.cbSize = sizeof(base);
2170 ParseURLW(lpstrPath, &base);
2171 return (base.nScheme != URL_SCHEME_INVALID);
2174 /*************************************************************************
2175 * UrlCreateFromPathA [SHLWAPI.@]
2177 * See UrlCreateFromPathW
2179 HRESULT WINAPI UrlCreateFromPathA(LPCSTR pszPath, LPSTR pszUrl, LPDWORD pcchUrl, DWORD dwReserved)
2181 WCHAR bufW[INTERNET_MAX_URL_LENGTH];
2183 UNICODE_STRING pathW;
2185 DWORD lenW = sizeof(bufW)/sizeof(WCHAR), lenA;
2187 if(!RtlCreateUnicodeStringFromAsciiz(&pathW, pszPath))
2188 return E_INVALIDARG;
2189 if((ret = UrlCreateFromPathW(pathW.Buffer, urlW, &lenW, dwReserved)) == E_POINTER) {
2190 urlW = HeapAlloc(GetProcessHeap(), 0, lenW * sizeof(WCHAR));
2191 ret = UrlCreateFromPathW(pathW.Buffer, urlW, &lenW, dwReserved);
2193 if(ret == S_OK || ret == S_FALSE) {
2194 RtlUnicodeToMultiByteSize(&lenA, urlW, lenW * sizeof(WCHAR));
2195 if(*pcchUrl > lenA) {
2196 RtlUnicodeToMultiByteN(pszUrl, *pcchUrl - 1, &lenA, urlW, lenW * sizeof(WCHAR));
2200 *pcchUrl = lenA + 1;
2204 if(urlW != bufW) HeapFree(GetProcessHeap(), 0, urlW);
2205 RtlFreeUnicodeString(&pathW);
2209 /*************************************************************************
2210 * UrlCreateFromPathW [SHLWAPI.@]
2212 * Create a Url from a file path.
2215 * pszPath [I] Path to convert
2216 * pszUrl [O] Destination for the converted Url
2217 * pcchUrl [I/O] Length of pszUrl
2218 * dwReserved [I] Reserved, must be 0
2221 * Success: S_OK pszUrl contains the converted path, S_FALSE if the path is already a Url
2222 * Failure: An HRESULT error code.
2224 HRESULT WINAPI UrlCreateFromPathW(LPCWSTR pszPath, LPWSTR pszUrl, LPDWORD pcchUrl, DWORD dwReserved)
2229 WCHAR file_colonW[] = {'f','i','l','e',':',0};
2230 WCHAR three_slashesW[] = {'/','/','/',0};
2231 PARSEDURLW parsed_url;
2233 TRACE("(%s, %p, %p, 0x%08x)\n", debugstr_w(pszPath), pszUrl, pcchUrl, dwReserved);
2235 /* Validate arguments */
2236 if (dwReserved != 0)
2237 return E_INVALIDARG;
2238 if (!pszUrl || !pcchUrl)
2239 return E_INVALIDARG;
2242 parsed_url.cbSize = sizeof(parsed_url);
2243 if(ParseURLW(pszPath, &parsed_url) == S_OK) {
2244 if(parsed_url.nScheme != URL_SCHEME_INVALID && parsed_url.cchProtocol > 1) {
2245 needed = strlenW(pszPath);
2246 if (needed >= *pcchUrl) {
2247 *pcchUrl = needed + 1;
2251 strcpyW(pszUrl, pszPath);
2257 pszNewUrl = HeapAlloc(GetProcessHeap(), 0, (strlenW(pszPath) + 9) * sizeof(WCHAR)); /* "file:///" + pszPath_len + 1 */
2258 strcpyW(pszNewUrl, file_colonW);
2259 if(isalphaW(pszPath[0]) && pszPath[1] == ':')
2260 strcatW(pszNewUrl, three_slashesW);
2261 strcatW(pszNewUrl, pszPath);
2262 ret = UrlEscapeW(pszNewUrl, pszUrl, pcchUrl, URL_ESCAPE_PERCENT);
2264 HeapFree(GetProcessHeap(), 0, pszNewUrl);
2268 /*************************************************************************
2269 * SHAutoComplete [SHLWAPI.@]
2271 * Enable auto-completion for an edit control.
2274 * hwndEdit [I] Handle of control to enable auto-completion for
2275 * dwFlags [I] SHACF_ flags from "shlwapi.h"
2278 * Success: S_OK. Auto-completion is enabled for the control.
2279 * Failure: An HRESULT error code indicating the error.
2281 HRESULT WINAPI SHAutoComplete(HWND hwndEdit, DWORD dwFlags)
2283 FIXME("SHAutoComplete stub\n");
2287 /*************************************************************************
2288 * MLBuildResURLA [SHLWAPI.405]
2290 * Create a Url pointing to a resource in a module.
2293 * lpszLibName [I] Name of the module containing the resource
2294 * hMod [I] Callers module handle
2295 * dwFlags [I] Undocumented flags for loading the module
2296 * lpszRes [I] Resource name
2297 * lpszDest [O] Destination for resulting Url
2298 * dwDestLen [I] Length of lpszDest
2301 * Success: S_OK. lpszDest contains the resource Url.
2302 * Failure: E_INVALIDARG, if any argument is invalid, or
2303 * E_FAIL if dwDestLen is too small.
2305 HRESULT WINAPI MLBuildResURLA(LPCSTR lpszLibName, HMODULE hMod, DWORD dwFlags,
2306 LPCSTR lpszRes, LPSTR lpszDest, DWORD dwDestLen)
2308 WCHAR szLibName[MAX_PATH], szRes[MAX_PATH], szDest[MAX_PATH];
2312 MultiByteToWideChar(CP_ACP, 0, lpszLibName, -1, szLibName, sizeof(szLibName)/sizeof(WCHAR));
2315 MultiByteToWideChar(CP_ACP, 0, lpszRes, -1, szRes, sizeof(szRes)/sizeof(WCHAR));
2317 if (dwDestLen > sizeof(szLibName)/sizeof(WCHAR))
2318 dwDestLen = sizeof(szLibName)/sizeof(WCHAR);
2320 hRet = MLBuildResURLW(lpszLibName ? szLibName : NULL, hMod, dwFlags,
2321 lpszRes ? szRes : NULL, lpszDest ? szDest : NULL, dwDestLen);
2322 if (SUCCEEDED(hRet) && lpszDest)
2323 WideCharToMultiByte(CP_ACP, 0, szDest, -1, lpszDest, dwDestLen, 0, 0);
2328 /*************************************************************************
2329 * MLBuildResURLA [SHLWAPI.406]
2331 * See MLBuildResURLA.
2333 HRESULT WINAPI MLBuildResURLW(LPCWSTR lpszLibName, HMODULE hMod, DWORD dwFlags,
2334 LPCWSTR lpszRes, LPWSTR lpszDest, DWORD dwDestLen)
2336 static const WCHAR szRes[] = { 'r','e','s',':','/','/','\0' };
2337 #define szResLen ((sizeof(szRes) - sizeof(WCHAR))/sizeof(WCHAR))
2338 HRESULT hRet = E_FAIL;
2340 TRACE("(%s,%p,0x%08x,%s,%p,%d)\n", debugstr_w(lpszLibName), hMod, dwFlags,
2341 debugstr_w(lpszRes), lpszDest, dwDestLen);
2343 if (!lpszLibName || !hMod || hMod == INVALID_HANDLE_VALUE || !lpszRes ||
2344 !lpszDest || (dwFlags && dwFlags != 2))
2345 return E_INVALIDARG;
2347 if (dwDestLen >= szResLen + 1)
2349 dwDestLen -= (szResLen + 1);
2350 memcpy(lpszDest, szRes, sizeof(szRes));
2352 hMod = MLLoadLibraryW(lpszLibName, hMod, dwFlags);
2356 WCHAR szBuff[MAX_PATH];
2359 len = GetModuleFileNameW(hMod, szBuff, sizeof(szBuff)/sizeof(WCHAR));
2360 if (len && len < sizeof(szBuff)/sizeof(WCHAR))
2362 DWORD dwPathLen = strlenW(szBuff) + 1;
2364 if (dwDestLen >= dwPathLen)
2368 dwDestLen -= dwPathLen;
2369 memcpy(lpszDest + szResLen, szBuff, dwPathLen * sizeof(WCHAR));
2371 dwResLen = strlenW(lpszRes) + 1;
2372 if (dwDestLen >= dwResLen + 1)
2374 lpszDest[szResLen + dwPathLen + dwResLen] = '/';
2375 memcpy(lpszDest + szResLen + dwPathLen, lpszRes, dwResLen * sizeof(WCHAR));
2380 MLFreeLibrary(hMod);