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 == '/')) str++;
136 while (*str && ((*str == '-') ||
138 isalnumW(*str))) str++;
139 if (*str == '/') 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)/sizeof(WCHAR));
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 <= ' ') 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 == '\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;
274 TRACE("(%s, %p, %p, 0x%08x) *pcchCanonicalized: %d\n", debugstr_a(pszUrl), pszCanonicalized,
275 pcchCanonicalized, dwFlags, pcchCanonicalized ? *pcchCanonicalized : -1);
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 *pcchCanonicalized = len * 2;
290 HeapFree(GetProcessHeap(), 0, base);
294 len2 = WideCharToMultiByte(0, 0, canonical, -1, 0, 0, 0, 0);
295 if (len2 > *pcchCanonicalized) {
296 *pcchCanonicalized = len2;
297 HeapFree(GetProcessHeap(), 0, base);
300 WideCharToMultiByte(0, 0, canonical, -1, pszCanonicalized, *pcchCanonicalized, 0, 0);
301 *pcchCanonicalized = len;
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) *pcchCanonicalized: %d\n", debugstr_w(pszUrl), pszCanonicalized,
324 pcchCanonicalized, dwFlags, pcchCanonicalized ? *pcchCanonicalized : -1);
326 if(!pszUrl || !pszCanonicalized || !pcchCanonicalized)
330 *pszCanonicalized = 0;
334 nByteLen = (lstrlenW(pszUrl) + 1) * sizeof(WCHAR); /* length in bytes */
335 lpszUrlCpy = HeapAlloc(GetProcessHeap(), 0,
336 INTERNET_MAX_URL_LENGTH * sizeof(WCHAR));
338 if((dwFlags & URL_FILE_USE_PATHURL) && nByteLen >= sizeof(wszFile)
339 && !memcmp(wszFile, pszUrl, sizeof(wszFile)))
345 * 1 have 2[+] alnum 2,3
346 * 2 have scheme (found :) 4,6,3
347 * 3 failed (no location)
349 * 5 have 1[+] alnum 6,3
350 * 6 have location (found /) save root location
353 wk1 = (LPWSTR)pszUrl;
357 if(pszUrl[1] == ':') { /* Assume path */
358 static const WCHAR wszFilePrefix[] = {'f','i','l','e',':','/','/','/'};
360 memcpy(wk2, wszFilePrefix, sizeof(wszFilePrefix));
361 wk2 += sizeof(wszFilePrefix)/sizeof(WCHAR);
362 if (dwFlags & URL_FILE_USE_PATHURL)
368 dwFlags |= URL_ESCAPE_UNSAFE;
375 if (!isalnumW(*wk1)) {state = 3; break;}
377 if (!isalnumW(*wk1)) {state = 3; break;}
383 if (*wk1++ == ':') state = 2;
386 if (*wk1 != '/') {state = 3; break;}
388 if (*wk1 != '/') {state = 6; break;}
390 if(*wk1 == '/' && (dwFlags & URL_FILE_USE_PATHURL))
395 nWkLen = strlenW(wk1);
396 memcpy(wk2, wk1, (nWkLen + 1) * sizeof(WCHAR));
402 if(*mp == '/' || *mp == '\\')
408 if (!isalnumW(*wk1) && (*wk1 != '-') && (*wk1 != '.') && (*wk1 != ':'))
410 while(isalnumW(*wk1) || (*wk1 == '-') || (*wk1 == '.') || (*wk1 == ':'))
417 if (*wk1 != '/' && *wk1 != '\\') {state = 3; break;}
418 while(*wk1 == '/' || *wk1 == '\\') {
425 if(dwFlags & URL_DONT_SIMPLIFY) {
430 /* Now at root location, cannot back up any more. */
431 /* "root" will point at the '/' */
435 mp = strchrW(wk1, '/');
436 mp2 = strchrW(wk1, '\\');
437 if(mp2 && (!mp || mp2 < mp))
440 nWkLen = strlenW(wk1);
441 memcpy(wk2, wk1, (nWkLen + 1) * sizeof(WCHAR));
448 memcpy(wk2, wk1, nLen * sizeof(WCHAR));
456 TRACE("found '/.'\n");
457 if (wk1[1] == '/' || wk1[1] == '\\') {
458 /* case of /./ -> skip the ./ */
461 else if (wk1[1] == '.') {
462 /* found /.. look for next / */
463 TRACE("found '/..'\n");
464 if (wk1[2] == '/' || wk1[2] == '\\' ||wk1[2] == '?'
465 || wk1[2] == '#' || !wk1[2]) {
466 /* case /../ -> need to backup wk2 */
467 TRACE("found '/../'\n");
468 *(wk2-1) = '\0'; /* set end of string */
469 mp = strrchrW(root, slash);
470 if (mp && (mp >= root)) {
471 /* found valid backup point */
473 if(wk1[2] != '/' && wk1[2] != '\\')
479 /* did not find point, restore '/' */
489 FIXME("how did we get here - state=%d\n", state);
490 HeapFree(GetProcessHeap(), 0, lpszUrlCpy);
494 TRACE("Simplified, orig <%s>, simple <%s>\n",
495 debugstr_w(pszUrl), debugstr_w(lpszUrlCpy));
497 nLen = lstrlenW(lpszUrlCpy);
498 while ((nLen > 0) && ((lpszUrlCpy[nLen-1] == '\r')||(lpszUrlCpy[nLen-1] == '\n')))
499 lpszUrlCpy[--nLen]=0;
501 if(dwFlags & (URL_UNESCAPE | URL_FILE_USE_PATHURL))
502 UrlUnescapeW(lpszUrlCpy, NULL, &nLen, URL_UNESCAPE_INPLACE);
504 if((EscapeFlags = dwFlags & (URL_ESCAPE_UNSAFE |
505 URL_ESCAPE_SPACES_ONLY |
507 URL_DONT_ESCAPE_EXTRA_INFO |
508 URL_ESCAPE_SEGMENT_ONLY ))) {
509 EscapeFlags &= ~URL_ESCAPE_UNSAFE;
510 hr = UrlEscapeW(lpszUrlCpy, pszCanonicalized, pcchCanonicalized,
512 } else { /* No escaping needed, just copy the string */
513 nLen = lstrlenW(lpszUrlCpy);
514 if(nLen < *pcchCanonicalized)
515 memcpy(pszCanonicalized, lpszUrlCpy, (nLen + 1)*sizeof(WCHAR));
520 *pcchCanonicalized = nLen;
523 HeapFree(GetProcessHeap(), 0, lpszUrlCpy);
526 TRACE("result %s\n", debugstr_w(pszCanonicalized));
531 /*************************************************************************
532 * UrlCombineA [SHLWAPI.@]
537 * pszBase [I] Base Url
538 * pszRelative [I] Url to combine with pszBase
539 * pszCombined [O] Destination for combined Url
540 * pcchCombined [O] Destination for length of pszCombined
541 * dwFlags [I] URL_ flags from "shlwapi.h"
544 * Success: S_OK. pszCombined contains the combined Url, pcchCombined
545 * contains its length.
546 * Failure: An HRESULT error code indicating the error.
548 HRESULT WINAPI UrlCombineA(LPCSTR pszBase, LPCSTR pszRelative,
549 LPSTR pszCombined, LPDWORD pcchCombined,
552 LPWSTR base, relative, combined;
553 DWORD ret, len, len2;
555 TRACE("(base %s, Relative %s, Combine size %d, flags %08x) using W version\n",
556 debugstr_a(pszBase),debugstr_a(pszRelative),
557 pcchCombined?*pcchCombined:0,dwFlags);
559 if(!pszBase || !pszRelative || !pcchCombined)
562 base = HeapAlloc(GetProcessHeap(), 0,
563 (3*INTERNET_MAX_URL_LENGTH) * sizeof(WCHAR));
564 relative = base + INTERNET_MAX_URL_LENGTH;
565 combined = relative + INTERNET_MAX_URL_LENGTH;
567 MultiByteToWideChar(0, 0, pszBase, -1, base, INTERNET_MAX_URL_LENGTH);
568 MultiByteToWideChar(0, 0, pszRelative, -1, relative, INTERNET_MAX_URL_LENGTH);
571 ret = UrlCombineW(base, relative, pszCombined?combined:NULL, &len, dwFlags);
574 HeapFree(GetProcessHeap(), 0, base);
578 len2 = WideCharToMultiByte(0, 0, combined, len, 0, 0, 0, 0);
579 if (len2 > *pcchCombined) {
580 *pcchCombined = len2;
581 HeapFree(GetProcessHeap(), 0, base);
584 WideCharToMultiByte(0, 0, combined, len+1, pszCombined, (*pcchCombined)+1,
586 *pcchCombined = len2;
587 HeapFree(GetProcessHeap(), 0, base);
591 /*************************************************************************
592 * UrlCombineW [SHLWAPI.@]
596 HRESULT WINAPI UrlCombineW(LPCWSTR pszBase, LPCWSTR pszRelative,
597 LPWSTR pszCombined, LPDWORD pcchCombined,
600 PARSEDURLW base, relative;
601 DWORD myflags, sizeloc = 0;
602 DWORD len, res1, res2, process_case = 0;
603 LPWSTR work, preliminary, mbase, mrelative;
604 static const WCHAR myfilestr[] = {'f','i','l','e',':','/','/','/','\0'};
605 static const WCHAR single_slash[] = {'/','\0'};
608 TRACE("(base %s, Relative %s, Combine size %d, flags %08x)\n",
609 debugstr_w(pszBase),debugstr_w(pszRelative),
610 pcchCombined?*pcchCombined:0,dwFlags);
612 if(!pszBase || !pszRelative || !pcchCombined)
615 base.cbSize = sizeof(base);
616 relative.cbSize = sizeof(relative);
618 /* Get space for duplicates of the input and the output */
619 preliminary = HeapAlloc(GetProcessHeap(), 0, (3*INTERNET_MAX_URL_LENGTH) *
621 mbase = preliminary + INTERNET_MAX_URL_LENGTH;
622 mrelative = mbase + INTERNET_MAX_URL_LENGTH;
625 /* Canonicalize the base input prior to looking for the scheme */
626 myflags = dwFlags & (URL_DONT_SIMPLIFY | URL_UNESCAPE);
627 len = INTERNET_MAX_URL_LENGTH;
628 ret = UrlCanonicalizeW(pszBase, mbase, &len, myflags);
630 /* Canonicalize the relative input prior to looking for the scheme */
631 len = INTERNET_MAX_URL_LENGTH;
632 ret = UrlCanonicalizeW(pszRelative, mrelative, &len, myflags);
634 /* See if the base has a scheme */
635 res1 = ParseURLW(mbase, &base);
637 /* if pszBase has no scheme, then return pszRelative */
638 TRACE("no scheme detected in Base\n");
642 /* mk is a special case */
643 if(base.nScheme == URL_SCHEME_MK) {
644 static const WCHAR wsz[] = {':',':',0};
646 WCHAR *ptr = strstrW(base.pszSuffix, wsz);
651 delta = ptr-base.pszSuffix;
652 base.cchProtocol += delta;
653 base.pszSuffix += delta;
654 base.cchSuffix -= delta;
658 /* get size of location field (if it exists) */
659 work = (LPWSTR)base.pszSuffix;
661 if (*work++ == '/') {
662 if (*work++ == '/') {
663 /* At this point have start of location and
664 * it ends at next '/' or end of string.
666 while(*work && (*work != '/')) work++;
667 sizeloc = (DWORD)(work - base.pszSuffix);
671 /* Change .sizep2 to not have the last leaf in it,
672 * Note: we need to start after the location (if it exists)
674 work = strrchrW((base.pszSuffix+sizeloc), '/');
676 len = (DWORD)(work - base.pszSuffix + 1);
677 base.cchSuffix = len;
682 * .pszSuffix points to location (starting with '//')
683 * .cchSuffix length of location (above) and rest less the last
685 * sizeloc length of location (above) up to but not including
689 res2 = ParseURLW(mrelative, &relative);
691 /* no scheme in pszRelative */
692 TRACE("no scheme detected in Relative\n");
693 relative.pszSuffix = mrelative; /* case 3,4,5 depends on this */
694 relative.cchSuffix = strlenW(mrelative);
695 if (*pszRelative == ':') {
696 /* case that is either left alone or uses pszBase */
697 if (dwFlags & URL_PLUGGABLE_PROTOCOL) {
704 if (isalnum(*mrelative) && (*(mrelative + 1) == ':')) {
705 /* case that becomes "file:///" */
706 strcpyW(preliminary, myfilestr);
710 if ((*mrelative == '/') && (*(mrelative+1) == '/')) {
711 /* pszRelative has location and rest */
715 if (*mrelative == '/') {
716 /* case where pszRelative is root to location */
720 process_case = (*base.pszSuffix == '/') ? 5 : 3;
724 /* handle cases where pszRelative has scheme */
725 if ((base.cchProtocol == relative.cchProtocol) &&
726 (strncmpW(base.pszProtocol, relative.pszProtocol, base.cchProtocol) == 0)) {
728 /* since the schemes are the same */
729 if ((*relative.pszSuffix == '/') && (*(relative.pszSuffix+1) == '/')) {
730 /* case where pszRelative replaces location and following */
734 if (*relative.pszSuffix == '/') {
735 /* case where pszRelative is root to location */
739 /* replace either just location if base's location starts with a
740 * slash or otherwise everything */
741 process_case = (*base.pszSuffix == '/') ? 5 : 1;
744 if ((*relative.pszSuffix == '/') && (*(relative.pszSuffix+1) == '/')) {
745 /* case where pszRelative replaces scheme, location,
746 * and following and handles PLUGGABLE
753 } while(FALSE); /* a litte trick to allow easy exit from nested if's */
757 switch (process_case) {
760 * Return pszRelative appended to what ever is in pszCombined,
761 * (which may the string "file:///"
763 strcatW(preliminary, mrelative);
767 * Same as case 1, but if URL_PLUGGABLE_PROTOCOL was specified
768 * and pszRelative starts with "//", then append a "/"
770 strcpyW(preliminary, mrelative);
771 if (!(dwFlags & URL_PLUGGABLE_PROTOCOL) &&
772 URL_JustLocation(relative.pszSuffix))
773 strcatW(preliminary, single_slash);
777 * Return the pszBase scheme with pszRelative. Basically
778 * keeps the scheme and replaces the domain and following.
780 memcpy(preliminary, base.pszProtocol, (base.cchProtocol + 1)*sizeof(WCHAR));
781 work = preliminary + base.cchProtocol + 1;
782 strcpyW(work, relative.pszSuffix);
783 if (!(dwFlags & URL_PLUGGABLE_PROTOCOL) &&
784 URL_JustLocation(relative.pszSuffix))
785 strcatW(work, single_slash);
789 * Return the pszBase scheme and location but everything
790 * after the location is pszRelative. (Replace document
793 memcpy(preliminary, base.pszProtocol, (base.cchProtocol+1+sizeloc)*sizeof(WCHAR));
794 work = preliminary + base.cchProtocol + 1 + sizeloc;
795 if (dwFlags & URL_PLUGGABLE_PROTOCOL)
797 strcpyW(work, relative.pszSuffix);
801 * Return the pszBase without its document (if any) and
802 * append pszRelative after its scheme.
804 memcpy(preliminary, base.pszProtocol,
805 (base.cchProtocol+1+base.cchSuffix)*sizeof(WCHAR));
806 work = preliminary + base.cchProtocol+1+base.cchSuffix - 1;
809 strcpyW(work, relative.pszSuffix);
813 FIXME("How did we get here????? process_case=%d\n", process_case);
818 /* Reuse mrelative as temp storage as its already allocated and not needed anymore */
819 ret = UrlCanonicalizeW(preliminary, mrelative, pcchCombined, (dwFlags & ~URL_FILE_USE_PATHURL));
820 if(SUCCEEDED(ret) && pszCombined) {
821 lstrcpyW(pszCombined, mrelative);
823 TRACE("return-%d len=%d, %s\n",
824 process_case, *pcchCombined, debugstr_w(pszCombined));
826 HeapFree(GetProcessHeap(), 0, preliminary);
830 /*************************************************************************
831 * UrlEscapeA [SHLWAPI.@]
834 HRESULT WINAPI UrlEscapeA(
840 WCHAR bufW[INTERNET_MAX_URL_LENGTH];
841 WCHAR *escapedW = bufW;
844 DWORD lenW = sizeof(bufW)/sizeof(WCHAR), lenA;
846 if(!RtlCreateUnicodeStringFromAsciiz(&urlW, pszUrl))
848 if((ret = UrlEscapeW(urlW.Buffer, escapedW, &lenW, dwFlags)) == E_POINTER) {
849 escapedW = HeapAlloc(GetProcessHeap(), 0, lenW * sizeof(WCHAR));
850 ret = UrlEscapeW(urlW.Buffer, escapedW, &lenW, dwFlags);
853 RtlUnicodeToMultiByteSize(&lenA, escapedW, lenW * sizeof(WCHAR));
854 if(pszEscaped && *pcchEscaped > lenA) {
855 RtlUnicodeToMultiByteN(pszEscaped, *pcchEscaped - 1, &lenA, escapedW, lenW * sizeof(WCHAR));
856 pszEscaped[lenA] = 0;
859 *pcchEscaped = lenA + 1;
863 if(escapedW != bufW) HeapFree(GetProcessHeap(), 0, escapedW);
864 RtlFreeUnicodeString(&urlW);
868 #define WINE_URL_BASH_AS_SLASH 0x01
869 #define WINE_URL_COLLAPSE_SLASHES 0x02
870 #define WINE_URL_ESCAPE_SLASH 0x04
871 #define WINE_URL_ESCAPE_HASH 0x08
872 #define WINE_URL_ESCAPE_QUESTION 0x10
873 #define WINE_URL_STOP_ON_HASH 0x20
874 #define WINE_URL_STOP_ON_QUESTION 0x40
876 static inline BOOL URL_NeedEscapeW(WCHAR ch, DWORD dwFlags, DWORD int_flags)
882 if(dwFlags & URL_ESCAPE_SPACES_ONLY) {
889 if ((dwFlags & URL_ESCAPE_PERCENT) && (ch == '%'))
892 if (ch <= 31 || ch >= 127)
913 if (int_flags & WINE_URL_ESCAPE_SLASH) return TRUE;
917 if (int_flags & WINE_URL_ESCAPE_QUESTION) return TRUE;
921 if (int_flags & WINE_URL_ESCAPE_HASH) return TRUE;
931 /*************************************************************************
932 * UrlEscapeW [SHLWAPI.@]
934 * Converts unsafe characters in a Url into escape sequences.
937 * pszUrl [I] Url to modify
938 * pszEscaped [O] Destination for modified Url
939 * pcchEscaped [I/O] Length of pszUrl, destination for length of pszEscaped
940 * dwFlags [I] URL_ flags from "shlwapi.h"
943 * Success: S_OK. pszEscaped contains the escaped Url, pcchEscaped
944 * contains its length.
945 * Failure: E_POINTER, if pszEscaped is not large enough. In this case
946 * pcchEscaped is set to the required length.
948 * Converts unsafe characters into their escape sequences.
951 * - By default this function stops converting at the first '?' or
953 * - If dwFlags contains URL_ESCAPE_SPACES_ONLY then only spaces are
954 * converted, but the conversion continues past a '?' or '#'.
955 * - Note that this function did not work well (or at all) in shlwapi version 4.
958 * Only the following flags are implemented:
959 *| URL_ESCAPE_SPACES_ONLY
960 *| URL_DONT_ESCAPE_EXTRA_INFO
961 *| URL_ESCAPE_SEGMENT_ONLY
962 *| URL_ESCAPE_PERCENT
964 HRESULT WINAPI UrlEscapeW(
971 DWORD needed = 0, ret;
972 BOOL stop_escaping = FALSE;
973 WCHAR next[5], *dst = pszEscaped;
975 PARSEDURLW parsed_url;
978 static const WCHAR localhost[] = {'l','o','c','a','l','h','o','s','t',0};
980 TRACE("(%s %p %p 0x%08x)\n", debugstr_w(pszUrl), pszEscaped,
981 pcchEscaped, dwFlags);
983 if(!pszUrl || !pcchEscaped)
986 if(dwFlags & ~(URL_ESCAPE_SPACES_ONLY |
987 URL_ESCAPE_SEGMENT_ONLY |
988 URL_DONT_ESCAPE_EXTRA_INFO |
990 FIXME("Unimplemented flags: %08x\n", dwFlags);
993 if (dwFlags & URL_ESCAPE_SPACES_ONLY)
994 /* if SPACES_ONLY specified, reset the other controls */
995 dwFlags &= ~(URL_DONT_ESCAPE_EXTRA_INFO |
997 URL_ESCAPE_SEGMENT_ONLY);
1000 /* if SPACES_ONLY *not* specified the assume DONT_ESCAPE_EXTRA_INFO */
1001 dwFlags |= URL_DONT_ESCAPE_EXTRA_INFO;
1005 if(dwFlags & URL_ESCAPE_SEGMENT_ONLY) {
1006 int_flags = WINE_URL_ESCAPE_QUESTION | WINE_URL_ESCAPE_HASH | WINE_URL_ESCAPE_SLASH;
1008 parsed_url.cbSize = sizeof(parsed_url);
1009 if(ParseURLW(pszUrl, &parsed_url) != S_OK)
1010 parsed_url.nScheme = URL_SCHEME_INVALID;
1012 TRACE("scheme = %d (%s)\n", parsed_url.nScheme, debugstr_wn(parsed_url.pszProtocol, parsed_url.cchProtocol));
1014 if(dwFlags & URL_DONT_ESCAPE_EXTRA_INFO)
1015 int_flags = WINE_URL_STOP_ON_HASH | WINE_URL_STOP_ON_QUESTION;
1017 switch(parsed_url.nScheme) {
1018 case URL_SCHEME_FILE:
1019 int_flags |= WINE_URL_BASH_AS_SLASH | WINE_URL_COLLAPSE_SLASHES | WINE_URL_ESCAPE_HASH;
1020 int_flags &= ~WINE_URL_STOP_ON_HASH;
1023 case URL_SCHEME_HTTP:
1024 case URL_SCHEME_HTTPS:
1025 int_flags |= WINE_URL_BASH_AS_SLASH;
1026 if(parsed_url.pszSuffix[0] != '/' && parsed_url.pszSuffix[0] != '\\')
1027 int_flags |= WINE_URL_ESCAPE_SLASH;
1030 case URL_SCHEME_MAILTO:
1031 int_flags |= WINE_URL_ESCAPE_SLASH | WINE_URL_ESCAPE_QUESTION | WINE_URL_ESCAPE_HASH;
1032 int_flags &= ~(WINE_URL_STOP_ON_QUESTION | WINE_URL_STOP_ON_HASH);
1035 case URL_SCHEME_INVALID:
1038 case URL_SCHEME_FTP:
1040 if(parsed_url.pszSuffix[0] != '/')
1041 int_flags |= WINE_URL_ESCAPE_SLASH;
1046 for(src = pszUrl; *src; ) {
1050 if((int_flags & WINE_URL_COLLAPSE_SLASHES) && src == pszUrl + parsed_url.cchProtocol + 1) {
1051 int localhost_len = sizeof(localhost)/sizeof(WCHAR) - 1;
1052 while(cur == '/' || cur == '\\') {
1056 if(slashes == 2 && !strncmpiW(src, localhost, localhost_len)) { /* file://localhost/ -> file:/// */
1057 if(*(src + localhost_len) == '/' || *(src + localhost_len) == '\\')
1058 src += localhost_len + 1;
1065 next[0] = next[1] = next[2] = '/';
1072 next[0] = next[1] = '/';
1079 if(cur == '#' && (int_flags & WINE_URL_STOP_ON_HASH))
1080 stop_escaping = TRUE;
1082 if(cur == '?' && (int_flags & WINE_URL_STOP_ON_QUESTION))
1083 stop_escaping = TRUE;
1085 if(cur == '\\' && (int_flags & WINE_URL_BASH_AS_SLASH) && !stop_escaping) cur = '/';
1087 if(URL_NeedEscapeW(cur, dwFlags, int_flags) && stop_escaping == FALSE) {
1089 next[1] = hexDigits[(cur >> 4) & 0xf];
1090 next[2] = hexDigits[cur & 0xf];
1099 if(needed + len <= *pcchEscaped) {
1100 memcpy(dst, next, len*sizeof(WCHAR));
1106 if(needed < *pcchEscaped) {
1110 needed++; /* add one for the '\0' */
1113 *pcchEscaped = needed;
1118 /*************************************************************************
1119 * UrlUnescapeA [SHLWAPI.@]
1121 * Converts Url escape sequences back to ordinary characters.
1124 * pszUrl [I/O] Url to convert
1125 * pszUnescaped [O] Destination for converted Url
1126 * pcchUnescaped [I/O] Size of output string
1127 * dwFlags [I] URL_ESCAPE_ Flags from "shlwapi.h"
1130 * Success: S_OK. The converted value is in pszUnescaped, or in pszUrl if
1131 * dwFlags includes URL_ESCAPE_INPLACE.
1132 * Failure: E_POINTER if the converted Url is bigger than pcchUnescaped. In
1133 * this case pcchUnescaped is set to the size required.
1135 * If dwFlags includes URL_DONT_ESCAPE_EXTRA_INFO, the conversion stops at
1136 * the first occurrence of either a '?' or '#' character.
1138 HRESULT WINAPI UrlUnescapeA(
1141 LPDWORD pcchUnescaped,
1148 BOOL stop_unescaping = FALSE;
1150 TRACE("(%s, %p, %p, 0x%08x)\n", debugstr_a(pszUrl), pszUnescaped,
1151 pcchUnescaped, dwFlags);
1153 if(!pszUrl || (!pszUnescaped && !(dwFlags & URL_UNESCAPE_INPLACE)) || !pcchUnescaped)
1154 return E_INVALIDARG;
1156 if(dwFlags & URL_UNESCAPE_INPLACE)
1161 for(src = pszUrl, needed = 0; *src; src++, needed++) {
1162 if(dwFlags & URL_DONT_UNESCAPE_EXTRA_INFO &&
1163 (*src == '#' || *src == '?')) {
1164 stop_unescaping = TRUE;
1166 } else if(*src == '%' && isxdigit(*(src + 1)) && isxdigit(*(src + 2))
1167 && stop_unescaping == FALSE) {
1170 memcpy(buf, src + 1, 2);
1172 ih = strtol(buf, NULL, 16);
1174 src += 2; /* Advance to end of escape */
1178 if(dwFlags & URL_UNESCAPE_INPLACE || needed < *pcchUnescaped)
1182 if(dwFlags & URL_UNESCAPE_INPLACE || needed < *pcchUnescaped) {
1186 needed++; /* add one for the '\0' */
1189 if(!(dwFlags & URL_UNESCAPE_INPLACE))
1190 *pcchUnescaped = needed;
1193 TRACE("result %s\n", (dwFlags & URL_UNESCAPE_INPLACE) ?
1194 debugstr_a(pszUrl) : debugstr_a(pszUnescaped));
1200 /*************************************************************************
1201 * UrlUnescapeW [SHLWAPI.@]
1205 HRESULT WINAPI UrlUnescapeW(
1207 LPWSTR pszUnescaped,
1208 LPDWORD pcchUnescaped,
1215 BOOL stop_unescaping = FALSE;
1217 TRACE("(%s, %p, %p, 0x%08x)\n", debugstr_w(pszUrl), pszUnescaped,
1218 pcchUnescaped, dwFlags);
1220 if(!pszUrl || (!pszUnescaped && !(dwFlags & URL_UNESCAPE_INPLACE))|| !pcchUnescaped)
1221 return E_INVALIDARG;
1223 if(dwFlags & URL_UNESCAPE_INPLACE)
1228 for(src = pszUrl, needed = 0; *src; src++, needed++) {
1229 if(dwFlags & URL_DONT_UNESCAPE_EXTRA_INFO &&
1230 (*src == '#' || *src == '?')) {
1231 stop_unescaping = TRUE;
1233 } else if(*src == '%' && isxdigitW(*(src + 1)) && isxdigitW(*(src + 2))
1234 && stop_unescaping == FALSE) {
1236 WCHAR buf[5] = {'0','x',0};
1237 memcpy(buf + 2, src + 1, 2*sizeof(WCHAR));
1239 StrToIntExW(buf, STIF_SUPPORT_HEX, &ih);
1241 src += 2; /* Advance to end of escape */
1245 if(dwFlags & URL_UNESCAPE_INPLACE || needed < *pcchUnescaped)
1249 if(dwFlags & URL_UNESCAPE_INPLACE || needed < *pcchUnescaped) {
1253 needed++; /* add one for the '\0' */
1256 if(!(dwFlags & URL_UNESCAPE_INPLACE))
1257 *pcchUnescaped = needed;
1260 TRACE("result %s\n", (dwFlags & URL_UNESCAPE_INPLACE) ?
1261 debugstr_w(pszUrl) : debugstr_w(pszUnescaped));
1267 /*************************************************************************
1268 * UrlGetLocationA [SHLWAPI.@]
1270 * Get the location from a Url.
1273 * pszUrl [I] Url to get the location from
1276 * A pointer to the start of the location in pszUrl, or NULL if there is
1280 * - MSDN erroneously states that "The location is the segment of the Url
1281 * starting with a '?' or '#' character". Neither V4 nor V5 of shlwapi.dll
1282 * stop at '?' and always return a NULL in this case.
1283 * - MSDN also erroneously states that "If a file URL has a query string,
1284 * the returned string is the query string". In all tested cases, if the
1285 * Url starts with "fi" then a NULL is returned. V5 gives the following results:
1288 *| NULL file://aa/b/cd#hohoh
1289 *| #hohoh http://aa/b/cd#hohoh
1290 *| NULL fi://aa/b/cd#hohoh
1291 *| #hohoh ff://aa/b/cd#hohoh
1293 LPCSTR WINAPI UrlGetLocationA(
1299 base.cbSize = sizeof(base);
1300 res1 = ParseURLA(pszUrl, &base);
1301 if (res1) return NULL; /* invalid scheme */
1303 /* if scheme is file: then never return pointer */
1304 if (strncmp(base.pszProtocol, "file", min(4,base.cchProtocol)) == 0) return NULL;
1306 /* Look for '#' and return its addr */
1307 return strchr(base.pszSuffix, '#');
1310 /*************************************************************************
1311 * UrlGetLocationW [SHLWAPI.@]
1313 * See UrlGetLocationA.
1315 LPCWSTR WINAPI UrlGetLocationW(
1321 base.cbSize = sizeof(base);
1322 res1 = ParseURLW(pszUrl, &base);
1323 if (res1) return NULL; /* invalid scheme */
1325 /* if scheme is file: then never return pointer */
1326 if (strncmpW(base.pszProtocol, fileW, min(4,base.cchProtocol)) == 0) return NULL;
1328 /* Look for '#' and return its addr */
1329 return strchrW(base.pszSuffix, '#');
1332 /*************************************************************************
1333 * UrlCompareA [SHLWAPI.@]
1338 * pszUrl1 [I] First Url to compare
1339 * pszUrl2 [I] Url to compare to pszUrl1
1340 * fIgnoreSlash [I] TRUE = compare only up to a final slash
1343 * less than zero, zero, or greater than zero indicating pszUrl2 is greater
1344 * than, equal to, or less than pszUrl1 respectively.
1346 INT WINAPI UrlCompareA(
1351 INT ret, len, len1, len2;
1354 return strcmp(pszUrl1, pszUrl2);
1355 len1 = strlen(pszUrl1);
1356 if (pszUrl1[len1-1] == '/') len1--;
1357 len2 = strlen(pszUrl2);
1358 if (pszUrl2[len2-1] == '/') len2--;
1360 return strncmp(pszUrl1, pszUrl2, len1);
1361 len = min(len1, len2);
1362 ret = strncmp(pszUrl1, pszUrl2, len);
1363 if (ret) return ret;
1364 if (len1 > len2) return 1;
1368 /*************************************************************************
1369 * UrlCompareW [SHLWAPI.@]
1373 INT WINAPI UrlCompareW(
1379 size_t len, len1, len2;
1382 return strcmpW(pszUrl1, pszUrl2);
1383 len1 = strlenW(pszUrl1);
1384 if (pszUrl1[len1-1] == '/') len1--;
1385 len2 = strlenW(pszUrl2);
1386 if (pszUrl2[len2-1] == '/') len2--;
1388 return strncmpW(pszUrl1, pszUrl2, len1);
1389 len = min(len1, len2);
1390 ret = strncmpW(pszUrl1, pszUrl2, len);
1391 if (ret) return ret;
1392 if (len1 > len2) return 1;
1396 /*************************************************************************
1397 * HashData [SHLWAPI.@]
1399 * Hash an input block into a variable sized digest.
1402 * lpSrc [I] Input block
1403 * nSrcLen [I] Length of lpSrc
1404 * lpDest [I] Output for hash digest
1405 * nDestLen [I] Length of lpDest
1408 * Success: TRUE. lpDest is filled with the computed hash value.
1409 * Failure: FALSE, if any argument is invalid.
1411 HRESULT WINAPI HashData(const unsigned char *lpSrc, DWORD nSrcLen,
1412 unsigned char *lpDest, DWORD nDestLen)
1414 INT srcCount = nSrcLen - 1, destCount = nDestLen - 1;
1416 if (IsBadReadPtr(lpSrc, nSrcLen) ||
1417 IsBadWritePtr(lpDest, nDestLen))
1418 return E_INVALIDARG;
1420 while (destCount >= 0)
1422 lpDest[destCount] = (destCount & 0xff);
1426 while (srcCount >= 0)
1428 destCount = nDestLen - 1;
1429 while (destCount >= 0)
1431 lpDest[destCount] = HashDataLookup[lpSrc[srcCount] ^ lpDest[destCount]];
1439 /*************************************************************************
1440 * UrlHashA [SHLWAPI.@]
1442 * Produce a Hash from a Url.
1445 * pszUrl [I] Url to hash
1446 * lpDest [O] Destinationh for hash
1447 * nDestLen [I] Length of lpDest
1450 * Success: S_OK. lpDest is filled with the computed hash value.
1451 * Failure: E_INVALIDARG, if any argument is invalid.
1453 HRESULT WINAPI UrlHashA(LPCSTR pszUrl, unsigned char *lpDest, DWORD nDestLen)
1455 if (IsBadStringPtrA(pszUrl, -1) || IsBadWritePtr(lpDest, nDestLen))
1456 return E_INVALIDARG;
1458 HashData((const BYTE*)pszUrl, (int)strlen(pszUrl), lpDest, nDestLen);
1462 /*************************************************************************
1463 * UrlHashW [SHLWAPI.@]
1467 HRESULT WINAPI UrlHashW(LPCWSTR pszUrl, unsigned char *lpDest, DWORD nDestLen)
1469 char szUrl[MAX_PATH];
1471 TRACE("(%s,%p,%d)\n",debugstr_w(pszUrl), lpDest, nDestLen);
1473 if (IsBadStringPtrW(pszUrl, -1) || IsBadWritePtr(lpDest, nDestLen))
1474 return E_INVALIDARG;
1476 /* Win32 hashes the data as an ASCII string, presumably so that both A+W
1477 * return the same digests for the same URL.
1479 WideCharToMultiByte(0, 0, pszUrl, -1, szUrl, MAX_PATH, 0, 0);
1480 HashData((const BYTE*)szUrl, (int)strlen(szUrl), lpDest, nDestLen);
1484 /*************************************************************************
1485 * UrlApplySchemeA [SHLWAPI.@]
1487 * Apply a scheme to a Url.
1490 * pszIn [I] Url to apply scheme to
1491 * pszOut [O] Destination for modified Url
1492 * pcchOut [I/O] Length of pszOut/destination for length of pszOut
1493 * dwFlags [I] URL_ flags from "shlwapi.h"
1496 * Success: S_OK: pszOut contains the modified Url, pcchOut contains its length.
1497 * Failure: An HRESULT error code describing the error.
1499 HRESULT WINAPI UrlApplySchemeA(LPCSTR pszIn, LPSTR pszOut, LPDWORD pcchOut, DWORD dwFlags)
1502 DWORD ret, len, len2;
1504 TRACE("(in %s, out size %d, flags %08x) using W version\n",
1505 debugstr_a(pszIn), *pcchOut, dwFlags);
1507 in = HeapAlloc(GetProcessHeap(), 0,
1508 (2*INTERNET_MAX_URL_LENGTH) * sizeof(WCHAR));
1509 out = in + INTERNET_MAX_URL_LENGTH;
1511 MultiByteToWideChar(0, 0, pszIn, -1, in, INTERNET_MAX_URL_LENGTH);
1512 len = INTERNET_MAX_URL_LENGTH;
1514 ret = UrlApplySchemeW(in, out, &len, dwFlags);
1515 if ((ret != S_OK) && (ret != S_FALSE)) {
1516 HeapFree(GetProcessHeap(), 0, in);
1520 len2 = WideCharToMultiByte(0, 0, out, len+1, 0, 0, 0, 0);
1521 if (len2 > *pcchOut) {
1523 HeapFree(GetProcessHeap(), 0, in);
1526 WideCharToMultiByte(0, 0, out, len+1, pszOut, *pcchOut, 0, 0);
1528 HeapFree(GetProcessHeap(), 0, in);
1532 static HRESULT URL_GuessScheme(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut)
1537 DWORD value_len, data_len, dwType, i;
1538 WCHAR reg_path[MAX_PATH];
1539 WCHAR value[MAX_PATH], data[MAX_PATH];
1542 MultiByteToWideChar(0, 0,
1543 "Software\\Microsoft\\Windows\\CurrentVersion\\URL\\Prefixes",
1544 -1, reg_path, MAX_PATH);
1545 RegOpenKeyExW(HKEY_LOCAL_MACHINE, reg_path, 0, 1, &newkey);
1547 while(value_len = data_len = MAX_PATH,
1548 RegEnumValueW(newkey, index, value, &value_len,
1549 0, &dwType, (LPVOID)data, &data_len) == 0) {
1550 TRACE("guess %d %s is %s\n",
1551 index, debugstr_w(value), debugstr_w(data));
1554 for(i=0; i<value_len; i++) {
1557 /* remember that TRUE is not-equal */
1558 j = ChrCmpIW(Wxx, Wyy);
1561 if ((i == value_len) && !j) {
1562 if (strlenW(data) + strlenW(pszIn) + 1 > *pcchOut) {
1563 *pcchOut = strlenW(data) + strlenW(pszIn) + 1;
1564 RegCloseKey(newkey);
1567 strcpyW(pszOut, data);
1568 strcatW(pszOut, pszIn);
1569 *pcchOut = strlenW(pszOut);
1570 TRACE("matched and set to %s\n", debugstr_w(pszOut));
1571 RegCloseKey(newkey);
1576 RegCloseKey(newkey);
1580 static HRESULT URL_ApplyDefault(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut)
1583 DWORD data_len, dwType;
1584 WCHAR reg_path[MAX_PATH];
1585 WCHAR value[MAX_PATH], data[MAX_PATH];
1587 /* get and prepend default */
1588 MultiByteToWideChar(0, 0,
1589 "Software\\Microsoft\\Windows\\CurrentVersion\\URL\\DefaultPrefix",
1590 -1, reg_path, MAX_PATH);
1591 RegOpenKeyExW(HKEY_LOCAL_MACHINE, reg_path, 0, 1, &newkey);
1592 data_len = MAX_PATH;
1595 RegQueryValueExW(newkey, value, 0, &dwType, (LPBYTE)data, &data_len);
1596 RegCloseKey(newkey);
1597 if (strlenW(data) + strlenW(pszIn) + 1 > *pcchOut) {
1598 *pcchOut = strlenW(data) + strlenW(pszIn) + 1;
1601 strcpyW(pszOut, data);
1602 strcatW(pszOut, pszIn);
1603 *pcchOut = strlenW(pszOut);
1604 TRACE("used default %s\n", debugstr_w(pszOut));
1608 /*************************************************************************
1609 * UrlApplySchemeW [SHLWAPI.@]
1611 * See UrlApplySchemeA.
1613 HRESULT WINAPI UrlApplySchemeW(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut, DWORD dwFlags)
1615 PARSEDURLW in_scheme;
1619 TRACE("(in %s, out size %d, flags %08x)\n",
1620 debugstr_w(pszIn), *pcchOut, dwFlags);
1622 if (dwFlags & URL_APPLY_GUESSFILE) {
1623 FIXME("(%s %p %p(%d) 0x%08x): stub URL_APPLY_GUESSFILE not implemented\n",
1624 debugstr_w(pszIn), pszOut, pcchOut, *pcchOut, dwFlags);
1625 strcpyW(pszOut, pszIn);
1626 *pcchOut = strlenW(pszOut);
1630 in_scheme.cbSize = sizeof(in_scheme);
1631 /* See if the base has a scheme */
1632 res1 = ParseURLW(pszIn, &in_scheme);
1634 /* no scheme in input, need to see if we need to guess */
1635 if (dwFlags & URL_APPLY_GUESSSCHEME) {
1636 if ((ret = URL_GuessScheme(pszIn, pszOut, pcchOut)) != -1)
1641 /* we have a scheme, see if valid (known scheme) */
1642 if (in_scheme.nScheme) {
1643 /* have valid scheme, so just copy and exit */
1644 if (strlenW(pszIn) + 1 > *pcchOut) {
1645 *pcchOut = strlenW(pszIn) + 1;
1648 strcpyW(pszOut, pszIn);
1649 *pcchOut = strlenW(pszOut);
1650 TRACE("valid scheme, returning copy\n");
1655 /* If we are here, then either invalid scheme,
1656 * or no scheme and can't/failed guess.
1658 if ( ( ((res1 == 0) && (dwFlags & URL_APPLY_FORCEAPPLY)) ||
1660 (dwFlags & URL_APPLY_DEFAULT)) {
1661 /* find and apply default scheme */
1662 return URL_ApplyDefault(pszIn, pszOut, pcchOut);
1665 /* just copy and give proper return code */
1666 if (strlenW(pszIn) + 1 > *pcchOut) {
1667 *pcchOut = strlenW(pszIn) + 1;
1670 strcpyW(pszOut, pszIn);
1671 *pcchOut = strlenW(pszOut);
1672 TRACE("returning copy, left alone\n");
1676 /*************************************************************************
1677 * UrlIsA [SHLWAPI.@]
1679 * Determine if a Url is of a certain class.
1682 * pszUrl [I] Url to check
1683 * Urlis [I] URLIS_ constant from "shlwapi.h"
1686 * TRUE if pszUrl belongs to the class type in Urlis.
1689 BOOL WINAPI UrlIsA(LPCSTR pszUrl, URLIS Urlis)
1695 TRACE("(%s %d)\n", debugstr_a(pszUrl), Urlis);
1700 base.cbSize = sizeof(base);
1701 res1 = ParseURLA(pszUrl, &base);
1702 if (res1) return FALSE; /* invalid scheme */
1703 switch (base.nScheme)
1705 case URL_SCHEME_MAILTO:
1706 case URL_SCHEME_SHELL:
1707 case URL_SCHEME_JAVASCRIPT:
1708 case URL_SCHEME_VBSCRIPT:
1709 case URL_SCHEME_ABOUT:
1715 return !StrCmpNA("file:", pszUrl, 5);
1717 case URLIS_DIRECTORY:
1718 last = pszUrl + strlen(pszUrl) - 1;
1719 return (last >= pszUrl && (*last == '/' || *last == '\\' ));
1722 return PathIsURLA(pszUrl);
1724 case URLIS_NOHISTORY:
1725 case URLIS_APPLIABLE:
1726 case URLIS_HASQUERY:
1728 FIXME("(%s %d): stub\n", debugstr_a(pszUrl), Urlis);
1733 /*************************************************************************
1734 * UrlIsW [SHLWAPI.@]
1738 BOOL WINAPI UrlIsW(LPCWSTR pszUrl, URLIS Urlis)
1740 static const WCHAR stemp[] = { 'f','i','l','e',':',0 };
1745 TRACE("(%s %d)\n", debugstr_w(pszUrl), Urlis);
1750 base.cbSize = sizeof(base);
1751 res1 = ParseURLW(pszUrl, &base);
1752 if (res1) return FALSE; /* invalid scheme */
1753 switch (base.nScheme)
1755 case URL_SCHEME_MAILTO:
1756 case URL_SCHEME_SHELL:
1757 case URL_SCHEME_JAVASCRIPT:
1758 case URL_SCHEME_VBSCRIPT:
1759 case URL_SCHEME_ABOUT:
1765 return !strncmpW(stemp, pszUrl, 5);
1767 case URLIS_DIRECTORY:
1768 last = pszUrl + strlenW(pszUrl) - 1;
1769 return (last >= pszUrl && (*last == '/' || *last == '\\'));
1772 return PathIsURLW(pszUrl);
1774 case URLIS_NOHISTORY:
1775 case URLIS_APPLIABLE:
1776 case URLIS_HASQUERY:
1778 FIXME("(%s %d): stub\n", debugstr_w(pszUrl), Urlis);
1783 /*************************************************************************
1784 * UrlIsNoHistoryA [SHLWAPI.@]
1786 * Determine if a Url should not be stored in the users history list.
1789 * pszUrl [I] Url to check
1792 * TRUE, if pszUrl should be excluded from the history list,
1795 BOOL WINAPI UrlIsNoHistoryA(LPCSTR pszUrl)
1797 return UrlIsA(pszUrl, URLIS_NOHISTORY);
1800 /*************************************************************************
1801 * UrlIsNoHistoryW [SHLWAPI.@]
1803 * See UrlIsNoHistoryA.
1805 BOOL WINAPI UrlIsNoHistoryW(LPCWSTR pszUrl)
1807 return UrlIsW(pszUrl, URLIS_NOHISTORY);
1810 /*************************************************************************
1811 * UrlIsOpaqueA [SHLWAPI.@]
1813 * Determine if a Url is opaque.
1816 * pszUrl [I] Url to check
1819 * TRUE if pszUrl is opaque,
1823 * An opaque Url is one that does not start with "<protocol>://".
1825 BOOL WINAPI UrlIsOpaqueA(LPCSTR pszUrl)
1827 return UrlIsA(pszUrl, URLIS_OPAQUE);
1830 /*************************************************************************
1831 * UrlIsOpaqueW [SHLWAPI.@]
1835 BOOL WINAPI UrlIsOpaqueW(LPCWSTR pszUrl)
1837 return UrlIsW(pszUrl, URLIS_OPAQUE);
1840 /*************************************************************************
1841 * Scans for characters of type "type" and when not matching found,
1842 * returns pointer to it and length in size.
1844 * Characters tested based on RFC 1738
1846 static LPCWSTR URL_ScanID(LPCWSTR start, LPDWORD size, WINE_URL_SCAN_TYPE type)
1848 static DWORD alwayszero = 0;
1857 if ( (islowerW(*start) && isalphaW(*start)) ||
1872 if ( isalphaW(*start) ||
1874 /* user/password only characters */
1879 /* *extra* characters */
1886 /* *safe* characters */
1894 } else if (*start == '%') {
1895 if (isxdigitW(*(start+1)) &&
1896 isxdigitW(*(start+2))) {
1908 if (isdigitW(*start)) {
1919 if (isalnumW(*start) ||
1930 FIXME("unknown type %d\n", type);
1931 return (LPWSTR)&alwayszero;
1933 /* TRACE("scanned %d characters next char %p<%c>\n",
1934 *size, start, *start); */
1938 /*************************************************************************
1939 * Attempt to parse URL into pieces.
1941 static LONG URL_ParseUrl(LPCWSTR pszUrl, WINE_PARSE_URL *pl)
1945 memset(pl, 0, sizeof(WINE_PARSE_URL));
1946 pl->pScheme = pszUrl;
1947 work = URL_ScanID(pl->pScheme, &pl->szScheme, SCHEME);
1948 if (!*work || (*work != ':')) goto ErrorExit;
1950 if ((*work != '/') || (*(work+1) != '/')) goto ErrorExit;
1951 pl->pUserName = work + 2;
1952 work = URL_ScanID(pl->pUserName, &pl->szUserName, USERPASS);
1953 if (*work == ':' ) {
1954 /* parse password */
1956 pl->pPassword = work;
1957 work = URL_ScanID(pl->pPassword, &pl->szPassword, USERPASS);
1959 /* what we just parsed must be the hostname and port
1960 * so reset pointers and clear then let it parse */
1961 pl->szUserName = pl->szPassword = 0;
1962 work = pl->pUserName - 1;
1963 pl->pUserName = pl->pPassword = 0;
1965 } else if (*work == '@') {
1969 } else if (!*work || (*work == '/') || (*work == '.')) {
1970 /* what was parsed was hostname, so reset pointers and let it parse */
1971 pl->szUserName = pl->szPassword = 0;
1972 work = pl->pUserName - 1;
1973 pl->pUserName = pl->pPassword = 0;
1974 } else goto ErrorExit;
1976 /* now start parsing hostname or hostnumber */
1978 pl->pHostName = work;
1979 work = URL_ScanID(pl->pHostName, &pl->szHostName, HOST);
1984 work = URL_ScanID(pl->pPort, &pl->szPort, PORT);
1987 /* see if query string */
1988 pl->pQuery = strchrW(work, '?');
1989 if (pl->pQuery) pl->szQuery = strlenW(pl->pQuery);
1991 TRACE("parse successful: scheme=%p(%d), user=%p(%d), pass=%p(%d), host=%p(%d), port=%p(%d), query=%p(%d)\n",
1992 pl->pScheme, pl->szScheme,
1993 pl->pUserName, pl->szUserName,
1994 pl->pPassword, pl->szPassword,
1995 pl->pHostName, pl->szHostName,
1996 pl->pPort, pl->szPort,
1997 pl->pQuery, pl->szQuery);
2000 FIXME("failed to parse %s\n", debugstr_w(pszUrl));
2001 return E_INVALIDARG;
2004 /*************************************************************************
2005 * UrlGetPartA [SHLWAPI.@]
2007 * Retrieve part of a Url.
2010 * pszIn [I] Url to parse
2011 * pszOut [O] Destination for part of pszIn requested
2012 * pcchOut [I] Size of pszOut
2013 * [O] length of pszOut string EXLUDING '\0' if S_OK, otherwise
2014 * needed size of pszOut INCLUDING '\0'.
2015 * dwPart [I] URL_PART_ enum from "shlwapi.h"
2016 * dwFlags [I] URL_ flags from "shlwapi.h"
2019 * Success: S_OK. pszOut contains the part requested, pcchOut contains its length.
2020 * Failure: An HRESULT error code describing the error.
2022 HRESULT WINAPI UrlGetPartA(LPCSTR pszIn, LPSTR pszOut, LPDWORD pcchOut,
2023 DWORD dwPart, DWORD dwFlags)
2026 DWORD ret, len, len2;
2028 in = HeapAlloc(GetProcessHeap(), 0,
2029 (2*INTERNET_MAX_URL_LENGTH) * sizeof(WCHAR));
2030 out = in + INTERNET_MAX_URL_LENGTH;
2032 MultiByteToWideChar(0, 0, pszIn, -1, in, INTERNET_MAX_URL_LENGTH);
2034 len = INTERNET_MAX_URL_LENGTH;
2035 ret = UrlGetPartW(in, out, &len, dwPart, dwFlags);
2038 HeapFree(GetProcessHeap(), 0, in);
2042 len2 = WideCharToMultiByte(0, 0, out, len, 0, 0, 0, 0);
2043 if (len2 > *pcchOut) {
2045 HeapFree(GetProcessHeap(), 0, in);
2048 WideCharToMultiByte(0, 0, out, len+1, pszOut, *pcchOut, 0, 0);
2050 HeapFree(GetProcessHeap(), 0, in);
2054 /*************************************************************************
2055 * UrlGetPartW [SHLWAPI.@]
2059 HRESULT WINAPI UrlGetPartW(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut,
2060 DWORD dwPart, DWORD dwFlags)
2064 DWORD size, schsize;
2065 LPCWSTR addr, schaddr;
2067 TRACE("(%s %p %p(%d) %08x %08x)\n",
2068 debugstr_w(pszIn), pszOut, pcchOut, *pcchOut, dwPart, dwFlags);
2070 ret = URL_ParseUrl(pszIn, &pl);
2072 schaddr = pl.pScheme;
2073 schsize = pl.szScheme;
2076 case URL_PART_SCHEME:
2077 if (!pl.szScheme) return E_INVALIDARG;
2082 case URL_PART_HOSTNAME:
2083 if (!pl.szHostName) return E_INVALIDARG;
2084 addr = pl.pHostName;
2085 size = pl.szHostName;
2088 case URL_PART_USERNAME:
2089 if (!pl.szUserName) return E_INVALIDARG;
2090 addr = pl.pUserName;
2091 size = pl.szUserName;
2094 case URL_PART_PASSWORD:
2095 if (!pl.szPassword) return E_INVALIDARG;
2096 addr = pl.pPassword;
2097 size = pl.szPassword;
2101 if (!pl.szPort) return E_INVALIDARG;
2106 case URL_PART_QUERY:
2107 if (!pl.szQuery) return E_INVALIDARG;
2113 return E_INVALIDARG;
2116 if (dwFlags == URL_PARTFLAG_KEEPSCHEME) {
2117 if (*pcchOut < schsize + size + 2) {
2118 *pcchOut = schsize + size + 2;
2121 memcpy(pszOut, schaddr, schsize*sizeof(WCHAR));
2122 pszOut[schsize] = ':';
2123 memcpy(pszOut+schsize+1, addr, size*sizeof(WCHAR));
2124 pszOut[schsize+1+size] = 0;
2125 *pcchOut = schsize + 1 + size;
2128 if (*pcchOut < size + 1) {*pcchOut = size+1; return E_POINTER;}
2129 memcpy(pszOut, addr, size*sizeof(WCHAR));
2133 TRACE("len=%d %s\n", *pcchOut, debugstr_w(pszOut));
2138 /*************************************************************************
2139 * PathIsURLA [SHLWAPI.@]
2141 * Check if the given path is a Url.
2144 * lpszPath [I] Path to check.
2147 * TRUE if lpszPath is a Url.
2148 * FALSE if lpszPath is NULL or not a Url.
2150 BOOL WINAPI PathIsURLA(LPCSTR lpstrPath)
2155 if (!lpstrPath || !*lpstrPath) return FALSE;
2158 base.cbSize = sizeof(base);
2159 res1 = ParseURLA(lpstrPath, &base);
2160 return (base.nScheme != URL_SCHEME_INVALID);
2163 /*************************************************************************
2164 * PathIsURLW [SHLWAPI.@]
2168 BOOL WINAPI PathIsURLW(LPCWSTR lpstrPath)
2173 if (!lpstrPath || !*lpstrPath) return FALSE;
2176 base.cbSize = sizeof(base);
2177 res1 = ParseURLW(lpstrPath, &base);
2178 return (base.nScheme != URL_SCHEME_INVALID);
2181 /*************************************************************************
2182 * UrlCreateFromPathA [SHLWAPI.@]
2184 * See UrlCreateFromPathW
2186 HRESULT WINAPI UrlCreateFromPathA(LPCSTR pszPath, LPSTR pszUrl, LPDWORD pcchUrl, DWORD dwReserved)
2188 WCHAR bufW[INTERNET_MAX_URL_LENGTH];
2190 UNICODE_STRING pathW;
2192 DWORD lenW = sizeof(bufW)/sizeof(WCHAR), lenA;
2194 if(!RtlCreateUnicodeStringFromAsciiz(&pathW, pszPath))
2195 return E_INVALIDARG;
2196 if((ret = UrlCreateFromPathW(pathW.Buffer, urlW, &lenW, dwReserved)) == E_POINTER) {
2197 urlW = HeapAlloc(GetProcessHeap(), 0, lenW * sizeof(WCHAR));
2198 ret = UrlCreateFromPathW(pathW.Buffer, urlW, &lenW, dwReserved);
2200 if(ret == S_OK || ret == S_FALSE) {
2201 RtlUnicodeToMultiByteSize(&lenA, urlW, lenW * sizeof(WCHAR));
2202 if(*pcchUrl > lenA) {
2203 RtlUnicodeToMultiByteN(pszUrl, *pcchUrl - 1, &lenA, urlW, lenW * sizeof(WCHAR));
2207 *pcchUrl = lenA + 1;
2211 if(urlW != bufW) HeapFree(GetProcessHeap(), 0, urlW);
2212 RtlFreeUnicodeString(&pathW);
2216 /*************************************************************************
2217 * UrlCreateFromPathW [SHLWAPI.@]
2219 * Create a Url from a file path.
2222 * pszPath [I] Path to convert
2223 * pszUrl [O] Destination for the converted Url
2224 * pcchUrl [I/O] Length of pszUrl
2225 * dwReserved [I] Reserved, must be 0
2228 * Success: S_OK pszUrl contains the converted path, S_FALSE if the path is already a Url
2229 * Failure: An HRESULT error code.
2231 HRESULT WINAPI UrlCreateFromPathW(LPCWSTR pszPath, LPWSTR pszUrl, LPDWORD pcchUrl, DWORD dwReserved)
2236 WCHAR file_colonW[] = {'f','i','l','e',':',0};
2237 WCHAR three_slashesW[] = {'/','/','/',0};
2238 PARSEDURLW parsed_url;
2240 TRACE("(%s, %p, %p, 0x%08x)\n", debugstr_w(pszPath), pszUrl, pcchUrl, dwReserved);
2242 /* Validate arguments */
2243 if (dwReserved != 0)
2244 return E_INVALIDARG;
2245 if (!pszUrl || !pcchUrl)
2246 return E_INVALIDARG;
2249 parsed_url.cbSize = sizeof(parsed_url);
2250 if(ParseURLW(pszPath, &parsed_url) == S_OK) {
2251 if(parsed_url.nScheme != URL_SCHEME_INVALID && parsed_url.cchProtocol > 1) {
2252 needed = strlenW(pszPath);
2253 if (needed >= *pcchUrl) {
2254 *pcchUrl = needed + 1;
2258 strcpyW(pszUrl, pszPath);
2264 pszNewUrl = HeapAlloc(GetProcessHeap(), 0, (strlenW(pszPath) + 9) * sizeof(WCHAR)); /* "file:///" + pszPath_len + 1 */
2265 strcpyW(pszNewUrl, file_colonW);
2266 if(isalphaW(pszPath[0]) && pszPath[1] == ':')
2267 strcatW(pszNewUrl, three_slashesW);
2268 strcatW(pszNewUrl, pszPath);
2269 ret = UrlEscapeW(pszNewUrl, pszUrl, pcchUrl, URL_ESCAPE_PERCENT);
2271 HeapFree(GetProcessHeap(), 0, pszNewUrl);
2275 /*************************************************************************
2276 * SHAutoComplete [SHLWAPI.@]
2278 * Enable auto-completion for an edit control.
2281 * hwndEdit [I] Handle of control to enable auto-completion for
2282 * dwFlags [I] SHACF_ flags from "shlwapi.h"
2285 * Success: S_OK. Auto-completion is enabled for the control.
2286 * Failure: An HRESULT error code indicating the error.
2288 HRESULT WINAPI SHAutoComplete(HWND hwndEdit, DWORD dwFlags)
2290 FIXME("SHAutoComplete stub\n");
2294 /*************************************************************************
2295 * MLBuildResURLA [SHLWAPI.405]
2297 * Create a Url pointing to a resource in a module.
2300 * lpszLibName [I] Name of the module containing the resource
2301 * hMod [I] Callers module handle
2302 * dwFlags [I] Undocumented flags for loading the module
2303 * lpszRes [I] Resource name
2304 * lpszDest [O] Destination for resulting Url
2305 * dwDestLen [I] Length of lpszDest
2308 * Success: S_OK. lpszDest constains the resource Url.
2309 * Failure: E_INVALIDARG, if any argument is invalid, or
2310 * E_FAIL if dwDestLen is too small.
2312 HRESULT WINAPI MLBuildResURLA(LPCSTR lpszLibName, HMODULE hMod, DWORD dwFlags,
2313 LPCSTR lpszRes, LPSTR lpszDest, DWORD dwDestLen)
2315 WCHAR szLibName[MAX_PATH], szRes[MAX_PATH], szDest[MAX_PATH];
2319 MultiByteToWideChar(CP_ACP, 0, lpszLibName, -1, szLibName, sizeof(szLibName)/sizeof(WCHAR));
2322 MultiByteToWideChar(CP_ACP, 0, lpszRes, -1, szRes, sizeof(szRes)/sizeof(WCHAR));
2324 if (dwDestLen > sizeof(szLibName)/sizeof(WCHAR))
2325 dwDestLen = sizeof(szLibName)/sizeof(WCHAR);
2327 hRet = MLBuildResURLW(lpszLibName ? szLibName : NULL, hMod, dwFlags,
2328 lpszRes ? szRes : NULL, lpszDest ? szDest : NULL, dwDestLen);
2329 if (SUCCEEDED(hRet) && lpszDest)
2330 WideCharToMultiByte(CP_ACP, 0, szDest, -1, lpszDest, dwDestLen, 0, 0);
2335 /*************************************************************************
2336 * MLBuildResURLA [SHLWAPI.406]
2338 * See MLBuildResURLA.
2340 HRESULT WINAPI MLBuildResURLW(LPCWSTR lpszLibName, HMODULE hMod, DWORD dwFlags,
2341 LPCWSTR lpszRes, LPWSTR lpszDest, DWORD dwDestLen)
2343 static const WCHAR szRes[] = { 'r','e','s',':','/','/','\0' };
2344 #define szResLen ((sizeof(szRes) - sizeof(WCHAR))/sizeof(WCHAR))
2345 HRESULT hRet = E_FAIL;
2347 TRACE("(%s,%p,0x%08x,%s,%p,%d)\n", debugstr_w(lpszLibName), hMod, dwFlags,
2348 debugstr_w(lpszRes), lpszDest, dwDestLen);
2350 if (!lpszLibName || !hMod || hMod == INVALID_HANDLE_VALUE || !lpszRes ||
2351 !lpszDest || (dwFlags && dwFlags != 2))
2352 return E_INVALIDARG;
2354 if (dwDestLen >= szResLen + 1)
2356 dwDestLen -= (szResLen + 1);
2357 memcpy(lpszDest, szRes, sizeof(szRes));
2359 hMod = MLLoadLibraryW(lpszLibName, hMod, dwFlags);
2363 WCHAR szBuff[MAX_PATH];
2366 len = GetModuleFileNameW(hMod, szBuff, sizeof(szBuff)/sizeof(WCHAR));
2367 if (len && len < sizeof(szBuff)/sizeof(WCHAR))
2369 DWORD dwPathLen = strlenW(szBuff) + 1;
2371 if (dwDestLen >= dwPathLen)
2375 dwDestLen -= dwPathLen;
2376 memcpy(lpszDest + szResLen, szBuff, dwPathLen * sizeof(WCHAR));
2378 dwResLen = strlenW(lpszRes) + 1;
2379 if (dwDestLen >= dwResLen + 1)
2381 lpszDest[szResLen + dwPathLen + dwResLen] = '/';
2382 memcpy(lpszDest + szResLen + dwPathLen, lpszRes, dwResLen * sizeof(WCHAR));
2387 MLFreeLibrary(hMod);