4 * Copyright 2000 Huw D M Davies for CodeWeavers.
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 #include "wine/port.h"
30 #include "wine/unicode.h"
34 #define NO_SHLWAPI_STREAM
36 #include "wine/debug.h"
38 HMODULE WINAPI
MLLoadLibraryW(LPCWSTR
,HMODULE
,DWORD
);
39 BOOL WINAPI
MLFreeLibrary(HMODULE
);
40 HRESULT WINAPI
MLBuildResURLW(LPCWSTR
,HMODULE
,DWORD
,LPCWSTR
,LPWSTR
,DWORD
);
42 WINE_DEFAULT_DEBUG_CHANNEL(shell
);
44 /* The following schemes were identified in the native version of
45 * SHLWAPI.DLL version 5.50
48 URL_SCHEME scheme_number
;
52 static const SHL_2_inet_scheme shlwapi_schemes
[] = {
53 {URL_SCHEME_FTP
, "ftp"},
54 {URL_SCHEME_HTTP
, "http"},
55 {URL_SCHEME_GOPHER
, "gopher"},
56 {URL_SCHEME_MAILTO
, "mailto"},
57 {URL_SCHEME_NEWS
, "news"},
58 {URL_SCHEME_NNTP
, "nntp"},
59 {URL_SCHEME_TELNET
, "telnet"},
60 {URL_SCHEME_WAIS
, "wais"},
61 {URL_SCHEME_FILE
, "file"},
62 {URL_SCHEME_MK
, "mk"},
63 {URL_SCHEME_HTTPS
, "https"},
64 {URL_SCHEME_SHELL
, "shell"},
65 {URL_SCHEME_SNEWS
, "snews"},
66 {URL_SCHEME_LOCAL
, "local"},
67 {URL_SCHEME_JAVASCRIPT
, "javascript"},
68 {URL_SCHEME_VBSCRIPT
, "vbscript"},
69 {URL_SCHEME_ABOUT
, "about"},
70 {URL_SCHEME_RES
, "res"},
75 LPCWSTR pScheme
; /* [out] start of scheme */
76 DWORD szScheme
; /* [out] size of scheme (until colon) */
77 LPCWSTR pUserName
; /* [out] start of Username */
78 DWORD szUserName
; /* [out] size of Username (until ":" or "@") */
79 LPCWSTR pPassword
; /* [out] start of Password */
80 DWORD szPassword
; /* [out] size of Password (until "@") */
81 LPCWSTR pHostName
; /* [out] start of Hostname */
82 DWORD szHostName
; /* [out] size of Hostname (until ":" or "/") */
83 LPCWSTR pPort
; /* [out] start of Port */
84 DWORD szPort
; /* [out] size of Port (until "/" or eos) */
85 LPCWSTR pQuery
; /* [out] start of Query */
86 DWORD szQuery
; /* [out] size of Query (until eos) */
96 static const CHAR hexDigits
[] = "0123456789ABCDEF";
98 static const WCHAR fileW
[] = {'f','i','l','e','\0'};
100 static const unsigned char HashDataLookup
[256] = {
101 0x01, 0x0E, 0x6E, 0x19, 0x61, 0xAE, 0x84, 0x77, 0x8A, 0xAA, 0x7D, 0x76, 0x1B,
102 0xE9, 0x8C, 0x33, 0x57, 0xC5, 0xB1, 0x6B, 0xEA, 0xA9, 0x38, 0x44, 0x1E, 0x07,
103 0xAD, 0x49, 0xBC, 0x28, 0x24, 0x41, 0x31, 0xD5, 0x68, 0xBE, 0x39, 0xD3, 0x94,
104 0xDF, 0x30, 0x73, 0x0F, 0x02, 0x43, 0xBA, 0xD2, 0x1C, 0x0C, 0xB5, 0x67, 0x46,
105 0x16, 0x3A, 0x4B, 0x4E, 0xB7, 0xA7, 0xEE, 0x9D, 0x7C, 0x93, 0xAC, 0x90, 0xB0,
106 0xA1, 0x8D, 0x56, 0x3C, 0x42, 0x80, 0x53, 0x9C, 0xF1, 0x4F, 0x2E, 0xA8, 0xC6,
107 0x29, 0xFE, 0xB2, 0x55, 0xFD, 0xED, 0xFA, 0x9A, 0x85, 0x58, 0x23, 0xCE, 0x5F,
108 0x74, 0xFC, 0xC0, 0x36, 0xDD, 0x66, 0xDA, 0xFF, 0xF0, 0x52, 0x6A, 0x9E, 0xC9,
109 0x3D, 0x03, 0x59, 0x09, 0x2A, 0x9B, 0x9F, 0x5D, 0xA6, 0x50, 0x32, 0x22, 0xAF,
110 0xC3, 0x64, 0x63, 0x1A, 0x96, 0x10, 0x91, 0x04, 0x21, 0x08, 0xBD, 0x79, 0x40,
111 0x4D, 0x48, 0xD0, 0xF5, 0x82, 0x7A, 0x8F, 0x37, 0x69, 0x86, 0x1D, 0xA4, 0xB9,
112 0xC2, 0xC1, 0xEF, 0x65, 0xF2, 0x05, 0xAB, 0x7E, 0x0B, 0x4A, 0x3B, 0x89, 0xE4,
113 0x6C, 0xBF, 0xE8, 0x8B, 0x06, 0x18, 0x51, 0x14, 0x7F, 0x11, 0x5B, 0x5C, 0xFB,
114 0x97, 0xE1, 0xCF, 0x15, 0x62, 0x71, 0x70, 0x54, 0xE2, 0x12, 0xD6, 0xC7, 0xBB,
115 0x0D, 0x20, 0x5E, 0xDC, 0xE0, 0xD4, 0xF7, 0xCC, 0xC4, 0x2B, 0xF9, 0xEC, 0x2D,
116 0xF4, 0x6F, 0xB6, 0x99, 0x88, 0x81, 0x5A, 0xD9, 0xCA, 0x13, 0xA5, 0xE7, 0x47,
117 0xE6, 0x8E, 0x60, 0xE3, 0x3E, 0xB3, 0xF6, 0x72, 0xA2, 0x35, 0xA0, 0xD7, 0xCD,
118 0xB4, 0x2F, 0x6D, 0x2C, 0x26, 0x1F, 0x95, 0x87, 0x00, 0xD8, 0x34, 0x3F, 0x17,
119 0x25, 0x45, 0x27, 0x75, 0x92, 0xB8, 0xA3, 0xC8, 0xDE, 0xEB, 0xF8, 0xF3, 0xDB,
120 0x0A, 0x98, 0x83, 0x7B, 0xE5, 0xCB, 0x4C, 0x78, 0xD1 };
122 static BOOL
URL_JustLocation(LPCWSTR str
)
124 while(*str
&& (*str
== L
'/')) str
++;
126 while (*str
&& ((*str
== L
'-') ||
128 isalnumW(*str
))) str
++;
129 if (*str
== L
'/') return FALSE
;
135 /*************************************************************************
138 * Parse a Url into its constituent parts.
142 * y [O] Undocumented structure holding the parsed information
145 * Success: S_OK. y contains the parsed Url details.
146 * Failure: An HRESULT error code.
148 HRESULT WINAPI
ParseURLA(LPCSTR x
, PARSEDURLA
*y
)
151 const SHL_2_inet_scheme
*inet_pro
;
153 y
->nScheme
= URL_SCHEME_INVALID
;
154 if (y
->cbSize
!= sizeof(*y
)) return E_INVALIDARG
;
155 /* FIXME: leading white space generates error of 0x80041001 which
158 if (*x
<= ' ') return 0x80041001;
164 y
->cchProtocol
= cnt
;
173 /* check for no scheme in string start */
174 /* (apparently schemes *must* be larger than a single character) */
175 if ((*x
== '\0') || (y
->cchProtocol
<= 1)) {
176 y
->pszProtocol
= NULL
;
180 /* found scheme, set length of remainder */
181 y
->cchSuffix
= lstrlenA(y
->pszSuffix
);
183 /* see if known scheme and return indicator number */
184 y
->nScheme
= URL_SCHEME_UNKNOWN
;
185 inet_pro
= shlwapi_schemes
;
186 while (inet_pro
->scheme_name
) {
187 if (!strncasecmp(inet_pro
->scheme_name
, y
->pszProtocol
,
188 min(y
->cchProtocol
, lstrlenA(inet_pro
->scheme_name
)))) {
189 y
->nScheme
= inet_pro
->scheme_number
;
197 /*************************************************************************
200 * Unicode version of ParseURLA.
202 HRESULT WINAPI
ParseURLW(LPCWSTR x
, PARSEDURLW
*y
)
205 const SHL_2_inet_scheme
*inet_pro
;
209 y
->nScheme
= URL_SCHEME_INVALID
;
210 if (y
->cbSize
!= sizeof(*y
)) return E_INVALIDARG
;
211 /* FIXME: leading white space generates error of 0x80041001 which
214 if (*x
<= L
' ') return 0x80041001;
220 y
->cchProtocol
= cnt
;
229 /* check for no scheme in string start */
230 /* (apparently schemes *must* be larger than a single character) */
231 if ((*x
== L
'\0') || (y
->cchProtocol
<= 1)) {
232 y
->pszProtocol
= NULL
;
236 /* found scheme, set length of remainder */
237 y
->cchSuffix
= lstrlenW(y
->pszSuffix
);
239 /* see if known scheme and return indicator number */
240 len
= WideCharToMultiByte(0, 0, y
->pszProtocol
, y
->cchProtocol
, 0, 0, 0, 0);
241 cmpstr
= (LPSTR
)HeapAlloc(GetProcessHeap(), 0, len
);
242 WideCharToMultiByte(0, 0, y
->pszProtocol
, y
->cchProtocol
, cmpstr
, len
, 0, 0);
243 y
->nScheme
= URL_SCHEME_UNKNOWN
;
244 inet_pro
= shlwapi_schemes
;
245 while (inet_pro
->scheme_name
) {
246 if (!strncasecmp(inet_pro
->scheme_name
, cmpstr
,
247 min(len
, lstrlenA(inet_pro
->scheme_name
)))) {
248 y
->nScheme
= inet_pro
->scheme_number
;
253 HeapFree(GetProcessHeap(), 0, cmpstr
);
257 /*************************************************************************
258 * UrlCanonicalizeA [SHLWAPI.@]
260 * Canonicalize a Url.
263 * pszUrl [I] Url to cCanonicalize
264 * pszCanonicalized [O] Destination for converted Url.
265 * pcchCanonicalized [I/O] Length of pszUrl, destination for length of pszCanonicalized
266 * dwFlags [I] Flags controlling the conversion.
269 * Success: S_OK. The pszCanonicalized contains the converted Url.
270 * Failure: E_POINTER, if *pcchCanonicalized is too small.
272 * MSDN incorrectly describes the flags for this function. They should be:
273 *| URL_DONT_ESCAPE_EXTRA_INFO 0x02000000
274 *| URL_ESCAPE_SPACES_ONLY 0x04000000
275 *| URL_ESCAPE_PERCENT 0x00001000
276 *| URL_ESCAPE_UNSAFE 0x10000000
277 *| URL_UNESCAPE 0x10000000
278 *| URL_DONT_SIMPLIFY 0x08000000
279 *| URL_ESCAPE_SEGMENT_ONLY 0x00002000
281 HRESULT WINAPI
UrlCanonicalizeA(LPCSTR pszUrl
, LPSTR pszCanonicalized
,
282 LPDWORD pcchCanonicalized
, DWORD dwFlags
)
284 LPWSTR base
, canonical
;
285 DWORD ret
, len
, len2
;
287 TRACE("(%s %p %p 0x%08lx) using W version\n",
288 debugstr_a(pszUrl
), pszCanonicalized
,
289 pcchCanonicalized
, dwFlags
);
291 if(!pszUrl
|| !pszCanonicalized
|| !pcchCanonicalized
)
294 base
= HeapAlloc(GetProcessHeap(), 0,
295 (2*INTERNET_MAX_URL_LENGTH
) * sizeof(WCHAR
));
296 canonical
= base
+ INTERNET_MAX_URL_LENGTH
;
298 MultiByteToWideChar(0, 0, pszUrl
, -1, base
, INTERNET_MAX_URL_LENGTH
);
299 len
= INTERNET_MAX_URL_LENGTH
;
301 ret
= UrlCanonicalizeW(base
, canonical
, &len
, dwFlags
);
303 HeapFree(GetProcessHeap(), 0, base
);
307 len2
= WideCharToMultiByte(0, 0, canonical
, len
, 0, 0, 0, 0);
308 if (len2
> *pcchCanonicalized
) {
309 *pcchCanonicalized
= len
;
310 HeapFree(GetProcessHeap(), 0, base
);
313 WideCharToMultiByte(0, 0, canonical
, len
+1, pszCanonicalized
,
314 *pcchCanonicalized
, 0, 0);
315 *pcchCanonicalized
= len2
;
316 HeapFree(GetProcessHeap(), 0, base
);
320 /*************************************************************************
321 * UrlCanonicalizeW [SHLWAPI.@]
323 * See UrlCanonicalizeA.
325 HRESULT WINAPI
UrlCanonicalizeW(LPCWSTR pszUrl
, LPWSTR pszCanonicalized
,
326 LPDWORD pcchCanonicalized
, DWORD dwFlags
)
330 LPWSTR lpszUrlCpy
, wk1
, wk2
, mp
, root
;
334 TRACE("(%s %p %p 0x%08lx)\n", debugstr_w(pszUrl
), pszCanonicalized
,
335 pcchCanonicalized
, dwFlags
);
337 if(!pszUrl
|| !pszCanonicalized
|| !pcchCanonicalized
)
340 nByteLen
= (lstrlenW(pszUrl
) + 1) * sizeof(WCHAR
); /* length in bytes */
341 lpszUrlCpy
= HeapAlloc(GetProcessHeap(), 0, nByteLen
);
343 if (dwFlags
& URL_DONT_SIMPLIFY
)
344 memcpy(lpszUrlCpy
, pszUrl
, nByteLen
);
350 * 1 have 2[+] alnum 2,3
351 * 2 have scheme (found :) 4,6,3
352 * 3 failed (no location)
354 * 5 have 1[+] alnum 6,3
355 * 6 have location (found /) save root location
358 wk1
= (LPWSTR
)pszUrl
;
364 if (!isalnumW(*wk1
)) {state
= 3; break;}
366 if (!isalnumW(*wk1
)) {state
= 3; break;}
372 if (*wk1
++ == L
':') state
= 2;
375 if (*wk1
!= L
'/') {state
= 3; break;}
377 if (*wk1
!= L
'/') {state
= 6; break;}
387 if (!isalnumW(*wk1
) && (*wk1
!= L
'-') && (*wk1
!= L
'.')) {state
= 3; break;}
388 while(isalnumW(*wk1
) || (*wk1
== L
'-') || (*wk1
== L
'.')) *wk2
++ = *wk1
++;
392 if (*wk1
!= L
'/') {state
= 3; break;}
397 /* Now at root location, cannot back up any more. */
398 /* "root" will point at the '/' */
401 TRACE("wk1=%c\n", (CHAR
)*wk1
);
402 mp
= strchrW(wk1
, L
'/');
410 strncpyW(wk2
, wk1
, nLen
);
414 TRACE("found '/.'\n");
415 if (*(wk1
+1) == L
'/') {
416 /* case of /./ -> skip the ./ */
419 else if (*(wk1
+1) == L
'.') {
420 /* found /.. look for next / */
421 TRACE("found '/..'\n");
422 if (*(wk1
+2) == L
'/' || *(wk1
+2) == L
'?' || *(wk1
+2) == L
'#' || *(wk1
+2) == 0) {
423 /* case /../ -> need to backup wk2 */
424 TRACE("found '/../'\n");
425 *(wk2
-1) = L
'\0'; /* set end of string */
426 mp
= strrchrW(root
, L
'/');
427 if (mp
&& (mp
>= root
)) {
428 /* found valid backup point */
436 /* did not find point, restore '/' */
446 FIXME("how did we get here - state=%d\n", state
);
447 HeapFree(GetProcessHeap(), 0, lpszUrlCpy
);
452 TRACE("Simplified, orig <%s>, simple <%s>\n",
453 debugstr_w(pszUrl
), debugstr_w(lpszUrlCpy
));
455 nLen
= lstrlenW(lpszUrlCpy
);
456 while ((nLen
> 0) && ((lpszUrlCpy
[nLen
-1] == '\r')||(lpszUrlCpy
[nLen
-1] == '\n')))
457 lpszUrlCpy
[--nLen
]=0;
459 if(dwFlags
& URL_UNESCAPE
)
460 UrlUnescapeW(lpszUrlCpy
, NULL
, NULL
, URL_UNESCAPE_INPLACE
);
462 if((EscapeFlags
= dwFlags
& (URL_ESCAPE_UNSAFE
|
463 URL_ESCAPE_SPACES_ONLY
|
465 URL_DONT_ESCAPE_EXTRA_INFO
|
466 URL_ESCAPE_SEGMENT_ONLY
))) {
467 EscapeFlags
&= ~URL_ESCAPE_UNSAFE
;
468 hr
= UrlEscapeW(lpszUrlCpy
, pszCanonicalized
, pcchCanonicalized
,
470 } else { /* No escaping needed, just copy the string */
471 nLen
= lstrlenW(lpszUrlCpy
);
472 if(nLen
< *pcchCanonicalized
)
473 memcpy(pszCanonicalized
, lpszUrlCpy
, (nLen
+ 1)*sizeof(WCHAR
));
478 *pcchCanonicalized
= nLen
;
481 HeapFree(GetProcessHeap(), 0, lpszUrlCpy
);
484 TRACE("result %s\n", debugstr_w(pszCanonicalized
));
489 /*************************************************************************
490 * UrlCombineA [SHLWAPI.@]
495 * pszBase [I] Base Url
496 * pszRelative [I] Url to combine with pszBase
497 * pszCombined [O] Destination for combined Url
498 * pcchCombined [O] Destination for length of pszCombined
499 * dwFlags [I] URL_ flags from "shlwapi.h"
502 * Success: S_OK. pszCombined contains the combined Url, pcchCombined
503 * contains its length.
504 * Failure: An HRESULT error code indicating the error.
506 HRESULT WINAPI
UrlCombineA(LPCSTR pszBase
, LPCSTR pszRelative
,
507 LPSTR pszCombined
, LPDWORD pcchCombined
,
510 LPWSTR base
, relative
, combined
;
511 DWORD ret
, len
, len2
;
513 TRACE("(base %s, Relative %s, Combine size %ld, flags %08lx) using W version\n",
514 debugstr_a(pszBase
),debugstr_a(pszRelative
),
515 pcchCombined
?*pcchCombined
:0,dwFlags
);
517 if(!pszBase
|| !pszRelative
|| !pcchCombined
)
520 base
= (LPWSTR
) HeapAlloc(GetProcessHeap(), 0,
521 (3*INTERNET_MAX_URL_LENGTH
) * sizeof(WCHAR
));
522 relative
= base
+ INTERNET_MAX_URL_LENGTH
;
523 combined
= relative
+ INTERNET_MAX_URL_LENGTH
;
525 MultiByteToWideChar(0, 0, pszBase
, -1, base
, INTERNET_MAX_URL_LENGTH
);
526 MultiByteToWideChar(0, 0, pszRelative
, -1, relative
, INTERNET_MAX_URL_LENGTH
);
529 ret
= UrlCombineW(base
, relative
, pszCombined
?combined
:NULL
, &len
, dwFlags
);
532 HeapFree(GetProcessHeap(), 0, base
);
536 len2
= WideCharToMultiByte(0, 0, combined
, len
, 0, 0, 0, 0);
537 if (len2
> *pcchCombined
) {
538 *pcchCombined
= len2
;
539 HeapFree(GetProcessHeap(), 0, base
);
542 WideCharToMultiByte(0, 0, combined
, len
+1, pszCombined
, (*pcchCombined
)+1,
544 *pcchCombined
= len2
;
545 HeapFree(GetProcessHeap(), 0, base
);
549 /*************************************************************************
550 * UrlCombineW [SHLWAPI.@]
554 HRESULT WINAPI
UrlCombineW(LPCWSTR pszBase
, LPCWSTR pszRelative
,
555 LPWSTR pszCombined
, LPDWORD pcchCombined
,
558 PARSEDURLW base
, relative
;
559 DWORD myflags
, sizeloc
= 0;
560 DWORD len
, res1
, res2
, process_case
= 0;
561 LPWSTR work
, preliminary
, mbase
, mrelative
;
562 static const WCHAR myfilestr
[] = {'f','i','l','e',':','/','/','/','\0'};
563 static const WCHAR single_slash
[] = {'/','\0'};
566 TRACE("(base %s, Relative %s, Combine size %ld, flags %08lx)\n",
567 debugstr_w(pszBase
),debugstr_w(pszRelative
),
568 pcchCombined
?*pcchCombined
:0,dwFlags
);
570 if(!pszBase
|| !pszRelative
|| !pcchCombined
)
573 base
.cbSize
= sizeof(base
);
574 relative
.cbSize
= sizeof(relative
);
576 /* Get space for duplicates of the input and the output */
577 preliminary
= HeapAlloc(GetProcessHeap(), 0, (3*INTERNET_MAX_URL_LENGTH
) *
579 mbase
= preliminary
+ INTERNET_MAX_URL_LENGTH
;
580 mrelative
= mbase
+ INTERNET_MAX_URL_LENGTH
;
581 *preliminary
= L
'\0';
583 /* Canonicalize the base input prior to looking for the scheme */
584 myflags
= dwFlags
& (URL_DONT_SIMPLIFY
| URL_UNESCAPE
);
585 len
= INTERNET_MAX_URL_LENGTH
;
586 ret
= UrlCanonicalizeW(pszBase
, mbase
, &len
, myflags
);
588 /* Canonicalize the relative input prior to looking for the scheme */
589 len
= INTERNET_MAX_URL_LENGTH
;
590 ret
= UrlCanonicalizeW(pszRelative
, mrelative
, &len
, myflags
);
592 /* See if the base has a scheme */
593 res1
= ParseURLW(mbase
, &base
);
595 /* if pszBase has no scheme, then return pszRelative */
596 TRACE("no scheme detected in Base\n");
601 /* get size of location field (if it exists) */
602 work
= (LPWSTR
)base
.pszSuffix
;
604 if (*work
++ == L
'/') {
605 if (*work
++ == L
'/') {
606 /* At this point have start of location and
607 * it ends at next '/' or end of string.
609 while(*work
&& (*work
!= L
'/')) work
++;
610 sizeloc
= (DWORD
)(work
- base
.pszSuffix
);
614 /* Change .sizep2 to not have the last leaf in it,
615 * Note: we need to start after the location (if it exists)
617 work
= strrchrW((base
.pszSuffix
+sizeloc
), L
'/');
619 len
= (DWORD
)(work
- base
.pszSuffix
+ 1);
620 base
.cchSuffix
= len
;
624 * .pszSuffix points to location (starting with '//')
625 * .cchSuffix length of location (above) and rest less the last
627 * sizeloc length of location (above) up to but not including
631 res2
= ParseURLW(mrelative
, &relative
);
633 /* no scheme in pszRelative */
634 TRACE("no scheme detected in Relative\n");
635 relative
.pszSuffix
= mrelative
; /* case 3,4,5 depends on this */
636 relative
.cchSuffix
= strlenW(mrelative
);
637 if (*pszRelative
== L
':') {
638 /* case that is either left alone or uses pszBase */
639 if (dwFlags
& URL_PLUGGABLE_PROTOCOL
) {
646 if (isalnum(*mrelative
) && (*(mrelative
+ 1) == L
':')) {
647 /* case that becomes "file:///" */
648 strcpyW(preliminary
, myfilestr
);
652 if ((*mrelative
== L
'/') && (*(mrelative
+1) == L
'/')) {
653 /* pszRelative has location and rest */
657 if (*mrelative
== L
'/') {
658 /* case where pszRelative is root to location */
662 process_case
= (*base
.pszSuffix
== L
'/') ? 5 : 3;
666 /* handle cases where pszRelative has scheme */
667 if ((base
.cchProtocol
== relative
.cchProtocol
) &&
668 (strncmpW(base
.pszProtocol
, relative
.pszProtocol
, base
.cchProtocol
) == 0)) {
670 /* since the schemes are the same */
671 if ((*relative
.pszSuffix
== L
'/') && (*(relative
.pszSuffix
+1) == L
'/')) {
672 /* case where pszRelative replaces location and following */
676 if (*relative
.pszSuffix
== L
'/') {
677 /* case where pszRelative is root to location */
681 /* case where scheme is followed by document path */
685 if ((*relative
.pszSuffix
== L
'/') && (*(relative
.pszSuffix
+1) == L
'/')) {
686 /* case where pszRelative replaces scheme, location,
687 * and following and handles PLUGGABLE
694 } while(FALSE
); /* a litte trick to allow easy exit from nested if's */
698 switch (process_case
) {
701 * Return pszRelative appended to what ever is in pszCombined,
702 * (which may the string "file:///"
704 strcatW(preliminary
, mrelative
);
708 * Same as case 1, but if URL_PLUGGABLE_PROTOCOL was specified
709 * and pszRelative starts with "//", then append a "/"
711 strcpyW(preliminary
, mrelative
);
712 if (!(dwFlags
& URL_PLUGGABLE_PROTOCOL
) &&
713 URL_JustLocation(relative
.pszSuffix
))
714 strcatW(preliminary
, single_slash
);
718 * Return the pszBase scheme with pszRelative. Basically
719 * keeps the scheme and replaces the domain and following.
721 strncpyW(preliminary
, base
.pszProtocol
, base
.cchProtocol
+ 1);
722 work
= preliminary
+ base
.cchProtocol
+ 1;
723 strcpyW(work
, relative
.pszSuffix
);
724 if (!(dwFlags
& URL_PLUGGABLE_PROTOCOL
) &&
725 URL_JustLocation(relative
.pszSuffix
))
726 strcatW(work
, single_slash
);
730 * Return the pszBase scheme and location but everything
731 * after the location is pszRelative. (Replace document
734 strncpyW(preliminary
, base
.pszProtocol
, base
.cchProtocol
+1+sizeloc
);
735 work
= preliminary
+ base
.cchProtocol
+ 1 + sizeloc
;
736 if (dwFlags
& URL_PLUGGABLE_PROTOCOL
)
738 strcpyW(work
, relative
.pszSuffix
);
742 * Return the pszBase without its document (if any) and
743 * append pszRelative after its scheme.
745 strncpyW(preliminary
, base
.pszProtocol
, base
.cchProtocol
+1+base
.cchSuffix
);
746 work
= preliminary
+ base
.cchProtocol
+1+base
.cchSuffix
- 1;
749 strcpyW(work
, relative
.pszSuffix
);
753 FIXME("How did we get here????? process_case=%ld\n", process_case
);
758 /* Reuse mrelative as temp storage as its already allocated and not needed anymore */
759 ret
= UrlCanonicalizeW(preliminary
, mrelative
, pcchCombined
, dwFlags
);
760 if(SUCCEEDED(ret
) && pszCombined
) {
761 lstrcpyW(pszCombined
, mrelative
);
763 TRACE("return-%ld len=%ld, %s\n",
764 process_case
, *pcchCombined
, debugstr_w(pszCombined
));
766 HeapFree(GetProcessHeap(), 0, preliminary
);
770 /*************************************************************************
771 * UrlEscapeA [SHLWAPI.@]
774 HRESULT WINAPI
UrlEscapeA(
780 WCHAR bufW
[INTERNET_MAX_URL_LENGTH
];
781 WCHAR
*escapedW
= bufW
;
784 DWORD lenW
= sizeof(bufW
)/sizeof(WCHAR
), lenA
;
786 if(!RtlCreateUnicodeStringFromAsciiz(&urlW
, pszUrl
))
788 if((ret
= UrlEscapeW(urlW
.Buffer
, escapedW
, &lenW
, dwFlags
)) == E_POINTER
) {
789 escapedW
= HeapAlloc(GetProcessHeap(), 0, lenW
* sizeof(WCHAR
));
790 ret
= UrlEscapeW(urlW
.Buffer
, escapedW
, &lenW
, dwFlags
);
793 RtlUnicodeToMultiByteSize(&lenA
, escapedW
, lenW
* sizeof(WCHAR
));
794 if(*pcchEscaped
> lenA
) {
795 RtlUnicodeToMultiByteN(pszEscaped
, *pcchEscaped
- 1, &lenA
, escapedW
, lenW
* sizeof(WCHAR
));
796 pszEscaped
[lenA
] = 0;
799 *pcchEscaped
= lenA
+ 1;
803 if(escapedW
!= bufW
) HeapFree(GetProcessHeap(), 0, escapedW
);
804 RtlFreeUnicodeString(&urlW
);
808 #define WINE_URL_BASH_AS_SLASH 0x01
809 #define WINE_URL_COLLAPSE_SLASHES 0x02
810 #define WINE_URL_ESCAPE_SLASH 0x04
811 #define WINE_URL_ESCAPE_HASH 0x08
812 #define WINE_URL_ESCAPE_QUESTION 0x10
813 #define WINE_URL_STOP_ON_HASH 0x20
814 #define WINE_URL_STOP_ON_QUESTION 0x40
816 static inline BOOL
URL_NeedEscapeW(WCHAR ch
, DWORD dwFlags
, DWORD int_flags
)
822 if(dwFlags
& URL_ESCAPE_SPACES_ONLY
) {
829 if ((dwFlags
& URL_ESCAPE_PERCENT
) && (ch
== '%'))
832 if (ch
<= 31 || ch
>= 127)
853 if (int_flags
& WINE_URL_ESCAPE_SLASH
) return TRUE
;
857 if (int_flags
& WINE_URL_ESCAPE_QUESTION
) return TRUE
;
861 if (int_flags
& WINE_URL_ESCAPE_HASH
) return TRUE
;
871 /*************************************************************************
872 * UrlEscapeW [SHLWAPI.@]
874 * Converts unsafe characters in a Url into escape sequences.
877 * pszUrl [I] Url to modify
878 * pszEscaped [O] Destination for modified Url
879 * pcchEscaped [I/O] Length of pszUrl, destination for length of pszEscaped
880 * dwFlags [I] URL_ flags from "shlwapi.h"
883 * Success: S_OK. pszEscaped contains the escaped Url, pcchEscaped
884 * contains its length.
885 * Failure: E_POINTER, if pszEscaped is not large enough. In this case
886 * pcchEscaped is set to the required length.
888 * Converts unsafe characters into their escape sequences.
891 * - By default this function stops converting at the first '?' or
893 * - If dwFlags contains URL_ESCAPE_SPACES_ONLY then only spaces are
894 * converted, but the conversion continues past a '?' or '#'.
895 * - Note that this function did not work well (or at all) in shlwapi version 4.
898 * Only the following flags are implemented:
899 *| URL_ESCAPE_SPACES_ONLY
900 *| URL_DONT_ESCAPE_EXTRA_INFO
901 *| URL_ESCAPE_SEGMENT_ONLY
902 *| URL_ESCAPE_PERCENT
904 HRESULT WINAPI
UrlEscapeW(
911 DWORD needed
= 0, ret
;
912 BOOL stop_escaping
= FALSE
;
913 WCHAR next
[5], *dst
= pszEscaped
;
915 PARSEDURLW parsed_url
;
918 static const WCHAR localhost
[] = {'l','o','c','a','l','h','o','s','t',0};
920 TRACE("(%s %p %p 0x%08lx)\n", debugstr_w(pszUrl
), pszEscaped
,
921 pcchEscaped
, dwFlags
);
923 if(!pszUrl
|| !pszEscaped
|| !pcchEscaped
)
926 if(dwFlags
& ~(URL_ESCAPE_SPACES_ONLY
|
927 URL_ESCAPE_SEGMENT_ONLY
|
928 URL_DONT_ESCAPE_EXTRA_INFO
|
930 FIXME("Unimplemented flags: %08lx\n", dwFlags
);
933 if (dwFlags
& URL_ESCAPE_SPACES_ONLY
)
934 /* if SPACES_ONLY specified, reset the other controls */
935 dwFlags
&= ~(URL_DONT_ESCAPE_EXTRA_INFO
|
937 URL_ESCAPE_SEGMENT_ONLY
);
940 /* if SPACES_ONLY *not* specified the assume DONT_ESCAPE_EXTRA_INFO */
941 dwFlags
|= URL_DONT_ESCAPE_EXTRA_INFO
;
945 if(dwFlags
& URL_ESCAPE_SEGMENT_ONLY
) {
946 int_flags
= WINE_URL_ESCAPE_QUESTION
| WINE_URL_ESCAPE_HASH
| WINE_URL_ESCAPE_SLASH
;
948 parsed_url
.cbSize
= sizeof(parsed_url
);
949 if(ParseURLW(pszUrl
, &parsed_url
) != S_OK
)
950 parsed_url
.nScheme
= URL_SCHEME_INVALID
;
952 TRACE("scheme = %d (%s)\n", parsed_url
.nScheme
, debugstr_wn(parsed_url
.pszProtocol
, parsed_url
.cchProtocol
));
954 if(dwFlags
& URL_DONT_ESCAPE_EXTRA_INFO
)
955 int_flags
= WINE_URL_STOP_ON_HASH
| WINE_URL_STOP_ON_QUESTION
;
957 switch(parsed_url
.nScheme
) {
958 case URL_SCHEME_FILE
:
959 int_flags
|= WINE_URL_BASH_AS_SLASH
| WINE_URL_COLLAPSE_SLASHES
| WINE_URL_ESCAPE_HASH
;
960 int_flags
&= ~WINE_URL_STOP_ON_HASH
;
963 case URL_SCHEME_HTTP
:
964 case URL_SCHEME_HTTPS
:
965 int_flags
|= WINE_URL_BASH_AS_SLASH
;
966 if(parsed_url
.pszSuffix
[0] != '/' && parsed_url
.pszSuffix
[0] != '\\')
967 int_flags
|= WINE_URL_ESCAPE_SLASH
;
970 case URL_SCHEME_MAILTO
:
971 int_flags
|= WINE_URL_ESCAPE_SLASH
| WINE_URL_ESCAPE_QUESTION
| WINE_URL_ESCAPE_HASH
;
972 int_flags
&= ~(WINE_URL_STOP_ON_QUESTION
| WINE_URL_STOP_ON_HASH
);
975 case URL_SCHEME_INVALID
:
980 if(parsed_url
.pszSuffix
[0] != '/')
981 int_flags
|= WINE_URL_ESCAPE_SLASH
;
986 for(src
= pszUrl
; *src
; ) {
990 if((int_flags
& WINE_URL_COLLAPSE_SLASHES
) && src
== pszUrl
+ parsed_url
.cchProtocol
+ 1) {
991 int localhost_len
= sizeof(localhost
)/sizeof(WCHAR
) - 1;
992 while(cur
== '/' || cur
== '\\') {
996 if(slashes
== 2 && !strncmpiW(src
, localhost
, localhost_len
)) { /* file://localhost/ -> file:/// */
997 if(*(src
+ localhost_len
) == '/' || *(src
+ localhost_len
) == '\\')
998 src
+= localhost_len
+ 1;
1005 next
[0] = next
[1] = next
[2] = '/';
1012 next
[0] = next
[1] = '/';
1019 if(cur
== '#' && (int_flags
& WINE_URL_STOP_ON_HASH
))
1020 stop_escaping
= TRUE
;
1022 if(cur
== '?' && (int_flags
& WINE_URL_STOP_ON_QUESTION
))
1023 stop_escaping
= TRUE
;
1025 if(cur
== '\\' && (int_flags
& WINE_URL_BASH_AS_SLASH
) && !stop_escaping
) cur
= '/';
1027 if(URL_NeedEscapeW(cur
, dwFlags
, int_flags
) && stop_escaping
== FALSE
) {
1029 next
[1] = hexDigits
[(cur
>> 4) & 0xf];
1030 next
[2] = hexDigits
[cur
& 0xf];
1039 if(needed
+ len
<= *pcchEscaped
) {
1040 memcpy(dst
, next
, len
*sizeof(WCHAR
));
1046 if(needed
< *pcchEscaped
) {
1050 needed
++; /* add one for the '\0' */
1053 *pcchEscaped
= needed
;
1058 /*************************************************************************
1059 * UrlUnescapeA [SHLWAPI.@]
1061 * Converts Url escape sequences back to ordinary characters.
1064 * pszUrl [I/O] Url to convert
1065 * pszUnescaped [O] Destination for converted Url
1066 * pcchUnescaped [I/O] Size of output string
1067 * dwFlags [I] URL_ESCAPE_ Flags from "shlwapi.h"
1070 * Success: S_OK. The converted value is in pszUnescaped, or in pszUrl if
1071 * dwFlags includes URL_ESCAPE_INPLACE.
1072 * Failure: E_POINTER if the converted Url is bigger than pcchUnescaped. In
1073 * this case pcchUnescaped is set to the size required.
1075 * If dwFlags includes URL_DONT_ESCAPE_EXTRA_INFO, the conversion stops at
1076 * the first occurrence of either a '?' or '#' character.
1078 HRESULT WINAPI
UrlUnescapeA(
1081 LPDWORD pcchUnescaped
,
1088 BOOL stop_unescaping
= FALSE
;
1090 TRACE("(%s, %p, %p, 0x%08lx)\n", debugstr_a(pszUrl
), pszUnescaped
,
1091 pcchUnescaped
, dwFlags
);
1093 if(!pszUrl
|| !pszUnescaped
|| !pcchUnescaped
)
1094 return E_INVALIDARG
;
1096 if(dwFlags
& URL_UNESCAPE_INPLACE
)
1101 for(src
= pszUrl
, needed
= 0; *src
; src
++, needed
++) {
1102 if(dwFlags
& URL_DONT_UNESCAPE_EXTRA_INFO
&&
1103 (*src
== '#' || *src
== '?')) {
1104 stop_unescaping
= TRUE
;
1106 } else if(*src
== '%' && isxdigit(*(src
+ 1)) && isxdigit(*(src
+ 2))
1107 && stop_unescaping
== FALSE
) {
1110 memcpy(buf
, src
+ 1, 2);
1112 ih
= strtol(buf
, NULL
, 16);
1114 src
+= 2; /* Advance to end of escape */
1118 if(dwFlags
& URL_UNESCAPE_INPLACE
|| needed
< *pcchUnescaped
)
1122 if(dwFlags
& URL_UNESCAPE_INPLACE
|| needed
< *pcchUnescaped
) {
1126 needed
++; /* add one for the '\0' */
1129 if(!(dwFlags
& URL_UNESCAPE_INPLACE
))
1130 *pcchUnescaped
= needed
;
1133 TRACE("result %s\n", (dwFlags
& URL_UNESCAPE_INPLACE
) ?
1134 debugstr_a(pszUrl
) : debugstr_a(pszUnescaped
));
1140 /*************************************************************************
1141 * UrlUnescapeW [SHLWAPI.@]
1145 HRESULT WINAPI
UrlUnescapeW(
1147 LPWSTR pszUnescaped
,
1148 LPDWORD pcchUnescaped
,
1155 BOOL stop_unescaping
= FALSE
;
1157 TRACE("(%s, %p, %p, 0x%08lx)\n", debugstr_w(pszUrl
), pszUnescaped
,
1158 pcchUnescaped
, dwFlags
);
1160 if(!pszUrl
|| !pszUnescaped
|| !pcchUnescaped
)
1161 return E_INVALIDARG
;
1163 if(dwFlags
& URL_UNESCAPE_INPLACE
)
1168 for(src
= pszUrl
, needed
= 0; *src
; src
++, needed
++) {
1169 if(dwFlags
& URL_DONT_UNESCAPE_EXTRA_INFO
&&
1170 (*src
== L
'#' || *src
== L
'?')) {
1171 stop_unescaping
= TRUE
;
1173 } else if(*src
== L
'%' && isxdigitW(*(src
+ 1)) && isxdigitW(*(src
+ 2))
1174 && stop_unescaping
== FALSE
) {
1176 WCHAR buf
[5] = {'0','x',0};
1177 memcpy(buf
+ 2, src
+ 1, 2*sizeof(WCHAR
));
1179 StrToIntExW(buf
, STIF_SUPPORT_HEX
, &ih
);
1181 src
+= 2; /* Advance to end of escape */
1185 if(dwFlags
& URL_UNESCAPE_INPLACE
|| needed
< *pcchUnescaped
)
1189 if(dwFlags
& URL_UNESCAPE_INPLACE
|| needed
< *pcchUnescaped
) {
1193 needed
++; /* add one for the '\0' */
1196 if(!(dwFlags
& URL_UNESCAPE_INPLACE
))
1197 *pcchUnescaped
= needed
;
1200 TRACE("result %s\n", (dwFlags
& URL_UNESCAPE_INPLACE
) ?
1201 debugstr_w(pszUrl
) : debugstr_w(pszUnescaped
));
1207 /*************************************************************************
1208 * UrlGetLocationA [SHLWAPI.@]
1210 * Get the location from a Url.
1213 * pszUrl [I] Url to get the location from
1216 * A pointer to the start of the location in pszUrl, or NULL if there is
1220 * - MSDN erroneously states that "The location is the segment of the Url
1221 * starting with a '?' or '#' character". Neither V4 nor V5 of shlwapi.dll
1222 * stop at '?' and always return a NULL in this case.
1223 * - MSDN also erroneously states that "If a file URL has a query string,
1224 * the returned string is the query string". In all tested cases, if the
1225 * Url starts with "fi" then a NULL is returned. V5 gives the following results:
1228 *| NULL file://aa/b/cd#hohoh
1229 *| #hohoh http://aa/b/cd#hohoh
1230 *| NULL fi://aa/b/cd#hohoh
1231 *| #hohoh ff://aa/b/cd#hohoh
1233 LPCSTR WINAPI
UrlGetLocationA(
1239 base
.cbSize
= sizeof(base
);
1240 res1
= ParseURLA(pszUrl
, &base
);
1241 if (res1
) return NULL
; /* invalid scheme */
1243 /* if scheme is file: then never return pointer */
1244 if (strncmp(base
.pszProtocol
, "file", min(4,base
.cchProtocol
)) == 0) return NULL
;
1246 /* Look for '#' and return its addr */
1247 return strchr(base
.pszSuffix
, '#');
1250 /*************************************************************************
1251 * UrlGetLocationW [SHLWAPI.@]
1253 * See UrlGetLocationA.
1255 LPCWSTR WINAPI
UrlGetLocationW(
1261 base
.cbSize
= sizeof(base
);
1262 res1
= ParseURLW(pszUrl
, &base
);
1263 if (res1
) return NULL
; /* invalid scheme */
1265 /* if scheme is file: then never return pointer */
1266 if (strncmpW(base
.pszProtocol
, fileW
, min(4,base
.cchProtocol
)) == 0) return NULL
;
1268 /* Look for '#' and return its addr */
1269 return strchrW(base
.pszSuffix
, L
'#');
1272 /*************************************************************************
1273 * UrlCompareA [SHLWAPI.@]
1278 * pszUrl1 [I] First Url to compare
1279 * pszUrl2 [I] Url to compare to pszUrl1
1280 * fIgnoreSlash [I] TRUE = compare only up to a final slash
1283 * less than zero, zero, or greater than zero indicating pszUrl2 is greater
1284 * than, equal to, or less than pszUrl1 respectively.
1286 INT WINAPI
UrlCompareA(
1291 INT ret
, len
, len1
, len2
;
1294 return strcmp(pszUrl1
, pszUrl2
);
1295 len1
= strlen(pszUrl1
);
1296 if (pszUrl1
[len1
-1] == '/') len1
--;
1297 len2
= strlen(pszUrl2
);
1298 if (pszUrl2
[len2
-1] == '/') len2
--;
1300 return strncmp(pszUrl1
, pszUrl2
, len1
);
1301 len
= min(len1
, len2
);
1302 ret
= strncmp(pszUrl1
, pszUrl2
, len
);
1303 if (ret
) return ret
;
1304 if (len1
> len2
) return 1;
1308 /*************************************************************************
1309 * UrlCompareW [SHLWAPI.@]
1313 INT WINAPI
UrlCompareW(
1319 size_t len
, len1
, len2
;
1322 return strcmpW(pszUrl1
, pszUrl2
);
1323 len1
= strlenW(pszUrl1
);
1324 if (pszUrl1
[len1
-1] == '/') len1
--;
1325 len2
= strlenW(pszUrl2
);
1326 if (pszUrl2
[len2
-1] == '/') len2
--;
1328 return strncmpW(pszUrl1
, pszUrl2
, len1
);
1329 len
= min(len1
, len2
);
1330 ret
= strncmpW(pszUrl1
, pszUrl2
, len
);
1331 if (ret
) return ret
;
1332 if (len1
> len2
) return 1;
1336 /*************************************************************************
1337 * HashData [SHLWAPI.@]
1339 * Hash an input block into a variable sized digest.
1342 * lpSrc [I] Input block
1343 * nSrcLen [I] Length of lpSrc
1344 * lpDest [I] Output for hash digest
1345 * nDestLen [I] Length of lpDest
1348 * Success: TRUE. lpDest is filled with the computed hash value.
1349 * Failure: FALSE, if any argument is invalid.
1351 HRESULT WINAPI
HashData(const unsigned char *lpSrc
, DWORD nSrcLen
,
1352 unsigned char *lpDest
, DWORD nDestLen
)
1354 INT srcCount
= nSrcLen
- 1, destCount
= nDestLen
- 1;
1356 if (IsBadReadPtr(lpSrc
, nSrcLen
) ||
1357 IsBadWritePtr(lpDest
, nDestLen
))
1358 return E_INVALIDARG
;
1360 while (destCount
>= 0)
1362 lpDest
[destCount
] = (destCount
& 0xff);
1366 while (srcCount
>= 0)
1368 destCount
= nDestLen
- 1;
1369 while (destCount
>= 0)
1371 lpDest
[destCount
] = HashDataLookup
[lpSrc
[srcCount
] ^ lpDest
[destCount
]];
1379 /*************************************************************************
1380 * UrlHashA [SHLWAPI.@]
1382 * Produce a Hash from a Url.
1385 * pszUrl [I] Url to hash
1386 * lpDest [O] Destinationh for hash
1387 * nDestLen [I] Length of lpDest
1390 * Success: S_OK. lpDest is filled with the computed hash value.
1391 * Failure: E_INVALIDARG, if any argument is invalid.
1393 HRESULT WINAPI
UrlHashA(LPCSTR pszUrl
, unsigned char *lpDest
, DWORD nDestLen
)
1395 if (IsBadStringPtrA(pszUrl
, -1) || IsBadWritePtr(lpDest
, nDestLen
))
1396 return E_INVALIDARG
;
1398 HashData((const BYTE
*)pszUrl
, (int)strlen(pszUrl
), lpDest
, nDestLen
);
1402 /*************************************************************************
1403 * UrlHashW [SHLWAPI.@]
1407 HRESULT WINAPI
UrlHashW(LPCWSTR pszUrl
, unsigned char *lpDest
, DWORD nDestLen
)
1409 char szUrl
[MAX_PATH
];
1411 TRACE("(%s,%p,%ld)\n",debugstr_w(pszUrl
), lpDest
, nDestLen
);
1413 if (IsBadStringPtrW(pszUrl
, -1) || IsBadWritePtr(lpDest
, nDestLen
))
1414 return E_INVALIDARG
;
1416 /* Win32 hashes the data as an ASCII string, presumably so that both A+W
1417 * return the same digests for the same URL.
1419 WideCharToMultiByte(0, 0, pszUrl
, -1, szUrl
, MAX_PATH
, 0, 0);
1420 HashData((const BYTE
*)szUrl
, (int)strlen(szUrl
), lpDest
, nDestLen
);
1424 /*************************************************************************
1425 * UrlApplySchemeA [SHLWAPI.@]
1427 * Apply a scheme to a Url.
1430 * pszIn [I] Url to apply scheme to
1431 * pszOut [O] Destination for modified Url
1432 * pcchOut [I/O] Length of pszOut/destination for length of pszOut
1433 * dwFlags [I] URL_ flags from "shlwapi.h"
1436 * Success: S_OK: pszOut contains the modified Url, pcchOut contains its length.
1437 * Failure: An HRESULT error code describing the error.
1439 HRESULT WINAPI
UrlApplySchemeA(LPCSTR pszIn
, LPSTR pszOut
, LPDWORD pcchOut
, DWORD dwFlags
)
1442 DWORD ret
, len
, len2
;
1444 TRACE("(in %s, out size %ld, flags %08lx) using W version\n",
1445 debugstr_a(pszIn
), *pcchOut
, dwFlags
);
1447 in
= HeapAlloc(GetProcessHeap(), 0,
1448 (2*INTERNET_MAX_URL_LENGTH
) * sizeof(WCHAR
));
1449 out
= in
+ INTERNET_MAX_URL_LENGTH
;
1451 MultiByteToWideChar(0, 0, pszIn
, -1, in
, INTERNET_MAX_URL_LENGTH
);
1452 len
= INTERNET_MAX_URL_LENGTH
;
1454 ret
= UrlApplySchemeW(in
, out
, &len
, dwFlags
);
1455 if ((ret
!= S_OK
) && (ret
!= S_FALSE
)) {
1456 HeapFree(GetProcessHeap(), 0, in
);
1460 len2
= WideCharToMultiByte(0, 0, out
, len
+1, 0, 0, 0, 0);
1461 if (len2
> *pcchOut
) {
1463 HeapFree(GetProcessHeap(), 0, in
);
1466 WideCharToMultiByte(0, 0, out
, len
+1, pszOut
, *pcchOut
, 0, 0);
1468 HeapFree(GetProcessHeap(), 0, in
);
1472 static HRESULT
URL_GuessScheme(LPCWSTR pszIn
, LPWSTR pszOut
, LPDWORD pcchOut
)
1477 DWORD value_len
, data_len
, dwType
, i
;
1478 WCHAR reg_path
[MAX_PATH
];
1479 WCHAR value
[MAX_PATH
], data
[MAX_PATH
];
1482 MultiByteToWideChar(0, 0,
1483 "Software\\Microsoft\\Windows\\CurrentVersion\\URL\\Prefixes",
1484 -1, reg_path
, MAX_PATH
);
1485 RegOpenKeyExW(HKEY_LOCAL_MACHINE
, reg_path
, 0, 1, &newkey
);
1487 while(value_len
= data_len
= MAX_PATH
,
1488 RegEnumValueW(newkey
, index
, value
, &value_len
,
1489 0, &dwType
, (LPVOID
)data
, &data_len
) == 0) {
1490 TRACE("guess %d %s is %s\n",
1491 index
, debugstr_w(value
), debugstr_w(data
));
1494 for(i
=0; i
<value_len
; i
++) {
1497 /* remember that TRUE is not-equal */
1498 j
= ChrCmpIW(Wxx
, Wyy
);
1501 if ((i
== value_len
) && !j
) {
1502 if (strlenW(data
) + strlenW(pszIn
) + 1 > *pcchOut
) {
1503 *pcchOut
= strlenW(data
) + strlenW(pszIn
) + 1;
1504 RegCloseKey(newkey
);
1507 strcpyW(pszOut
, data
);
1508 strcatW(pszOut
, pszIn
);
1509 *pcchOut
= strlenW(pszOut
);
1510 TRACE("matched and set to %s\n", debugstr_w(pszOut
));
1511 RegCloseKey(newkey
);
1516 RegCloseKey(newkey
);
1520 static HRESULT
URL_ApplyDefault(LPCWSTR pszIn
, LPWSTR pszOut
, LPDWORD pcchOut
)
1523 DWORD data_len
, dwType
;
1524 WCHAR reg_path
[MAX_PATH
];
1525 WCHAR value
[MAX_PATH
], data
[MAX_PATH
];
1527 /* get and prepend default */
1528 MultiByteToWideChar(0, 0,
1529 "Software\\Microsoft\\Windows\\CurrentVersion\\URL\\DefaultPrefix",
1530 -1, reg_path
, MAX_PATH
);
1531 RegOpenKeyExW(HKEY_LOCAL_MACHINE
, reg_path
, 0, 1, &newkey
);
1532 data_len
= MAX_PATH
;
1535 RegQueryValueExW(newkey
, value
, 0, &dwType
, (LPBYTE
)data
, &data_len
);
1536 RegCloseKey(newkey
);
1537 if (strlenW(data
) + strlenW(pszIn
) + 1 > *pcchOut
) {
1538 *pcchOut
= strlenW(data
) + strlenW(pszIn
) + 1;
1541 strcpyW(pszOut
, data
);
1542 strcatW(pszOut
, pszIn
);
1543 *pcchOut
= strlenW(pszOut
);
1544 TRACE("used default %s\n", debugstr_w(pszOut
));
1548 /*************************************************************************
1549 * UrlApplySchemeW [SHLWAPI.@]
1551 * See UrlApplySchemeA.
1553 HRESULT WINAPI
UrlApplySchemeW(LPCWSTR pszIn
, LPWSTR pszOut
, LPDWORD pcchOut
, DWORD dwFlags
)
1555 PARSEDURLW in_scheme
;
1559 TRACE("(in %s, out size %ld, flags %08lx)\n",
1560 debugstr_w(pszIn
), *pcchOut
, dwFlags
);
1562 if (dwFlags
& URL_APPLY_GUESSFILE
) {
1563 FIXME("(%s %p %p(%ld) 0x%08lx): stub URL_APPLY_GUESSFILE not implemented\n",
1564 debugstr_w(pszIn
), pszOut
, pcchOut
, *pcchOut
, dwFlags
);
1565 strcpyW(pszOut
, pszIn
);
1566 *pcchOut
= strlenW(pszOut
);
1570 in_scheme
.cbSize
= sizeof(in_scheme
);
1571 /* See if the base has a scheme */
1572 res1
= ParseURLW(pszIn
, &in_scheme
);
1574 /* no scheme in input, need to see if we need to guess */
1575 if (dwFlags
& URL_APPLY_GUESSSCHEME
) {
1576 if ((ret
= URL_GuessScheme(pszIn
, pszOut
, pcchOut
)) != -1)
1581 /* we have a scheme, see if valid (known scheme) */
1582 if (in_scheme
.nScheme
) {
1583 /* have valid scheme, so just copy and exit */
1584 if (strlenW(pszIn
) + 1 > *pcchOut
) {
1585 *pcchOut
= strlenW(pszIn
) + 1;
1588 strcpyW(pszOut
, pszIn
);
1589 *pcchOut
= strlenW(pszOut
);
1590 TRACE("valid scheme, returing copy\n");
1595 /* If we are here, then either invalid scheme,
1596 * or no scheme and can't/failed guess.
1598 if ( ( ((res1
== 0) && (dwFlags
& URL_APPLY_FORCEAPPLY
)) ||
1600 (dwFlags
& URL_APPLY_DEFAULT
)) {
1601 /* find and apply default scheme */
1602 return URL_ApplyDefault(pszIn
, pszOut
, pcchOut
);
1605 /* just copy and give proper return code */
1606 if (strlenW(pszIn
) + 1 > *pcchOut
) {
1607 *pcchOut
= strlenW(pszIn
) + 1;
1610 strcpyW(pszOut
, pszIn
);
1611 *pcchOut
= strlenW(pszOut
);
1612 TRACE("returning copy, left alone\n");
1616 /*************************************************************************
1617 * UrlIsA [SHLWAPI.@]
1619 * Determine if a Url is of a certain class.
1622 * pszUrl [I] Url to check
1623 * Urlis [I] URLIS_ constant from "shlwapi.h"
1626 * TRUE if pszUrl belongs to the class type in Urlis.
1629 BOOL WINAPI
UrlIsA(LPCSTR pszUrl
, URLIS Urlis
)
1635 TRACE("(%s %d)\n", debugstr_a(pszUrl
), Urlis
);
1640 base
.cbSize
= sizeof(base
);
1641 res1
= ParseURLA(pszUrl
, &base
);
1642 if (res1
) return FALSE
; /* invalid scheme */
1643 switch (base
.nScheme
)
1645 case URL_SCHEME_MAILTO
:
1646 case URL_SCHEME_SHELL
:
1647 case URL_SCHEME_JAVASCRIPT
:
1648 case URL_SCHEME_VBSCRIPT
:
1649 case URL_SCHEME_ABOUT
:
1655 return !StrCmpNA("file:", pszUrl
, 5);
1657 case URLIS_DIRECTORY
:
1658 last
= pszUrl
+ strlen(pszUrl
) - 1;
1659 return (last
>= pszUrl
&& (*last
== '/' || *last
== '\\' ));
1662 return PathIsURLA(pszUrl
);
1664 case URLIS_NOHISTORY
:
1665 case URLIS_APPLIABLE
:
1666 case URLIS_HASQUERY
:
1668 FIXME("(%s %d): stub\n", debugstr_a(pszUrl
), Urlis
);
1673 /*************************************************************************
1674 * UrlIsW [SHLWAPI.@]
1678 BOOL WINAPI
UrlIsW(LPCWSTR pszUrl
, URLIS Urlis
)
1680 static const WCHAR stemp
[] = { 'f','i','l','e',':',0 };
1685 TRACE("(%s %d)\n", debugstr_w(pszUrl
), Urlis
);
1690 base
.cbSize
= sizeof(base
);
1691 res1
= ParseURLW(pszUrl
, &base
);
1692 if (res1
) return FALSE
; /* invalid scheme */
1693 switch (base
.nScheme
)
1695 case URL_SCHEME_MAILTO
:
1696 case URL_SCHEME_SHELL
:
1697 case URL_SCHEME_JAVASCRIPT
:
1698 case URL_SCHEME_VBSCRIPT
:
1699 case URL_SCHEME_ABOUT
:
1705 return !strncmpW(stemp
, pszUrl
, 5);
1707 case URLIS_DIRECTORY
:
1708 last
= pszUrl
+ strlenW(pszUrl
) - 1;
1709 return (last
>= pszUrl
&& (*last
== '/' || *last
== '\\'));
1712 return PathIsURLW(pszUrl
);
1714 case URLIS_NOHISTORY
:
1715 case URLIS_APPLIABLE
:
1716 case URLIS_HASQUERY
:
1718 FIXME("(%s %d): stub\n", debugstr_w(pszUrl
), Urlis
);
1723 /*************************************************************************
1724 * UrlIsNoHistoryA [SHLWAPI.@]
1726 * Determine if a Url should not be stored in the users history list.
1729 * pszUrl [I] Url to check
1732 * TRUE, if pszUrl should be excluded from the history list,
1735 BOOL WINAPI
UrlIsNoHistoryA(LPCSTR pszUrl
)
1737 return UrlIsA(pszUrl
, URLIS_NOHISTORY
);
1740 /*************************************************************************
1741 * UrlIsNoHistoryW [SHLWAPI.@]
1743 * See UrlIsNoHistoryA.
1745 BOOL WINAPI
UrlIsNoHistoryW(LPCWSTR pszUrl
)
1747 return UrlIsW(pszUrl
, URLIS_NOHISTORY
);
1750 /*************************************************************************
1751 * UrlIsOpaqueA [SHLWAPI.@]
1753 * Determine if a Url is opaque.
1756 * pszUrl [I] Url to check
1759 * TRUE if pszUrl is opaque,
1763 * An opaque Url is one that does not start with "<protocol>://".
1765 BOOL WINAPI
UrlIsOpaqueA(LPCSTR pszUrl
)
1767 return UrlIsA(pszUrl
, URLIS_OPAQUE
);
1770 /*************************************************************************
1771 * UrlIsOpaqueW [SHLWAPI.@]
1775 BOOL WINAPI
UrlIsOpaqueW(LPCWSTR pszUrl
)
1777 return UrlIsW(pszUrl
, URLIS_OPAQUE
);
1780 /*************************************************************************
1781 * Scans for characters of type "type" and when not matching found,
1782 * returns pointer to it and length in size.
1784 * Characters tested based on RFC 1738
1786 static LPCWSTR
URL_ScanID(LPCWSTR start
, LPDWORD size
, WINE_URL_SCAN_TYPE type
)
1788 static DWORD alwayszero
= 0;
1797 if ( (islowerW(*start
) && isalphaW(*start
)) ||
1812 if ( isalphaW(*start
) ||
1814 /* user/password only characters */
1819 /* *extra* characters */
1822 (*start
== L
'\'') ||
1826 /* *safe* characters */
1834 } else if (*start
== L
'%') {
1835 if (isxdigitW(*(start
+1)) &&
1836 isxdigitW(*(start
+2))) {
1848 if (isdigitW(*start
)) {
1859 if (isalnumW(*start
) ||
1861 (*start
== L
'.') ) {
1870 FIXME("unknown type %d\n", type
);
1871 return (LPWSTR
)&alwayszero
;
1873 /* TRACE("scanned %ld characters next char %p<%c>\n",
1874 *size, start, *start); */
1878 /*************************************************************************
1879 * Attempt to parse URL into pieces.
1881 static LONG
URL_ParseUrl(LPCWSTR pszUrl
, WINE_PARSE_URL
*pl
)
1885 memset(pl
, 0, sizeof(WINE_PARSE_URL
));
1886 pl
->pScheme
= pszUrl
;
1887 work
= URL_ScanID(pl
->pScheme
, &pl
->szScheme
, SCHEME
);
1888 if (!*work
|| (*work
!= L
':')) goto ErrorExit
;
1890 if ((*work
!= L
'/') || (*(work
+1) != L
'/')) goto ErrorExit
;
1891 pl
->pUserName
= work
+ 2;
1892 work
= URL_ScanID(pl
->pUserName
, &pl
->szUserName
, USERPASS
);
1893 if (*work
== L
':' ) {
1894 /* parse password */
1896 pl
->pPassword
= work
;
1897 work
= URL_ScanID(pl
->pPassword
, &pl
->szPassword
, USERPASS
);
1898 if (*work
!= L
'@') {
1899 /* what we just parsed must be the hostname and port
1900 * so reset pointers and clear then let it parse */
1901 pl
->szUserName
= pl
->szPassword
= 0;
1902 work
= pl
->pUserName
- 1;
1903 pl
->pUserName
= pl
->pPassword
= 0;
1905 } else if (*work
== L
'@') {
1909 } else if (!*work
|| (*work
== L
'/') || (*work
== L
'.')) {
1910 /* what was parsed was hostname, so reset pointers and let it parse */
1911 pl
->szUserName
= pl
->szPassword
= 0;
1912 work
= pl
->pUserName
- 1;
1913 pl
->pUserName
= pl
->pPassword
= 0;
1914 } else goto ErrorExit
;
1916 /* now start parsing hostname or hostnumber */
1918 pl
->pHostName
= work
;
1919 work
= URL_ScanID(pl
->pHostName
, &pl
->szHostName
, HOST
);
1920 if (*work
== L
':') {
1924 work
= URL_ScanID(pl
->pPort
, &pl
->szPort
, PORT
);
1926 if (*work
== L
'/') {
1927 /* see if query string */
1928 pl
->pQuery
= strchrW(work
, L
'?');
1929 if (pl
->pQuery
) pl
->szQuery
= strlenW(pl
->pQuery
);
1931 TRACE("parse successful: scheme=%p(%ld), user=%p(%ld), pass=%p(%ld), host=%p(%ld), port=%p(%ld), query=%p(%ld)\n",
1932 pl
->pScheme
, pl
->szScheme
,
1933 pl
->pUserName
, pl
->szUserName
,
1934 pl
->pPassword
, pl
->szPassword
,
1935 pl
->pHostName
, pl
->szHostName
,
1936 pl
->pPort
, pl
->szPort
,
1937 pl
->pQuery
, pl
->szQuery
);
1940 FIXME("failed to parse %s\n", debugstr_w(pszUrl
));
1941 return E_INVALIDARG
;
1944 /*************************************************************************
1945 * UrlGetPartA [SHLWAPI.@]
1947 * Retrieve part of a Url.
1950 * pszIn [I] Url to parse
1951 * pszOut [O] Destination for part of pszIn requested
1952 * pcchOut [I/O] Length of pszOut/destination for length of pszOut
1953 * dwPart [I] URL_PART_ enum from "shlwapi.h"
1954 * dwFlags [I] URL_ flags from "shlwapi.h"
1957 * Success: S_OK. pszOut contains the part requested, pcchOut contains its length.
1958 * Failure: An HRESULT error code describing the error.
1960 HRESULT WINAPI
UrlGetPartA(LPCSTR pszIn
, LPSTR pszOut
, LPDWORD pcchOut
,
1961 DWORD dwPart
, DWORD dwFlags
)
1964 DWORD ret
, len
, len2
;
1966 in
= HeapAlloc(GetProcessHeap(), 0,
1967 (2*INTERNET_MAX_URL_LENGTH
) * sizeof(WCHAR
));
1968 out
= in
+ INTERNET_MAX_URL_LENGTH
;
1970 MultiByteToWideChar(0, 0, pszIn
, -1, in
, INTERNET_MAX_URL_LENGTH
);
1972 len
= INTERNET_MAX_URL_LENGTH
;
1973 ret
= UrlGetPartW(in
, out
, &len
, dwPart
, dwFlags
);
1976 HeapFree(GetProcessHeap(), 0, in
);
1980 len2
= WideCharToMultiByte(0, 0, out
, len
, 0, 0, 0, 0);
1981 if (len2
> *pcchOut
) {
1983 HeapFree(GetProcessHeap(), 0, in
);
1986 WideCharToMultiByte(0, 0, out
, len
+1, pszOut
, *pcchOut
, 0, 0);
1988 HeapFree(GetProcessHeap(), 0, in
);
1992 /*************************************************************************
1993 * UrlGetPartW [SHLWAPI.@]
1997 HRESULT WINAPI
UrlGetPartW(LPCWSTR pszIn
, LPWSTR pszOut
, LPDWORD pcchOut
,
1998 DWORD dwPart
, DWORD dwFlags
)
2002 DWORD size
, schsize
;
2003 LPCWSTR addr
, schaddr
;
2006 TRACE("(%s %p %p(%ld) %08lx %08lx)\n",
2007 debugstr_w(pszIn
), pszOut
, pcchOut
, *pcchOut
, dwPart
, dwFlags
);
2009 ret
= URL_ParseUrl(pszIn
, &pl
);
2011 schaddr
= pl
.pScheme
;
2012 schsize
= pl
.szScheme
;
2015 case URL_PART_SCHEME
:
2016 if (!pl
.szScheme
) return E_INVALIDARG
;
2021 case URL_PART_HOSTNAME
:
2022 if (!pl
.szHostName
) return E_INVALIDARG
;
2023 addr
= pl
.pHostName
;
2024 size
= pl
.szHostName
;
2027 case URL_PART_USERNAME
:
2028 if (!pl
.szUserName
) return E_INVALIDARG
;
2029 addr
= pl
.pUserName
;
2030 size
= pl
.szUserName
;
2033 case URL_PART_PASSWORD
:
2034 if (!pl
.szPassword
) return E_INVALIDARG
;
2035 addr
= pl
.pPassword
;
2036 size
= pl
.szPassword
;
2040 if (!pl
.szPort
) return E_INVALIDARG
;
2045 case URL_PART_QUERY
:
2046 if (!pl
.szQuery
) return E_INVALIDARG
;
2052 return E_INVALIDARG
;
2055 if (dwFlags
== URL_PARTFLAG_KEEPSCHEME
) {
2056 if (*pcchOut
< size
+ schsize
+ 2) {
2057 *pcchOut
= size
+ schsize
+ 2;
2060 strncpyW(pszOut
, schaddr
, schsize
);
2061 work
= pszOut
+ schsize
;
2063 strncpyW(work
+1, addr
, size
);
2064 *pcchOut
= size
+ schsize
+ 1;
2069 if (*pcchOut
< size
+ 1) {*pcchOut
= size
+1; return E_POINTER
;}
2070 strncpyW(pszOut
, addr
, size
);
2072 work
= pszOut
+ size
;
2075 TRACE("len=%ld %s\n", *pcchOut
, debugstr_w(pszOut
));
2080 /*************************************************************************
2081 * PathIsURLA [SHLWAPI.@]
2083 * Check if the given path is a Url.
2086 * lpszPath [I] Path to check.
2089 * TRUE if lpszPath is a Url.
2090 * FALSE if lpszPath is NULL or not a Url.
2092 BOOL WINAPI
PathIsURLA(LPCSTR lpstrPath
)
2097 if (!lpstrPath
|| !*lpstrPath
) return FALSE
;
2100 base
.cbSize
= sizeof(base
);
2101 res1
= ParseURLA(lpstrPath
, &base
);
2102 return (base
.nScheme
!= URL_SCHEME_INVALID
);
2105 /*************************************************************************
2106 * PathIsURLW [SHLWAPI.@]
2110 BOOL WINAPI
PathIsURLW(LPCWSTR lpstrPath
)
2115 if (!lpstrPath
|| !*lpstrPath
) return FALSE
;
2118 base
.cbSize
= sizeof(base
);
2119 res1
= ParseURLW(lpstrPath
, &base
);
2120 return (base
.nScheme
!= URL_SCHEME_INVALID
);
2123 /*************************************************************************
2124 * UrlCreateFromPathA [SHLWAPI.@]
2126 * See UrlCreateFromPathW
2128 HRESULT WINAPI
UrlCreateFromPathA(LPCSTR pszPath
, LPSTR pszUrl
, LPDWORD pcchUrl
, DWORD dwReserved
)
2130 WCHAR bufW
[INTERNET_MAX_URL_LENGTH
];
2132 UNICODE_STRING pathW
;
2134 DWORD lenW
= sizeof(bufW
)/sizeof(WCHAR
), lenA
;
2136 if(!RtlCreateUnicodeStringFromAsciiz(&pathW
, pszPath
))
2137 return E_INVALIDARG
;
2138 if((ret
= UrlCreateFromPathW(pathW
.Buffer
, urlW
, &lenW
, dwReserved
)) == E_POINTER
) {
2139 urlW
= HeapAlloc(GetProcessHeap(), 0, lenW
* sizeof(WCHAR
));
2140 ret
= UrlCreateFromPathW(pathW
.Buffer
, urlW
, &lenW
, dwReserved
);
2142 if(ret
== S_OK
|| ret
== S_FALSE
) {
2143 RtlUnicodeToMultiByteSize(&lenA
, urlW
, lenW
* sizeof(WCHAR
));
2144 if(*pcchUrl
> lenA
) {
2145 RtlUnicodeToMultiByteN(pszUrl
, *pcchUrl
- 1, &lenA
, urlW
, lenW
* sizeof(WCHAR
));
2149 *pcchUrl
= lenA
+ 1;
2153 if(urlW
!= bufW
) HeapFree(GetProcessHeap(), 0, urlW
);
2154 RtlFreeUnicodeString(&pathW
);
2158 /*************************************************************************
2159 * UrlCreateFromPathW [SHLWAPI.@]
2161 * Create a Url from a file path.
2164 * pszPath [I] Path to convert
2165 * pszUrl [O] Destination for the converted Url
2166 * pcchUrl [I/O] Length of pszUrl
2167 * dwReserved [I] Reserved, must be 0
2170 * Success: S_OK pszUrl contains the converted path, S_FALSE if the path is already a Url
2171 * Failure: An HRESULT error code.
2173 HRESULT WINAPI
UrlCreateFromPathW(LPCWSTR pszPath
, LPWSTR pszUrl
, LPDWORD pcchUrl
, DWORD dwReserved
)
2178 WCHAR file_colonW
[] = {'f','i','l','e',':',0};
2179 WCHAR three_slashesW
[] = {'/','/','/',0};
2180 PARSEDURLW parsed_url
;
2182 TRACE("(%s, %p, %p, 0x%08lx)\n", debugstr_w(pszPath
), pszUrl
, pcchUrl
, dwReserved
);
2184 /* Validate arguments */
2185 if (dwReserved
!= 0)
2186 return E_INVALIDARG
;
2187 if (!pszUrl
|| !pcchUrl
)
2188 return E_INVALIDARG
;
2191 parsed_url
.cbSize
= sizeof(parsed_url
);
2192 if(ParseURLW(pszPath
, &parsed_url
) == S_OK
) {
2193 if(parsed_url
.nScheme
!= URL_SCHEME_INVALID
&& parsed_url
.cchProtocol
> 1) {
2194 needed
= strlenW(pszPath
);
2195 if (needed
>= *pcchUrl
) {
2196 *pcchUrl
= needed
+ 1;
2200 strcpyW(pszUrl
, pszPath
);
2206 pszNewUrl
= HeapAlloc(GetProcessHeap(), 0, (strlenW(pszPath
) + 9) * sizeof(WCHAR
)); /* "file:///" + pszPath_len + 1 */
2207 strcpyW(pszNewUrl
, file_colonW
);
2208 if(isalphaW(pszPath
[0]) && pszPath
[1] == ':')
2209 strcatW(pszNewUrl
, three_slashesW
);
2210 strcatW(pszNewUrl
, pszPath
);
2211 ret
= UrlEscapeW(pszNewUrl
, pszUrl
, pcchUrl
, URL_ESCAPE_PERCENT
);
2213 HeapFree(GetProcessHeap(), 0, pszNewUrl
);
2217 /*************************************************************************
2218 * SHAutoComplete [SHLWAPI.@]
2220 * Enable auto-completion for an edit control.
2223 * hwndEdit [I] Handle of control to enable auto-completion for
2224 * dwFlags [I] SHACF_ flags from "shlwapi.h"
2227 * Success: S_OK. Auto-completion is enabled for the control.
2228 * Failure: An HRESULT error code indicating the error.
2230 HRESULT WINAPI
SHAutoComplete(HWND hwndEdit
, DWORD dwFlags
)
2232 FIXME("SHAutoComplete stub\n");
2236 /*************************************************************************
2237 * MLBuildResURLA [SHLWAPI.405]
2239 * Create a Url pointing to a resource in a module.
2242 * lpszLibName [I] Name of the module containing the resource
2243 * hMod [I] Callers module handle
2244 * dwFlags [I] Undocumented flags for loading the module
2245 * lpszRes [I] Resource name
2246 * lpszDest [O] Destination for resulting Url
2247 * dwDestLen [I] Length of lpszDest
2250 * Success: S_OK. lpszDest constains the resource Url.
2251 * Failure: E_INVALIDARG, if any argument is invalid, or
2252 * E_FAIL if dwDestLen is too small.
2254 HRESULT WINAPI
MLBuildResURLA(LPCSTR lpszLibName
, HMODULE hMod
, DWORD dwFlags
,
2255 LPCSTR lpszRes
, LPSTR lpszDest
, DWORD dwDestLen
)
2257 WCHAR szLibName
[MAX_PATH
], szRes
[MAX_PATH
], szDest
[MAX_PATH
];
2261 MultiByteToWideChar(CP_ACP
, 0, lpszLibName
, -1, szLibName
, sizeof(szLibName
)/sizeof(WCHAR
));
2264 MultiByteToWideChar(CP_ACP
, 0, lpszRes
, -1, szRes
, sizeof(szRes
)/sizeof(WCHAR
));
2266 if (dwDestLen
> sizeof(szLibName
)/sizeof(WCHAR
))
2267 dwDestLen
= sizeof(szLibName
)/sizeof(WCHAR
);
2269 hRet
= MLBuildResURLW(lpszLibName
? szLibName
: NULL
, hMod
, dwFlags
,
2270 lpszRes
? szRes
: NULL
, lpszDest
? szDest
: NULL
, dwDestLen
);
2271 if (SUCCEEDED(hRet
) && lpszDest
)
2272 WideCharToMultiByte(CP_ACP
, 0, szDest
, -1, lpszDest
, dwDestLen
, 0, 0);
2277 /*************************************************************************
2278 * MLBuildResURLA [SHLWAPI.406]
2280 * See MLBuildResURLA.
2282 HRESULT WINAPI
MLBuildResURLW(LPCWSTR lpszLibName
, HMODULE hMod
, DWORD dwFlags
,
2283 LPCWSTR lpszRes
, LPWSTR lpszDest
, DWORD dwDestLen
)
2285 static const WCHAR szRes
[] = { 'r','e','s',':','/','/','\0' };
2286 #define szResLen ((sizeof(szRes) - sizeof(WCHAR))/sizeof(WCHAR))
2287 HRESULT hRet
= E_FAIL
;
2289 TRACE("(%s,%p,0x%08lx,%s,%p,%ld)\n", debugstr_w(lpszLibName
), hMod
, dwFlags
,
2290 debugstr_w(lpszRes
), lpszDest
, dwDestLen
);
2292 if (!lpszLibName
|| !hMod
|| hMod
== INVALID_HANDLE_VALUE
|| !lpszRes
||
2293 !lpszDest
|| (dwFlags
&& dwFlags
!= 2))
2294 return E_INVALIDARG
;
2296 if (dwDestLen
>= szResLen
+ 1)
2298 dwDestLen
-= (szResLen
+ 1);
2299 memcpy(lpszDest
, szRes
, sizeof(szRes
));
2301 hMod
= MLLoadLibraryW(lpszLibName
, hMod
, dwFlags
);
2305 WCHAR szBuff
[MAX_PATH
];
2308 len
= GetModuleFileNameW(hMod
, szBuff
, sizeof(szBuff
)/sizeof(WCHAR
));
2309 if (len
&& len
< sizeof(szBuff
)/sizeof(WCHAR
))
2311 DWORD dwPathLen
= strlenW(szBuff
) + 1;
2313 if (dwDestLen
>= dwPathLen
)
2317 dwDestLen
-= dwPathLen
;
2318 memcpy(lpszDest
+ szResLen
, szBuff
, dwPathLen
* sizeof(WCHAR
));
2320 dwResLen
= strlenW(lpszRes
) + 1;
2321 if (dwDestLen
>= dwResLen
+ 1)
2323 lpszDest
[szResLen
+ dwPathLen
+ dwResLen
] = '/';
2324 memcpy(lpszDest
+ szResLen
+ dwPathLen
, lpszRes
, dwResLen
* sizeof(WCHAR
));
2329 MLFreeLibrary(hMod
);