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"
31 #include "wine/unicode.h"
35 #define NO_SHLWAPI_STREAM
37 #include "wine/debug.h"
39 HMODULE WINAPI
MLLoadLibraryW(LPCWSTR
,HMODULE
,DWORD
);
40 BOOL WINAPI
MLFreeLibrary(HMODULE
);
41 HRESULT WINAPI
MLBuildResURLW(LPCWSTR
,HMODULE
,DWORD
,LPCWSTR
,LPWSTR
,DWORD
);
43 WINE_DEFAULT_DEBUG_CHANNEL(shell
);
45 /* The following schemes were identified in the native version of
46 * SHLWAPI.DLL version 5.50
49 URL_SCHEME scheme_number
;
53 static const SHL_2_inet_scheme shlwapi_schemes
[] = {
54 {URL_SCHEME_FTP
, "ftp"},
55 {URL_SCHEME_HTTP
, "http"},
56 {URL_SCHEME_GOPHER
, "gopher"},
57 {URL_SCHEME_MAILTO
, "mailto"},
58 {URL_SCHEME_NEWS
, "news"},
59 {URL_SCHEME_NNTP
, "nntp"},
60 {URL_SCHEME_TELNET
, "telnet"},
61 {URL_SCHEME_WAIS
, "wais"},
62 {URL_SCHEME_FILE
, "file"},
63 {URL_SCHEME_MK
, "mk"},
64 {URL_SCHEME_HTTPS
, "https"},
65 {URL_SCHEME_SHELL
, "shell"},
66 {URL_SCHEME_SNEWS
, "snews"},
67 {URL_SCHEME_LOCAL
, "local"},
68 {URL_SCHEME_JAVASCRIPT
, "javascript"},
69 {URL_SCHEME_VBSCRIPT
, "vbscript"},
70 {URL_SCHEME_ABOUT
, "about"},
71 {URL_SCHEME_RES
, "res"},
76 LPCWSTR pScheme
; /* [out] start of scheme */
77 DWORD szScheme
; /* [out] size of scheme (until colon) */
78 LPCWSTR pUserName
; /* [out] start of Username */
79 DWORD szUserName
; /* [out] size of Username (until ":" or "@") */
80 LPCWSTR pPassword
; /* [out] start of Password */
81 DWORD szPassword
; /* [out] size of Password (until "@") */
82 LPCWSTR pHostName
; /* [out] start of Hostname */
83 DWORD szHostName
; /* [out] size of Hostname (until ":" or "/") */
84 LPCWSTR pPort
; /* [out] start of Port */
85 DWORD szPort
; /* [out] size of Port (until "/" or eos) */
86 LPCWSTR pQuery
; /* [out] start of Query */
87 DWORD szQuery
; /* [out] size of Query (until eos) */
97 static const CHAR hexDigits
[] = "0123456789ABCDEF";
99 static const WCHAR fileW
[] = {'f','i','l','e','\0'};
101 static const unsigned char HashDataLookup
[256] = {
102 0x01, 0x0E, 0x6E, 0x19, 0x61, 0xAE, 0x84, 0x77, 0x8A, 0xAA, 0x7D, 0x76, 0x1B,
103 0xE9, 0x8C, 0x33, 0x57, 0xC5, 0xB1, 0x6B, 0xEA, 0xA9, 0x38, 0x44, 0x1E, 0x07,
104 0xAD, 0x49, 0xBC, 0x28, 0x24, 0x41, 0x31, 0xD5, 0x68, 0xBE, 0x39, 0xD3, 0x94,
105 0xDF, 0x30, 0x73, 0x0F, 0x02, 0x43, 0xBA, 0xD2, 0x1C, 0x0C, 0xB5, 0x67, 0x46,
106 0x16, 0x3A, 0x4B, 0x4E, 0xB7, 0xA7, 0xEE, 0x9D, 0x7C, 0x93, 0xAC, 0x90, 0xB0,
107 0xA1, 0x8D, 0x56, 0x3C, 0x42, 0x80, 0x53, 0x9C, 0xF1, 0x4F, 0x2E, 0xA8, 0xC6,
108 0x29, 0xFE, 0xB2, 0x55, 0xFD, 0xED, 0xFA, 0x9A, 0x85, 0x58, 0x23, 0xCE, 0x5F,
109 0x74, 0xFC, 0xC0, 0x36, 0xDD, 0x66, 0xDA, 0xFF, 0xF0, 0x52, 0x6A, 0x9E, 0xC9,
110 0x3D, 0x03, 0x59, 0x09, 0x2A, 0x9B, 0x9F, 0x5D, 0xA6, 0x50, 0x32, 0x22, 0xAF,
111 0xC3, 0x64, 0x63, 0x1A, 0x96, 0x10, 0x91, 0x04, 0x21, 0x08, 0xBD, 0x79, 0x40,
112 0x4D, 0x48, 0xD0, 0xF5, 0x82, 0x7A, 0x8F, 0x37, 0x69, 0x86, 0x1D, 0xA4, 0xB9,
113 0xC2, 0xC1, 0xEF, 0x65, 0xF2, 0x05, 0xAB, 0x7E, 0x0B, 0x4A, 0x3B, 0x89, 0xE4,
114 0x6C, 0xBF, 0xE8, 0x8B, 0x06, 0x18, 0x51, 0x14, 0x7F, 0x11, 0x5B, 0x5C, 0xFB,
115 0x97, 0xE1, 0xCF, 0x15, 0x62, 0x71, 0x70, 0x54, 0xE2, 0x12, 0xD6, 0xC7, 0xBB,
116 0x0D, 0x20, 0x5E, 0xDC, 0xE0, 0xD4, 0xF7, 0xCC, 0xC4, 0x2B, 0xF9, 0xEC, 0x2D,
117 0xF4, 0x6F, 0xB6, 0x99, 0x88, 0x81, 0x5A, 0xD9, 0xCA, 0x13, 0xA5, 0xE7, 0x47,
118 0xE6, 0x8E, 0x60, 0xE3, 0x3E, 0xB3, 0xF6, 0x72, 0xA2, 0x35, 0xA0, 0xD7, 0xCD,
119 0xB4, 0x2F, 0x6D, 0x2C, 0x26, 0x1F, 0x95, 0x87, 0x00, 0xD8, 0x34, 0x3F, 0x17,
120 0x25, 0x45, 0x27, 0x75, 0x92, 0xB8, 0xA3, 0xC8, 0xDE, 0xEB, 0xF8, 0xF3, 0xDB,
121 0x0A, 0x98, 0x83, 0x7B, 0xE5, 0xCB, 0x4C, 0x78, 0xD1 };
123 static BOOL
URL_JustLocation(LPCWSTR str
)
125 while(*str
&& (*str
== L
'/')) str
++;
127 while (*str
&& ((*str
== L
'-') ||
129 isalnumW(*str
))) str
++;
130 if (*str
== L
'/') return FALSE
;
136 /*************************************************************************
139 * Parse a Url into its constituent parts.
143 * y [O] Undocumented structure holding the parsed information
146 * Success: S_OK. y contains the parsed Url details.
147 * Failure: An HRESULT error code.
149 HRESULT WINAPI
ParseURLA(LPCSTR x
, PARSEDURLA
*y
)
152 const SHL_2_inet_scheme
*inet_pro
;
154 y
->nScheme
= URL_SCHEME_INVALID
;
155 if (y
->cbSize
!= sizeof(*y
)) return E_INVALIDARG
;
156 /* FIXME: leading white space generates error of 0x80041001 which
159 if (*x
<= ' ') return 0x80041001;
165 y
->cchProtocol
= cnt
;
174 /* check for no scheme in string start */
175 /* (apparently schemes *must* be larger than a single character) */
176 if ((*x
== '\0') || (y
->cchProtocol
<= 1)) {
177 y
->pszProtocol
= NULL
;
181 /* found scheme, set length of remainder */
182 y
->cchSuffix
= lstrlenA(y
->pszSuffix
);
184 /* see if known scheme and return indicator number */
185 y
->nScheme
= URL_SCHEME_UNKNOWN
;
186 inet_pro
= shlwapi_schemes
;
187 while (inet_pro
->scheme_name
) {
188 if (!strncasecmp(inet_pro
->scheme_name
, y
->pszProtocol
,
189 min(y
->cchProtocol
, lstrlenA(inet_pro
->scheme_name
)))) {
190 y
->nScheme
= inet_pro
->scheme_number
;
198 /*************************************************************************
201 * Unicode version of ParseURLA.
203 HRESULT WINAPI
ParseURLW(LPCWSTR x
, PARSEDURLW
*y
)
206 const SHL_2_inet_scheme
*inet_pro
;
210 y
->nScheme
= URL_SCHEME_INVALID
;
211 if (y
->cbSize
!= sizeof(*y
)) return E_INVALIDARG
;
212 /* FIXME: leading white space generates error of 0x80041001 which
215 if (*x
<= L
' ') return 0x80041001;
221 y
->cchProtocol
= cnt
;
230 /* check for no scheme in string start */
231 /* (apparently schemes *must* be larger than a single character) */
232 if ((*x
== L
'\0') || (y
->cchProtocol
<= 1)) {
233 y
->pszProtocol
= NULL
;
237 /* found scheme, set length of remainder */
238 y
->cchSuffix
= lstrlenW(y
->pszSuffix
);
240 /* see if known scheme and return indicator number */
241 len
= WideCharToMultiByte(0, 0, y
->pszProtocol
, y
->cchProtocol
, 0, 0, 0, 0);
242 cmpstr
= HeapAlloc(GetProcessHeap(), 0, len
);
243 WideCharToMultiByte(0, 0, y
->pszProtocol
, y
->cchProtocol
, cmpstr
, len
, 0, 0);
244 y
->nScheme
= URL_SCHEME_UNKNOWN
;
245 inet_pro
= shlwapi_schemes
;
246 while (inet_pro
->scheme_name
) {
247 if (!strncasecmp(inet_pro
->scheme_name
, cmpstr
,
248 min(len
, lstrlenA(inet_pro
->scheme_name
)))) {
249 y
->nScheme
= inet_pro
->scheme_number
;
254 HeapFree(GetProcessHeap(), 0, cmpstr
);
258 /*************************************************************************
259 * UrlCanonicalizeA [SHLWAPI.@]
261 * Canonicalize a Url.
264 * pszUrl [I] Url to cCanonicalize
265 * pszCanonicalized [O] Destination for converted Url.
266 * pcchCanonicalized [I/O] Length of pszUrl, destination for length of pszCanonicalized
267 * dwFlags [I] Flags controlling the conversion.
270 * Success: S_OK. The pszCanonicalized contains the converted Url.
271 * Failure: E_POINTER, if *pcchCanonicalized is too small.
273 * MSDN incorrectly describes the flags for this function. They should be:
274 *| URL_DONT_ESCAPE_EXTRA_INFO 0x02000000
275 *| URL_ESCAPE_SPACES_ONLY 0x04000000
276 *| URL_ESCAPE_PERCENT 0x00001000
277 *| URL_ESCAPE_UNSAFE 0x10000000
278 *| URL_UNESCAPE 0x10000000
279 *| URL_DONT_SIMPLIFY 0x08000000
280 *| URL_ESCAPE_SEGMENT_ONLY 0x00002000
282 HRESULT WINAPI
UrlCanonicalizeA(LPCSTR pszUrl
, LPSTR pszCanonicalized
,
283 LPDWORD pcchCanonicalized
, DWORD dwFlags
)
285 LPWSTR base
, canonical
;
286 DWORD ret
, len
, len2
;
288 TRACE("(%s %p %p 0x%08lx) using W version\n",
289 debugstr_a(pszUrl
), pszCanonicalized
,
290 pcchCanonicalized
, dwFlags
);
292 if(!pszUrl
|| !pszCanonicalized
|| !pcchCanonicalized
)
295 base
= HeapAlloc(GetProcessHeap(), 0,
296 (2*INTERNET_MAX_URL_LENGTH
) * sizeof(WCHAR
));
297 canonical
= base
+ INTERNET_MAX_URL_LENGTH
;
299 MultiByteToWideChar(0, 0, pszUrl
, -1, base
, INTERNET_MAX_URL_LENGTH
);
300 len
= INTERNET_MAX_URL_LENGTH
;
302 ret
= UrlCanonicalizeW(base
, canonical
, &len
, dwFlags
);
304 HeapFree(GetProcessHeap(), 0, base
);
308 len2
= WideCharToMultiByte(0, 0, canonical
, len
, 0, 0, 0, 0);
309 if (len2
> *pcchCanonicalized
) {
310 *pcchCanonicalized
= len
;
311 HeapFree(GetProcessHeap(), 0, base
);
314 WideCharToMultiByte(0, 0, canonical
, len
+1, pszCanonicalized
,
315 *pcchCanonicalized
, 0, 0);
316 *pcchCanonicalized
= len2
;
317 HeapFree(GetProcessHeap(), 0, base
);
321 /*************************************************************************
322 * UrlCanonicalizeW [SHLWAPI.@]
324 * See UrlCanonicalizeA.
326 HRESULT WINAPI
UrlCanonicalizeW(LPCWSTR pszUrl
, LPWSTR pszCanonicalized
,
327 LPDWORD pcchCanonicalized
, DWORD dwFlags
)
331 LPWSTR lpszUrlCpy
, wk1
, wk2
, mp
, mp2
, root
;
334 WCHAR slash
= dwFlags
& URL_FILE_USE_PATHURL
? '\\' : '/';
336 TRACE("(%s %p %p 0x%08lx)\n", debugstr_w(pszUrl
), pszCanonicalized
,
337 pcchCanonicalized
, dwFlags
);
339 if(!pszUrl
|| !pszCanonicalized
|| !pcchCanonicalized
)
342 nByteLen
= (lstrlenW(pszUrl
) + 1) * sizeof(WCHAR
); /* length in bytes */
343 lpszUrlCpy
= HeapAlloc(GetProcessHeap(), 0, nByteLen
);
345 if (dwFlags
& URL_DONT_SIMPLIFY
)
346 memcpy(lpszUrlCpy
, pszUrl
, nByteLen
);
352 * 1 have 2[+] alnum 2,3
353 * 2 have scheme (found :) 4,6,3
354 * 3 failed (no location)
356 * 5 have 1[+] alnum 6,3
357 * 6 have location (found /) save root location
360 wk1
= (LPWSTR
)pszUrl
;
366 if (!isalnumW(*wk1
)) {state
= 3; break;}
368 if (!isalnumW(*wk1
)) {state
= 3; break;}
374 if (*wk1
++ == L
':') state
= 2;
377 if (*wk1
!= L
'/') {state
= 3; break;}
379 if (*wk1
!= L
'/') {state
= 6; break;}
381 if((dwFlags
& URL_FILE_USE_PATHURL
) && *wk1
== '/')
386 nWkLen
= strlenW(wk1
);
387 memcpy(wk2
, wk1
, (nWkLen
+ 1) * sizeof(WCHAR
));
393 if(*mp
== '/' || *mp
== '\\')
399 if (!isalnumW(*wk1
) && (*wk1
!= L
'-') && (*wk1
!= L
'.') && (*wk1
!= ':'))
401 while(isalnumW(*wk1
) || (*wk1
== L
'-') || (*wk1
== L
'.') || (*wk1
== ':'))
406 if (*wk1
!= '/' && *wk1
!= '\\') {state
= 3; break;}
414 /* Now at root location, cannot back up any more. */
415 /* "root" will point at the '/' */
418 TRACE("wk1=%c\n", (CHAR
)*wk1
);
420 mp
= strchrW(wk1
, '/');
421 mp2
= strchrW(wk1
, '\\');
425 nWkLen
= strlenW(wk1
);
426 memcpy(wk2
, wk1
, (nWkLen
+ 1) * sizeof(WCHAR
));
433 memcpy(wk2
, wk1
, nLen
* sizeof(WCHAR
));
441 TRACE("found '/.'\n");
442 if (wk1
[1] == '/' || wk1
[1] == '\\') {
443 /* case of /./ -> skip the ./ */
446 else if (wk1
[1] == '.') {
447 /* found /.. look for next / */
448 TRACE("found '/..'\n");
449 if (wk1
[2] == '/' || wk1
[2] == '\\' ||wk1
[2] == '?'
450 || wk1
[2] == '#' || !wk1
[2]) {
451 /* case /../ -> need to backup wk2 */
452 TRACE("found '/../'\n");
453 *(wk2
-1) = L
'\0'; /* set end of string */
454 mp
= strrchrW(root
, slash
);
455 if (mp
&& (mp
>= root
)) {
456 /* found valid backup point */
458 if(wk1
[2] != '/' && wk1
[2] != '\\')
464 /* did not find point, restore '/' */
474 FIXME("how did we get here - state=%d\n", state
);
475 HeapFree(GetProcessHeap(), 0, lpszUrlCpy
);
480 TRACE("Simplified, orig <%s>, simple <%s>\n",
481 debugstr_w(pszUrl
), debugstr_w(lpszUrlCpy
));
483 nLen
= lstrlenW(lpszUrlCpy
);
484 while ((nLen
> 0) && ((lpszUrlCpy
[nLen
-1] == '\r')||(lpszUrlCpy
[nLen
-1] == '\n')))
485 lpszUrlCpy
[--nLen
]=0;
487 if(dwFlags
& (URL_UNESCAPE
| URL_FILE_USE_PATHURL
))
488 UrlUnescapeW(lpszUrlCpy
, NULL
, &nLen
, URL_UNESCAPE_INPLACE
);
490 if((EscapeFlags
= dwFlags
& (URL_ESCAPE_UNSAFE
|
491 URL_ESCAPE_SPACES_ONLY
|
493 URL_DONT_ESCAPE_EXTRA_INFO
|
494 URL_ESCAPE_SEGMENT_ONLY
))) {
495 EscapeFlags
&= ~URL_ESCAPE_UNSAFE
;
496 hr
= UrlEscapeW(lpszUrlCpy
, pszCanonicalized
, pcchCanonicalized
,
498 } else { /* No escaping needed, just copy the string */
499 nLen
= lstrlenW(lpszUrlCpy
);
500 if(nLen
< *pcchCanonicalized
)
501 memcpy(pszCanonicalized
, lpszUrlCpy
, (nLen
+ 1)*sizeof(WCHAR
));
506 *pcchCanonicalized
= nLen
;
509 HeapFree(GetProcessHeap(), 0, lpszUrlCpy
);
512 TRACE("result %s\n", debugstr_w(pszCanonicalized
));
517 /*************************************************************************
518 * UrlCombineA [SHLWAPI.@]
523 * pszBase [I] Base Url
524 * pszRelative [I] Url to combine with pszBase
525 * pszCombined [O] Destination for combined Url
526 * pcchCombined [O] Destination for length of pszCombined
527 * dwFlags [I] URL_ flags from "shlwapi.h"
530 * Success: S_OK. pszCombined contains the combined Url, pcchCombined
531 * contains its length.
532 * Failure: An HRESULT error code indicating the error.
534 HRESULT WINAPI
UrlCombineA(LPCSTR pszBase
, LPCSTR pszRelative
,
535 LPSTR pszCombined
, LPDWORD pcchCombined
,
538 LPWSTR base
, relative
, combined
;
539 DWORD ret
, len
, len2
;
541 TRACE("(base %s, Relative %s, Combine size %ld, flags %08lx) using W version\n",
542 debugstr_a(pszBase
),debugstr_a(pszRelative
),
543 pcchCombined
?*pcchCombined
:0,dwFlags
);
545 if(!pszBase
|| !pszRelative
|| !pcchCombined
)
548 base
= HeapAlloc(GetProcessHeap(), 0,
549 (3*INTERNET_MAX_URL_LENGTH
) * sizeof(WCHAR
));
550 relative
= base
+ INTERNET_MAX_URL_LENGTH
;
551 combined
= relative
+ INTERNET_MAX_URL_LENGTH
;
553 MultiByteToWideChar(0, 0, pszBase
, -1, base
, INTERNET_MAX_URL_LENGTH
);
554 MultiByteToWideChar(0, 0, pszRelative
, -1, relative
, INTERNET_MAX_URL_LENGTH
);
557 ret
= UrlCombineW(base
, relative
, pszCombined
?combined
:NULL
, &len
, dwFlags
);
560 HeapFree(GetProcessHeap(), 0, base
);
564 len2
= WideCharToMultiByte(0, 0, combined
, len
, 0, 0, 0, 0);
565 if (len2
> *pcchCombined
) {
566 *pcchCombined
= len2
;
567 HeapFree(GetProcessHeap(), 0, base
);
570 WideCharToMultiByte(0, 0, combined
, len
+1, pszCombined
, (*pcchCombined
)+1,
572 *pcchCombined
= len2
;
573 HeapFree(GetProcessHeap(), 0, base
);
577 /*************************************************************************
578 * UrlCombineW [SHLWAPI.@]
582 HRESULT WINAPI
UrlCombineW(LPCWSTR pszBase
, LPCWSTR pszRelative
,
583 LPWSTR pszCombined
, LPDWORD pcchCombined
,
586 PARSEDURLW base
, relative
;
587 DWORD myflags
, sizeloc
= 0;
588 DWORD len
, res1
, res2
, process_case
= 0;
589 LPWSTR work
, preliminary
, mbase
, mrelative
;
590 static const WCHAR myfilestr
[] = {'f','i','l','e',':','/','/','/','\0'};
591 static const WCHAR single_slash
[] = {'/','\0'};
594 TRACE("(base %s, Relative %s, Combine size %ld, flags %08lx)\n",
595 debugstr_w(pszBase
),debugstr_w(pszRelative
),
596 pcchCombined
?*pcchCombined
:0,dwFlags
);
598 if(!pszBase
|| !pszRelative
|| !pcchCombined
)
601 base
.cbSize
= sizeof(base
);
602 relative
.cbSize
= sizeof(relative
);
604 /* Get space for duplicates of the input and the output */
605 preliminary
= HeapAlloc(GetProcessHeap(), 0, (3*INTERNET_MAX_URL_LENGTH
) *
607 mbase
= preliminary
+ INTERNET_MAX_URL_LENGTH
;
608 mrelative
= mbase
+ INTERNET_MAX_URL_LENGTH
;
609 *preliminary
= L
'\0';
611 /* Canonicalize the base input prior to looking for the scheme */
612 myflags
= dwFlags
& (URL_DONT_SIMPLIFY
| URL_UNESCAPE
);
613 len
= INTERNET_MAX_URL_LENGTH
;
614 ret
= UrlCanonicalizeW(pszBase
, mbase
, &len
, myflags
);
616 /* Canonicalize the relative input prior to looking for the scheme */
617 len
= INTERNET_MAX_URL_LENGTH
;
618 ret
= UrlCanonicalizeW(pszRelative
, mrelative
, &len
, myflags
);
620 /* See if the base has a scheme */
621 res1
= ParseURLW(mbase
, &base
);
623 /* if pszBase has no scheme, then return pszRelative */
624 TRACE("no scheme detected in Base\n");
629 /* get size of location field (if it exists) */
630 work
= (LPWSTR
)base
.pszSuffix
;
632 if (*work
++ == L
'/') {
633 if (*work
++ == L
'/') {
634 /* At this point have start of location and
635 * it ends at next '/' or end of string.
637 while(*work
&& (*work
!= L
'/')) work
++;
638 sizeloc
= (DWORD
)(work
- base
.pszSuffix
);
642 /* Change .sizep2 to not have the last leaf in it,
643 * Note: we need to start after the location (if it exists)
645 work
= strrchrW((base
.pszSuffix
+sizeloc
), L
'/');
647 len
= (DWORD
)(work
- base
.pszSuffix
+ 1);
648 base
.cchSuffix
= len
;
652 * .pszSuffix points to location (starting with '//')
653 * .cchSuffix length of location (above) and rest less the last
655 * sizeloc length of location (above) up to but not including
659 res2
= ParseURLW(mrelative
, &relative
);
661 /* no scheme in pszRelative */
662 TRACE("no scheme detected in Relative\n");
663 relative
.pszSuffix
= mrelative
; /* case 3,4,5 depends on this */
664 relative
.cchSuffix
= strlenW(mrelative
);
665 if (*pszRelative
== L
':') {
666 /* case that is either left alone or uses pszBase */
667 if (dwFlags
& URL_PLUGGABLE_PROTOCOL
) {
674 if (isalnum(*mrelative
) && (*(mrelative
+ 1) == L
':')) {
675 /* case that becomes "file:///" */
676 strcpyW(preliminary
, myfilestr
);
680 if ((*mrelative
== L
'/') && (*(mrelative
+1) == L
'/')) {
681 /* pszRelative has location and rest */
685 if (*mrelative
== L
'/') {
686 /* case where pszRelative is root to location */
690 process_case
= (*base
.pszSuffix
== L
'/') ? 5 : 3;
694 /* handle cases where pszRelative has scheme */
695 if ((base
.cchProtocol
== relative
.cchProtocol
) &&
696 (strncmpW(base
.pszProtocol
, relative
.pszProtocol
, base
.cchProtocol
) == 0)) {
698 /* since the schemes are the same */
699 if ((*relative
.pszSuffix
== L
'/') && (*(relative
.pszSuffix
+1) == L
'/')) {
700 /* case where pszRelative replaces location and following */
704 if (*relative
.pszSuffix
== L
'/') {
705 /* case where pszRelative is root to location */
709 /* case where scheme is followed by document path */
713 if ((*relative
.pszSuffix
== L
'/') && (*(relative
.pszSuffix
+1) == L
'/')) {
714 /* case where pszRelative replaces scheme, location,
715 * and following and handles PLUGGABLE
722 } while(FALSE
); /* a litte trick to allow easy exit from nested if's */
726 switch (process_case
) {
729 * Return pszRelative appended to what ever is in pszCombined,
730 * (which may the string "file:///"
732 strcatW(preliminary
, mrelative
);
736 * Same as case 1, but if URL_PLUGGABLE_PROTOCOL was specified
737 * and pszRelative starts with "//", then append a "/"
739 strcpyW(preliminary
, mrelative
);
740 if (!(dwFlags
& URL_PLUGGABLE_PROTOCOL
) &&
741 URL_JustLocation(relative
.pszSuffix
))
742 strcatW(preliminary
, single_slash
);
746 * Return the pszBase scheme with pszRelative. Basically
747 * keeps the scheme and replaces the domain and following.
749 memcpy(preliminary
, base
.pszProtocol
, (base
.cchProtocol
+ 1)*sizeof(WCHAR
));
750 work
= preliminary
+ base
.cchProtocol
+ 1;
751 strcpyW(work
, relative
.pszSuffix
);
752 if (!(dwFlags
& URL_PLUGGABLE_PROTOCOL
) &&
753 URL_JustLocation(relative
.pszSuffix
))
754 strcatW(work
, single_slash
);
758 * Return the pszBase scheme and location but everything
759 * after the location is pszRelative. (Replace document
762 memcpy(preliminary
, base
.pszProtocol
, (base
.cchProtocol
+1+sizeloc
)*sizeof(WCHAR
));
763 work
= preliminary
+ base
.cchProtocol
+ 1 + sizeloc
;
764 if (dwFlags
& URL_PLUGGABLE_PROTOCOL
)
766 strcpyW(work
, relative
.pszSuffix
);
770 * Return the pszBase without its document (if any) and
771 * append pszRelative after its scheme.
773 memcpy(preliminary
, base
.pszProtocol
,
774 (base
.cchProtocol
+1+base
.cchSuffix
)*sizeof(WCHAR
));
775 work
= preliminary
+ base
.cchProtocol
+1+base
.cchSuffix
- 1;
778 strcpyW(work
, relative
.pszSuffix
);
782 FIXME("How did we get here????? process_case=%ld\n", process_case
);
787 /* Reuse mrelative as temp storage as its already allocated and not needed anymore */
788 ret
= UrlCanonicalizeW(preliminary
, mrelative
, pcchCombined
, dwFlags
);
789 if(SUCCEEDED(ret
) && pszCombined
) {
790 lstrcpyW(pszCombined
, mrelative
);
792 TRACE("return-%ld len=%ld, %s\n",
793 process_case
, *pcchCombined
, debugstr_w(pszCombined
));
795 HeapFree(GetProcessHeap(), 0, preliminary
);
799 /*************************************************************************
800 * UrlEscapeA [SHLWAPI.@]
803 HRESULT WINAPI
UrlEscapeA(
809 WCHAR bufW
[INTERNET_MAX_URL_LENGTH
];
810 WCHAR
*escapedW
= bufW
;
813 DWORD lenW
= sizeof(bufW
)/sizeof(WCHAR
), lenA
;
815 if(!RtlCreateUnicodeStringFromAsciiz(&urlW
, pszUrl
))
817 if((ret
= UrlEscapeW(urlW
.Buffer
, escapedW
, &lenW
, dwFlags
)) == E_POINTER
) {
818 escapedW
= HeapAlloc(GetProcessHeap(), 0, lenW
* sizeof(WCHAR
));
819 ret
= UrlEscapeW(urlW
.Buffer
, escapedW
, &lenW
, dwFlags
);
822 RtlUnicodeToMultiByteSize(&lenA
, escapedW
, lenW
* sizeof(WCHAR
));
823 if(*pcchEscaped
> lenA
) {
824 RtlUnicodeToMultiByteN(pszEscaped
, *pcchEscaped
- 1, &lenA
, escapedW
, lenW
* sizeof(WCHAR
));
825 pszEscaped
[lenA
] = 0;
828 *pcchEscaped
= lenA
+ 1;
832 if(escapedW
!= bufW
) HeapFree(GetProcessHeap(), 0, escapedW
);
833 RtlFreeUnicodeString(&urlW
);
837 #define WINE_URL_BASH_AS_SLASH 0x01
838 #define WINE_URL_COLLAPSE_SLASHES 0x02
839 #define WINE_URL_ESCAPE_SLASH 0x04
840 #define WINE_URL_ESCAPE_HASH 0x08
841 #define WINE_URL_ESCAPE_QUESTION 0x10
842 #define WINE_URL_STOP_ON_HASH 0x20
843 #define WINE_URL_STOP_ON_QUESTION 0x40
845 static inline BOOL
URL_NeedEscapeW(WCHAR ch
, DWORD dwFlags
, DWORD int_flags
)
851 if(dwFlags
& URL_ESCAPE_SPACES_ONLY
) {
858 if ((dwFlags
& URL_ESCAPE_PERCENT
) && (ch
== '%'))
861 if (ch
<= 31 || ch
>= 127)
882 if (int_flags
& WINE_URL_ESCAPE_SLASH
) return TRUE
;
886 if (int_flags
& WINE_URL_ESCAPE_QUESTION
) return TRUE
;
890 if (int_flags
& WINE_URL_ESCAPE_HASH
) return TRUE
;
900 /*************************************************************************
901 * UrlEscapeW [SHLWAPI.@]
903 * Converts unsafe characters in a Url into escape sequences.
906 * pszUrl [I] Url to modify
907 * pszEscaped [O] Destination for modified Url
908 * pcchEscaped [I/O] Length of pszUrl, destination for length of pszEscaped
909 * dwFlags [I] URL_ flags from "shlwapi.h"
912 * Success: S_OK. pszEscaped contains the escaped Url, pcchEscaped
913 * contains its length.
914 * Failure: E_POINTER, if pszEscaped is not large enough. In this case
915 * pcchEscaped is set to the required length.
917 * Converts unsafe characters into their escape sequences.
920 * - By default this function stops converting at the first '?' or
922 * - If dwFlags contains URL_ESCAPE_SPACES_ONLY then only spaces are
923 * converted, but the conversion continues past a '?' or '#'.
924 * - Note that this function did not work well (or at all) in shlwapi version 4.
927 * Only the following flags are implemented:
928 *| URL_ESCAPE_SPACES_ONLY
929 *| URL_DONT_ESCAPE_EXTRA_INFO
930 *| URL_ESCAPE_SEGMENT_ONLY
931 *| URL_ESCAPE_PERCENT
933 HRESULT WINAPI
UrlEscapeW(
940 DWORD needed
= 0, ret
;
941 BOOL stop_escaping
= FALSE
;
942 WCHAR next
[5], *dst
= pszEscaped
;
944 PARSEDURLW parsed_url
;
947 static const WCHAR localhost
[] = {'l','o','c','a','l','h','o','s','t',0};
949 TRACE("(%s %p %p 0x%08lx)\n", debugstr_w(pszUrl
), pszEscaped
,
950 pcchEscaped
, dwFlags
);
952 if(!pszUrl
|| !pszEscaped
|| !pcchEscaped
)
955 if(dwFlags
& ~(URL_ESCAPE_SPACES_ONLY
|
956 URL_ESCAPE_SEGMENT_ONLY
|
957 URL_DONT_ESCAPE_EXTRA_INFO
|
959 FIXME("Unimplemented flags: %08lx\n", dwFlags
);
962 if (dwFlags
& URL_ESCAPE_SPACES_ONLY
)
963 /* if SPACES_ONLY specified, reset the other controls */
964 dwFlags
&= ~(URL_DONT_ESCAPE_EXTRA_INFO
|
966 URL_ESCAPE_SEGMENT_ONLY
);
969 /* if SPACES_ONLY *not* specified the assume DONT_ESCAPE_EXTRA_INFO */
970 dwFlags
|= URL_DONT_ESCAPE_EXTRA_INFO
;
974 if(dwFlags
& URL_ESCAPE_SEGMENT_ONLY
) {
975 int_flags
= WINE_URL_ESCAPE_QUESTION
| WINE_URL_ESCAPE_HASH
| WINE_URL_ESCAPE_SLASH
;
977 parsed_url
.cbSize
= sizeof(parsed_url
);
978 if(ParseURLW(pszUrl
, &parsed_url
) != S_OK
)
979 parsed_url
.nScheme
= URL_SCHEME_INVALID
;
981 TRACE("scheme = %d (%s)\n", parsed_url
.nScheme
, debugstr_wn(parsed_url
.pszProtocol
, parsed_url
.cchProtocol
));
983 if(dwFlags
& URL_DONT_ESCAPE_EXTRA_INFO
)
984 int_flags
= WINE_URL_STOP_ON_HASH
| WINE_URL_STOP_ON_QUESTION
;
986 switch(parsed_url
.nScheme
) {
987 case URL_SCHEME_FILE
:
988 int_flags
|= WINE_URL_BASH_AS_SLASH
| WINE_URL_COLLAPSE_SLASHES
| WINE_URL_ESCAPE_HASH
;
989 int_flags
&= ~WINE_URL_STOP_ON_HASH
;
992 case URL_SCHEME_HTTP
:
993 case URL_SCHEME_HTTPS
:
994 int_flags
|= WINE_URL_BASH_AS_SLASH
;
995 if(parsed_url
.pszSuffix
[0] != '/' && parsed_url
.pszSuffix
[0] != '\\')
996 int_flags
|= WINE_URL_ESCAPE_SLASH
;
999 case URL_SCHEME_MAILTO
:
1000 int_flags
|= WINE_URL_ESCAPE_SLASH
| WINE_URL_ESCAPE_QUESTION
| WINE_URL_ESCAPE_HASH
;
1001 int_flags
&= ~(WINE_URL_STOP_ON_QUESTION
| WINE_URL_STOP_ON_HASH
);
1004 case URL_SCHEME_INVALID
:
1007 case URL_SCHEME_FTP
:
1009 if(parsed_url
.pszSuffix
[0] != '/')
1010 int_flags
|= WINE_URL_ESCAPE_SLASH
;
1015 for(src
= pszUrl
; *src
; ) {
1019 if((int_flags
& WINE_URL_COLLAPSE_SLASHES
) && src
== pszUrl
+ parsed_url
.cchProtocol
+ 1) {
1020 int localhost_len
= sizeof(localhost
)/sizeof(WCHAR
) - 1;
1021 while(cur
== '/' || cur
== '\\') {
1025 if(slashes
== 2 && !strncmpiW(src
, localhost
, localhost_len
)) { /* file://localhost/ -> file:/// */
1026 if(*(src
+ localhost_len
) == '/' || *(src
+ localhost_len
) == '\\')
1027 src
+= localhost_len
+ 1;
1034 next
[0] = next
[1] = next
[2] = '/';
1041 next
[0] = next
[1] = '/';
1048 if(cur
== '#' && (int_flags
& WINE_URL_STOP_ON_HASH
))
1049 stop_escaping
= TRUE
;
1051 if(cur
== '?' && (int_flags
& WINE_URL_STOP_ON_QUESTION
))
1052 stop_escaping
= TRUE
;
1054 if(cur
== '\\' && (int_flags
& WINE_URL_BASH_AS_SLASH
) && !stop_escaping
) cur
= '/';
1056 if(URL_NeedEscapeW(cur
, dwFlags
, int_flags
) && stop_escaping
== FALSE
) {
1058 next
[1] = hexDigits
[(cur
>> 4) & 0xf];
1059 next
[2] = hexDigits
[cur
& 0xf];
1068 if(needed
+ len
<= *pcchEscaped
) {
1069 memcpy(dst
, next
, len
*sizeof(WCHAR
));
1075 if(needed
< *pcchEscaped
) {
1079 needed
++; /* add one for the '\0' */
1082 *pcchEscaped
= needed
;
1087 /*************************************************************************
1088 * UrlUnescapeA [SHLWAPI.@]
1090 * Converts Url escape sequences back to ordinary characters.
1093 * pszUrl [I/O] Url to convert
1094 * pszUnescaped [O] Destination for converted Url
1095 * pcchUnescaped [I/O] Size of output string
1096 * dwFlags [I] URL_ESCAPE_ Flags from "shlwapi.h"
1099 * Success: S_OK. The converted value is in pszUnescaped, or in pszUrl if
1100 * dwFlags includes URL_ESCAPE_INPLACE.
1101 * Failure: E_POINTER if the converted Url is bigger than pcchUnescaped. In
1102 * this case pcchUnescaped is set to the size required.
1104 * If dwFlags includes URL_DONT_ESCAPE_EXTRA_INFO, the conversion stops at
1105 * the first occurrence of either a '?' or '#' character.
1107 HRESULT WINAPI
UrlUnescapeA(
1110 LPDWORD pcchUnescaped
,
1117 BOOL stop_unescaping
= FALSE
;
1119 TRACE("(%s, %p, %p, 0x%08lx)\n", debugstr_a(pszUrl
), pszUnescaped
,
1120 pcchUnescaped
, dwFlags
);
1122 if(!pszUrl
|| !pszUnescaped
|| !pcchUnescaped
)
1123 return E_INVALIDARG
;
1125 if(dwFlags
& URL_UNESCAPE_INPLACE
)
1130 for(src
= pszUrl
, needed
= 0; *src
; src
++, needed
++) {
1131 if(dwFlags
& URL_DONT_UNESCAPE_EXTRA_INFO
&&
1132 (*src
== '#' || *src
== '?')) {
1133 stop_unescaping
= TRUE
;
1135 } else if(*src
== '%' && isxdigit(*(src
+ 1)) && isxdigit(*(src
+ 2))
1136 && stop_unescaping
== FALSE
) {
1139 memcpy(buf
, src
+ 1, 2);
1141 ih
= strtol(buf
, NULL
, 16);
1143 src
+= 2; /* Advance to end of escape */
1147 if(dwFlags
& URL_UNESCAPE_INPLACE
|| needed
< *pcchUnescaped
)
1151 if(dwFlags
& URL_UNESCAPE_INPLACE
|| needed
< *pcchUnescaped
) {
1155 needed
++; /* add one for the '\0' */
1158 if(!(dwFlags
& URL_UNESCAPE_INPLACE
))
1159 *pcchUnescaped
= needed
;
1162 TRACE("result %s\n", (dwFlags
& URL_UNESCAPE_INPLACE
) ?
1163 debugstr_a(pszUrl
) : debugstr_a(pszUnescaped
));
1169 /*************************************************************************
1170 * UrlUnescapeW [SHLWAPI.@]
1174 HRESULT WINAPI
UrlUnescapeW(
1176 LPWSTR pszUnescaped
,
1177 LPDWORD pcchUnescaped
,
1184 BOOL stop_unescaping
= FALSE
;
1186 TRACE("(%s, %p, %p, 0x%08lx)\n", debugstr_w(pszUrl
), pszUnescaped
,
1187 pcchUnescaped
, dwFlags
);
1189 if(!pszUrl
|| (!pszUnescaped
&& !(dwFlags
& URL_UNESCAPE_INPLACE
))|| !pcchUnescaped
)
1190 return E_INVALIDARG
;
1192 if(dwFlags
& URL_UNESCAPE_INPLACE
)
1197 for(src
= pszUrl
, needed
= 0; *src
; src
++, needed
++) {
1198 if(dwFlags
& URL_DONT_UNESCAPE_EXTRA_INFO
&&
1199 (*src
== L
'#' || *src
== L
'?')) {
1200 stop_unescaping
= TRUE
;
1202 } else if(*src
== L
'%' && isxdigitW(*(src
+ 1)) && isxdigitW(*(src
+ 2))
1203 && stop_unescaping
== FALSE
) {
1205 WCHAR buf
[5] = {'0','x',0};
1206 memcpy(buf
+ 2, src
+ 1, 2*sizeof(WCHAR
));
1208 StrToIntExW(buf
, STIF_SUPPORT_HEX
, &ih
);
1210 src
+= 2; /* Advance to end of escape */
1214 if(dwFlags
& URL_UNESCAPE_INPLACE
|| needed
< *pcchUnescaped
)
1218 if(dwFlags
& URL_UNESCAPE_INPLACE
|| needed
< *pcchUnescaped
) {
1222 needed
++; /* add one for the '\0' */
1225 if(!(dwFlags
& URL_UNESCAPE_INPLACE
))
1226 *pcchUnescaped
= needed
;
1229 TRACE("result %s\n", (dwFlags
& URL_UNESCAPE_INPLACE
) ?
1230 debugstr_w(pszUrl
) : debugstr_w(pszUnescaped
));
1236 /*************************************************************************
1237 * UrlGetLocationA [SHLWAPI.@]
1239 * Get the location from a Url.
1242 * pszUrl [I] Url to get the location from
1245 * A pointer to the start of the location in pszUrl, or NULL if there is
1249 * - MSDN erroneously states that "The location is the segment of the Url
1250 * starting with a '?' or '#' character". Neither V4 nor V5 of shlwapi.dll
1251 * stop at '?' and always return a NULL in this case.
1252 * - MSDN also erroneously states that "If a file URL has a query string,
1253 * the returned string is the query string". In all tested cases, if the
1254 * Url starts with "fi" then a NULL is returned. V5 gives the following results:
1257 *| NULL file://aa/b/cd#hohoh
1258 *| #hohoh http://aa/b/cd#hohoh
1259 *| NULL fi://aa/b/cd#hohoh
1260 *| #hohoh ff://aa/b/cd#hohoh
1262 LPCSTR WINAPI
UrlGetLocationA(
1268 base
.cbSize
= sizeof(base
);
1269 res1
= ParseURLA(pszUrl
, &base
);
1270 if (res1
) return NULL
; /* invalid scheme */
1272 /* if scheme is file: then never return pointer */
1273 if (strncmp(base
.pszProtocol
, "file", min(4,base
.cchProtocol
)) == 0) return NULL
;
1275 /* Look for '#' and return its addr */
1276 return strchr(base
.pszSuffix
, '#');
1279 /*************************************************************************
1280 * UrlGetLocationW [SHLWAPI.@]
1282 * See UrlGetLocationA.
1284 LPCWSTR WINAPI
UrlGetLocationW(
1290 base
.cbSize
= sizeof(base
);
1291 res1
= ParseURLW(pszUrl
, &base
);
1292 if (res1
) return NULL
; /* invalid scheme */
1294 /* if scheme is file: then never return pointer */
1295 if (strncmpW(base
.pszProtocol
, fileW
, min(4,base
.cchProtocol
)) == 0) return NULL
;
1297 /* Look for '#' and return its addr */
1298 return strchrW(base
.pszSuffix
, L
'#');
1301 /*************************************************************************
1302 * UrlCompareA [SHLWAPI.@]
1307 * pszUrl1 [I] First Url to compare
1308 * pszUrl2 [I] Url to compare to pszUrl1
1309 * fIgnoreSlash [I] TRUE = compare only up to a final slash
1312 * less than zero, zero, or greater than zero indicating pszUrl2 is greater
1313 * than, equal to, or less than pszUrl1 respectively.
1315 INT WINAPI
UrlCompareA(
1320 INT ret
, len
, len1
, len2
;
1323 return strcmp(pszUrl1
, pszUrl2
);
1324 len1
= strlen(pszUrl1
);
1325 if (pszUrl1
[len1
-1] == '/') len1
--;
1326 len2
= strlen(pszUrl2
);
1327 if (pszUrl2
[len2
-1] == '/') len2
--;
1329 return strncmp(pszUrl1
, pszUrl2
, len1
);
1330 len
= min(len1
, len2
);
1331 ret
= strncmp(pszUrl1
, pszUrl2
, len
);
1332 if (ret
) return ret
;
1333 if (len1
> len2
) return 1;
1337 /*************************************************************************
1338 * UrlCompareW [SHLWAPI.@]
1342 INT WINAPI
UrlCompareW(
1348 size_t len
, len1
, len2
;
1351 return strcmpW(pszUrl1
, pszUrl2
);
1352 len1
= strlenW(pszUrl1
);
1353 if (pszUrl1
[len1
-1] == '/') len1
--;
1354 len2
= strlenW(pszUrl2
);
1355 if (pszUrl2
[len2
-1] == '/') len2
--;
1357 return strncmpW(pszUrl1
, pszUrl2
, len1
);
1358 len
= min(len1
, len2
);
1359 ret
= strncmpW(pszUrl1
, pszUrl2
, len
);
1360 if (ret
) return ret
;
1361 if (len1
> len2
) return 1;
1365 /*************************************************************************
1366 * HashData [SHLWAPI.@]
1368 * Hash an input block into a variable sized digest.
1371 * lpSrc [I] Input block
1372 * nSrcLen [I] Length of lpSrc
1373 * lpDest [I] Output for hash digest
1374 * nDestLen [I] Length of lpDest
1377 * Success: TRUE. lpDest is filled with the computed hash value.
1378 * Failure: FALSE, if any argument is invalid.
1380 HRESULT WINAPI
HashData(LPBYTE lpSrc
, DWORD nSrcLen
,
1381 LPBYTE lpDest
, DWORD nDestLen
)
1383 INT srcCount
= nSrcLen
- 1, destCount
= nDestLen
- 1;
1385 if (IsBadReadPtr(lpSrc
, nSrcLen
) ||
1386 IsBadWritePtr(lpDest
, nDestLen
))
1387 return E_INVALIDARG
;
1389 while (destCount
>= 0)
1391 lpDest
[destCount
] = (destCount
& 0xff);
1395 while (srcCount
>= 0)
1397 destCount
= nDestLen
- 1;
1398 while (destCount
>= 0)
1400 lpDest
[destCount
] = HashDataLookup
[lpSrc
[srcCount
] ^ lpDest
[destCount
]];
1408 /*************************************************************************
1409 * UrlHashA [SHLWAPI.@]
1411 * Produce a Hash from a Url.
1414 * pszUrl [I] Url to hash
1415 * lpDest [O] Destinationh for hash
1416 * nDestLen [I] Length of lpDest
1419 * Success: S_OK. lpDest is filled with the computed hash value.
1420 * Failure: E_INVALIDARG, if any argument is invalid.
1422 HRESULT WINAPI
UrlHashA(LPCSTR pszUrl
, unsigned char *lpDest
, DWORD nDestLen
)
1424 if (IsBadStringPtrA(pszUrl
, -1) || IsBadWritePtr(lpDest
, nDestLen
))
1425 return E_INVALIDARG
;
1427 HashData((LPSTR
)pszUrl
, (int)strlen(pszUrl
), lpDest
, nDestLen
);
1431 /*************************************************************************
1432 * UrlHashW [SHLWAPI.@]
1436 HRESULT WINAPI
UrlHashW(LPCWSTR pszUrl
, unsigned char *lpDest
, DWORD nDestLen
)
1438 char szUrl
[MAX_PATH
];
1440 TRACE("(%s,%p,%ld)\n",debugstr_w(pszUrl
), lpDest
, nDestLen
);
1442 if (IsBadStringPtrW(pszUrl
, -1) || IsBadWritePtr(lpDest
, nDestLen
))
1443 return E_INVALIDARG
;
1445 /* Win32 hashes the data as an ASCII string, presumably so that both A+W
1446 * return the same digests for the same URL.
1448 WideCharToMultiByte(0, 0, pszUrl
, -1, szUrl
, MAX_PATH
, 0, 0);
1449 HashData((BYTE
*)szUrl
, (int)strlen(szUrl
), lpDest
, nDestLen
);
1453 /*************************************************************************
1454 * UrlApplySchemeA [SHLWAPI.@]
1456 * Apply a scheme to a Url.
1459 * pszIn [I] Url to apply scheme to
1460 * pszOut [O] Destination for modified Url
1461 * pcchOut [I/O] Length of pszOut/destination for length of pszOut
1462 * dwFlags [I] URL_ flags from "shlwapi.h"
1465 * Success: S_OK: pszOut contains the modified Url, pcchOut contains its length.
1466 * Failure: An HRESULT error code describing the error.
1468 HRESULT WINAPI
UrlApplySchemeA(LPCSTR pszIn
, LPSTR pszOut
, LPDWORD pcchOut
, DWORD dwFlags
)
1471 DWORD ret
, len
, len2
;
1473 TRACE("(in %s, out size %ld, flags %08lx) using W version\n",
1474 debugstr_a(pszIn
), *pcchOut
, dwFlags
);
1476 in
= HeapAlloc(GetProcessHeap(), 0,
1477 (2*INTERNET_MAX_URL_LENGTH
) * sizeof(WCHAR
));
1478 out
= in
+ INTERNET_MAX_URL_LENGTH
;
1480 MultiByteToWideChar(0, 0, pszIn
, -1, in
, INTERNET_MAX_URL_LENGTH
);
1481 len
= INTERNET_MAX_URL_LENGTH
;
1483 ret
= UrlApplySchemeW(in
, out
, &len
, dwFlags
);
1484 if ((ret
!= S_OK
) && (ret
!= S_FALSE
)) {
1485 HeapFree(GetProcessHeap(), 0, in
);
1489 len2
= WideCharToMultiByte(0, 0, out
, len
+1, 0, 0, 0, 0);
1490 if (len2
> *pcchOut
) {
1492 HeapFree(GetProcessHeap(), 0, in
);
1495 WideCharToMultiByte(0, 0, out
, len
+1, pszOut
, *pcchOut
, 0, 0);
1497 HeapFree(GetProcessHeap(), 0, in
);
1501 static HRESULT
URL_GuessScheme(LPCWSTR pszIn
, LPWSTR pszOut
, LPDWORD pcchOut
)
1506 DWORD value_len
, data_len
, dwType
, i
;
1507 WCHAR reg_path
[MAX_PATH
];
1508 WCHAR value
[MAX_PATH
], data
[MAX_PATH
];
1511 MultiByteToWideChar(0, 0,
1512 "Software\\Microsoft\\Windows\\CurrentVersion\\URL\\Prefixes",
1513 -1, reg_path
, MAX_PATH
);
1514 RegOpenKeyExW(HKEY_LOCAL_MACHINE
, reg_path
, 0, 1, &newkey
);
1516 while(value_len
= data_len
= MAX_PATH
,
1517 RegEnumValueW(newkey
, index
, value
, &value_len
,
1518 0, &dwType
, (LPVOID
)data
, &data_len
) == 0) {
1519 TRACE("guess %d %s is %s\n",
1520 index
, debugstr_w(value
), debugstr_w(data
));
1523 for(i
=0; i
<value_len
; i
++) {
1526 /* remember that TRUE is not-equal */
1527 j
= ChrCmpIW(Wxx
, Wyy
);
1530 if ((i
== value_len
) && !j
) {
1531 if (strlenW(data
) + strlenW(pszIn
) + 1 > *pcchOut
) {
1532 *pcchOut
= strlenW(data
) + strlenW(pszIn
) + 1;
1533 RegCloseKey(newkey
);
1536 strcpyW(pszOut
, data
);
1537 strcatW(pszOut
, pszIn
);
1538 *pcchOut
= strlenW(pszOut
);
1539 TRACE("matched and set to %s\n", debugstr_w(pszOut
));
1540 RegCloseKey(newkey
);
1545 RegCloseKey(newkey
);
1549 static HRESULT
URL_ApplyDefault(LPCWSTR pszIn
, LPWSTR pszOut
, LPDWORD pcchOut
)
1552 DWORD data_len
, dwType
;
1553 WCHAR reg_path
[MAX_PATH
];
1554 WCHAR value
[MAX_PATH
], data
[MAX_PATH
];
1556 /* get and prepend default */
1557 MultiByteToWideChar(0, 0,
1558 "Software\\Microsoft\\Windows\\CurrentVersion\\URL\\DefaultPrefix",
1559 -1, reg_path
, MAX_PATH
);
1560 RegOpenKeyExW(HKEY_LOCAL_MACHINE
, reg_path
, 0, 1, &newkey
);
1561 data_len
= MAX_PATH
;
1564 RegQueryValueExW(newkey
, value
, 0, &dwType
, (LPBYTE
)data
, &data_len
);
1565 RegCloseKey(newkey
);
1566 if (strlenW(data
) + strlenW(pszIn
) + 1 > *pcchOut
) {
1567 *pcchOut
= strlenW(data
) + strlenW(pszIn
) + 1;
1570 strcpyW(pszOut
, data
);
1571 strcatW(pszOut
, pszIn
);
1572 *pcchOut
= strlenW(pszOut
);
1573 TRACE("used default %s\n", debugstr_w(pszOut
));
1577 /*************************************************************************
1578 * UrlApplySchemeW [SHLWAPI.@]
1580 * See UrlApplySchemeA.
1582 HRESULT WINAPI
UrlApplySchemeW(LPCWSTR pszIn
, LPWSTR pszOut
, LPDWORD pcchOut
, DWORD dwFlags
)
1584 PARSEDURLW in_scheme
;
1588 TRACE("(in %s, out size %ld, flags %08lx)\n",
1589 debugstr_w(pszIn
), *pcchOut
, dwFlags
);
1591 if (dwFlags
& URL_APPLY_GUESSFILE
) {
1592 FIXME("(%s %p %p(%ld) 0x%08lx): stub URL_APPLY_GUESSFILE not implemented\n",
1593 debugstr_w(pszIn
), pszOut
, pcchOut
, *pcchOut
, dwFlags
);
1594 strcpyW(pszOut
, pszIn
);
1595 *pcchOut
= strlenW(pszOut
);
1599 in_scheme
.cbSize
= sizeof(in_scheme
);
1600 /* See if the base has a scheme */
1601 res1
= ParseURLW(pszIn
, &in_scheme
);
1603 /* no scheme in input, need to see if we need to guess */
1604 if (dwFlags
& URL_APPLY_GUESSSCHEME
) {
1605 if ((ret
= URL_GuessScheme(pszIn
, pszOut
, pcchOut
)) != -1)
1610 /* we have a scheme, see if valid (known scheme) */
1611 if (in_scheme
.nScheme
) {
1612 /* have valid scheme, so just copy and exit */
1613 if (strlenW(pszIn
) + 1 > *pcchOut
) {
1614 *pcchOut
= strlenW(pszIn
) + 1;
1617 strcpyW(pszOut
, pszIn
);
1618 *pcchOut
= strlenW(pszOut
);
1619 TRACE("valid scheme, returing copy\n");
1624 /* If we are here, then either invalid scheme,
1625 * or no scheme and can't/failed guess.
1627 if ( ( ((res1
== 0) && (dwFlags
& URL_APPLY_FORCEAPPLY
)) ||
1629 (dwFlags
& URL_APPLY_DEFAULT
)) {
1630 /* find and apply default scheme */
1631 return URL_ApplyDefault(pszIn
, pszOut
, pcchOut
);
1634 /* just copy and give proper return code */
1635 if (strlenW(pszIn
) + 1 > *pcchOut
) {
1636 *pcchOut
= strlenW(pszIn
) + 1;
1639 strcpyW(pszOut
, pszIn
);
1640 *pcchOut
= strlenW(pszOut
);
1641 TRACE("returning copy, left alone\n");
1645 /*************************************************************************
1646 * UrlIsA [SHLWAPI.@]
1648 * Determine if a Url is of a certain class.
1651 * pszUrl [I] Url to check
1652 * Urlis [I] URLIS_ constant from "shlwapi.h"
1655 * TRUE if pszUrl belongs to the class type in Urlis.
1658 BOOL WINAPI
UrlIsA(LPCSTR pszUrl
, URLIS Urlis
)
1664 TRACE("(%s %d)\n", debugstr_a(pszUrl
), Urlis
);
1669 base
.cbSize
= sizeof(base
);
1670 res1
= ParseURLA(pszUrl
, &base
);
1671 if (res1
) return FALSE
; /* invalid scheme */
1672 switch (base
.nScheme
)
1674 case URL_SCHEME_MAILTO
:
1675 case URL_SCHEME_SHELL
:
1676 case URL_SCHEME_JAVASCRIPT
:
1677 case URL_SCHEME_VBSCRIPT
:
1678 case URL_SCHEME_ABOUT
:
1684 return !StrCmpNA("file:", pszUrl
, 5);
1686 case URLIS_DIRECTORY
:
1687 last
= pszUrl
+ strlen(pszUrl
) - 1;
1688 return (last
>= pszUrl
&& (*last
== '/' || *last
== '\\' ));
1691 return PathIsURLA(pszUrl
);
1693 case URLIS_NOHISTORY
:
1694 case URLIS_APPLIABLE
:
1695 case URLIS_HASQUERY
:
1697 FIXME("(%s %d): stub\n", debugstr_a(pszUrl
), Urlis
);
1702 /*************************************************************************
1703 * UrlIsW [SHLWAPI.@]
1707 BOOL WINAPI
UrlIsW(LPCWSTR pszUrl
, URLIS Urlis
)
1709 static const WCHAR stemp
[] = { 'f','i','l','e',':',0 };
1714 TRACE("(%s %d)\n", debugstr_w(pszUrl
), Urlis
);
1719 base
.cbSize
= sizeof(base
);
1720 res1
= ParseURLW(pszUrl
, &base
);
1721 if (res1
) return FALSE
; /* invalid scheme */
1722 switch (base
.nScheme
)
1724 case URL_SCHEME_MAILTO
:
1725 case URL_SCHEME_SHELL
:
1726 case URL_SCHEME_JAVASCRIPT
:
1727 case URL_SCHEME_VBSCRIPT
:
1728 case URL_SCHEME_ABOUT
:
1734 return !strncmpW(stemp
, pszUrl
, 5);
1736 case URLIS_DIRECTORY
:
1737 last
= pszUrl
+ strlenW(pszUrl
) - 1;
1738 return (last
>= pszUrl
&& (*last
== '/' || *last
== '\\'));
1741 return PathIsURLW(pszUrl
);
1743 case URLIS_NOHISTORY
:
1744 case URLIS_APPLIABLE
:
1745 case URLIS_HASQUERY
:
1747 FIXME("(%s %d): stub\n", debugstr_w(pszUrl
), Urlis
);
1752 /*************************************************************************
1753 * UrlIsNoHistoryA [SHLWAPI.@]
1755 * Determine if a Url should not be stored in the users history list.
1758 * pszUrl [I] Url to check
1761 * TRUE, if pszUrl should be excluded from the history list,
1764 BOOL WINAPI
UrlIsNoHistoryA(LPCSTR pszUrl
)
1766 return UrlIsA(pszUrl
, URLIS_NOHISTORY
);
1769 /*************************************************************************
1770 * UrlIsNoHistoryW [SHLWAPI.@]
1772 * See UrlIsNoHistoryA.
1774 BOOL WINAPI
UrlIsNoHistoryW(LPCWSTR pszUrl
)
1776 return UrlIsW(pszUrl
, URLIS_NOHISTORY
);
1779 /*************************************************************************
1780 * UrlIsOpaqueA [SHLWAPI.@]
1782 * Determine if a Url is opaque.
1785 * pszUrl [I] Url to check
1788 * TRUE if pszUrl is opaque,
1792 * An opaque Url is one that does not start with "<protocol>://".
1794 BOOL WINAPI
UrlIsOpaqueA(LPCSTR pszUrl
)
1796 return UrlIsA(pszUrl
, URLIS_OPAQUE
);
1799 /*************************************************************************
1800 * UrlIsOpaqueW [SHLWAPI.@]
1804 BOOL WINAPI
UrlIsOpaqueW(LPCWSTR pszUrl
)
1806 return UrlIsW(pszUrl
, URLIS_OPAQUE
);
1809 /*************************************************************************
1810 * Scans for characters of type "type" and when not matching found,
1811 * returns pointer to it and length in size.
1813 * Characters tested based on RFC 1738
1815 static LPCWSTR
URL_ScanID(LPCWSTR start
, LPDWORD size
, WINE_URL_SCAN_TYPE type
)
1817 static DWORD alwayszero
= 0;
1826 if ( (islowerW(*start
) && isalphaW(*start
)) ||
1841 if ( isalphaW(*start
) ||
1843 /* user/password only characters */
1848 /* *extra* characters */
1851 (*start
== L
'\'') ||
1855 /* *safe* characters */
1863 } else if (*start
== L
'%') {
1864 if (isxdigitW(*(start
+1)) &&
1865 isxdigitW(*(start
+2))) {
1877 if (isdigitW(*start
)) {
1888 if (isalnumW(*start
) ||
1890 (*start
== L
'.') ) {
1899 FIXME("unknown type %d\n", type
);
1900 return (LPWSTR
)&alwayszero
;
1902 /* TRACE("scanned %ld characters next char %p<%c>\n",
1903 *size, start, *start); */
1907 /*************************************************************************
1908 * Attempt to parse URL into pieces.
1910 static LONG
URL_ParseUrl(LPCWSTR pszUrl
, WINE_PARSE_URL
*pl
)
1914 memset(pl
, 0, sizeof(WINE_PARSE_URL
));
1915 pl
->pScheme
= pszUrl
;
1916 work
= URL_ScanID(pl
->pScheme
, &pl
->szScheme
, SCHEME
);
1917 if (!*work
|| (*work
!= L
':')) goto ErrorExit
;
1919 if ((*work
!= L
'/') || (*(work
+1) != L
'/')) goto ErrorExit
;
1920 pl
->pUserName
= work
+ 2;
1921 work
= URL_ScanID(pl
->pUserName
, &pl
->szUserName
, USERPASS
);
1922 if (*work
== L
':' ) {
1923 /* parse password */
1925 pl
->pPassword
= work
;
1926 work
= URL_ScanID(pl
->pPassword
, &pl
->szPassword
, USERPASS
);
1927 if (*work
!= L
'@') {
1928 /* what we just parsed must be the hostname and port
1929 * so reset pointers and clear then let it parse */
1930 pl
->szUserName
= pl
->szPassword
= 0;
1931 work
= pl
->pUserName
- 1;
1932 pl
->pUserName
= pl
->pPassword
= 0;
1934 } else if (*work
== L
'@') {
1938 } else if (!*work
|| (*work
== L
'/') || (*work
== L
'.')) {
1939 /* what was parsed was hostname, so reset pointers and let it parse */
1940 pl
->szUserName
= pl
->szPassword
= 0;
1941 work
= pl
->pUserName
- 1;
1942 pl
->pUserName
= pl
->pPassword
= 0;
1943 } else goto ErrorExit
;
1945 /* now start parsing hostname or hostnumber */
1947 pl
->pHostName
= work
;
1948 work
= URL_ScanID(pl
->pHostName
, &pl
->szHostName
, HOST
);
1949 if (*work
== L
':') {
1953 work
= URL_ScanID(pl
->pPort
, &pl
->szPort
, PORT
);
1955 if (*work
== L
'/') {
1956 /* see if query string */
1957 pl
->pQuery
= strchrW(work
, L
'?');
1958 if (pl
->pQuery
) pl
->szQuery
= strlenW(pl
->pQuery
);
1960 TRACE("parse successful: scheme=%p(%ld), user=%p(%ld), pass=%p(%ld), host=%p(%ld), port=%p(%ld), query=%p(%ld)\n",
1961 pl
->pScheme
, pl
->szScheme
,
1962 pl
->pUserName
, pl
->szUserName
,
1963 pl
->pPassword
, pl
->szPassword
,
1964 pl
->pHostName
, pl
->szHostName
,
1965 pl
->pPort
, pl
->szPort
,
1966 pl
->pQuery
, pl
->szQuery
);
1969 FIXME("failed to parse %s\n", debugstr_w(pszUrl
));
1970 return E_INVALIDARG
;
1973 /*************************************************************************
1974 * UrlGetPartA [SHLWAPI.@]
1976 * Retrieve part of a Url.
1979 * pszIn [I] Url to parse
1980 * pszOut [O] Destination for part of pszIn requested
1981 * pcchOut [I] Size of pszOut
1982 * [O] length of pszOut string EXLUDING '\0' if S_OK, otherwise
1983 * needed size of pszOut INCLUDING '\0'.
1984 * dwPart [I] URL_PART_ enum from "shlwapi.h"
1985 * dwFlags [I] URL_ flags from "shlwapi.h"
1988 * Success: S_OK. pszOut contains the part requested, pcchOut contains its length.
1989 * Failure: An HRESULT error code describing the error.
1991 HRESULT WINAPI
UrlGetPartA(LPCSTR pszIn
, LPSTR pszOut
, LPDWORD pcchOut
,
1992 DWORD dwPart
, DWORD dwFlags
)
1995 DWORD ret
, len
, len2
;
1997 in
= HeapAlloc(GetProcessHeap(), 0,
1998 (2*INTERNET_MAX_URL_LENGTH
) * sizeof(WCHAR
));
1999 out
= in
+ INTERNET_MAX_URL_LENGTH
;
2001 MultiByteToWideChar(0, 0, pszIn
, -1, in
, INTERNET_MAX_URL_LENGTH
);
2003 len
= INTERNET_MAX_URL_LENGTH
;
2004 ret
= UrlGetPartW(in
, out
, &len
, dwPart
, dwFlags
);
2007 HeapFree(GetProcessHeap(), 0, in
);
2011 len2
= WideCharToMultiByte(0, 0, out
, len
, 0, 0, 0, 0);
2012 if (len2
> *pcchOut
) {
2014 HeapFree(GetProcessHeap(), 0, in
);
2017 WideCharToMultiByte(0, 0, out
, len
+1, pszOut
, *pcchOut
, 0, 0);
2019 HeapFree(GetProcessHeap(), 0, in
);
2023 /*************************************************************************
2024 * UrlGetPartW [SHLWAPI.@]
2028 HRESULT WINAPI
UrlGetPartW(LPCWSTR pszIn
, LPWSTR pszOut
, LPDWORD pcchOut
,
2029 DWORD dwPart
, DWORD dwFlags
)
2033 DWORD size
, schsize
;
2034 LPCWSTR addr
, schaddr
;
2036 TRACE("(%s %p %p(%ld) %08lx %08lx)\n",
2037 debugstr_w(pszIn
), pszOut
, pcchOut
, *pcchOut
, dwPart
, dwFlags
);
2039 ret
= URL_ParseUrl(pszIn
, &pl
);
2041 schaddr
= pl
.pScheme
;
2042 schsize
= pl
.szScheme
;
2045 case URL_PART_SCHEME
:
2046 if (!pl
.szScheme
) return E_INVALIDARG
;
2051 case URL_PART_HOSTNAME
:
2052 if (!pl
.szHostName
) return E_INVALIDARG
;
2053 addr
= pl
.pHostName
;
2054 size
= pl
.szHostName
;
2057 case URL_PART_USERNAME
:
2058 if (!pl
.szUserName
) return E_INVALIDARG
;
2059 addr
= pl
.pUserName
;
2060 size
= pl
.szUserName
;
2063 case URL_PART_PASSWORD
:
2064 if (!pl
.szPassword
) return E_INVALIDARG
;
2065 addr
= pl
.pPassword
;
2066 size
= pl
.szPassword
;
2070 if (!pl
.szPort
) return E_INVALIDARG
;
2075 case URL_PART_QUERY
:
2076 if (!pl
.szQuery
) return E_INVALIDARG
;
2082 return E_INVALIDARG
;
2085 if (dwFlags
== URL_PARTFLAG_KEEPSCHEME
) {
2086 if (*pcchOut
< schsize
+ size
+ 2) {
2087 *pcchOut
= schsize
+ size
+ 2;
2090 memcpy(pszOut
, schaddr
, schsize
*sizeof(WCHAR
));
2091 pszOut
[schsize
] = ':';
2092 memcpy(pszOut
+schsize
+1, addr
, size
*sizeof(WCHAR
));
2093 pszOut
[schsize
+1+size
] = 0;
2094 *pcchOut
= schsize
+ 1 + size
;
2097 if (*pcchOut
< size
+ 1) {*pcchOut
= size
+1; return E_POINTER
;}
2098 memcpy(pszOut
, addr
, size
*sizeof(WCHAR
));
2102 TRACE("len=%ld %s\n", *pcchOut
, debugstr_w(pszOut
));
2107 /*************************************************************************
2108 * PathIsURLA [SHLWAPI.@]
2110 * Check if the given path is a Url.
2113 * lpszPath [I] Path to check.
2116 * TRUE if lpszPath is a Url.
2117 * FALSE if lpszPath is NULL or not a Url.
2119 BOOL WINAPI
PathIsURLA(LPCSTR lpstrPath
)
2124 if (!lpstrPath
|| !*lpstrPath
) return FALSE
;
2127 base
.cbSize
= sizeof(base
);
2128 res1
= ParseURLA(lpstrPath
, &base
);
2129 return (base
.nScheme
!= URL_SCHEME_INVALID
);
2132 /*************************************************************************
2133 * PathIsURLW [SHLWAPI.@]
2137 BOOL WINAPI
PathIsURLW(LPCWSTR lpstrPath
)
2142 if (!lpstrPath
|| !*lpstrPath
) return FALSE
;
2145 base
.cbSize
= sizeof(base
);
2146 res1
= ParseURLW(lpstrPath
, &base
);
2147 return (base
.nScheme
!= URL_SCHEME_INVALID
);
2150 /*************************************************************************
2151 * UrlCreateFromPathA [SHLWAPI.@]
2153 * See UrlCreateFromPathW
2155 HRESULT WINAPI
UrlCreateFromPathA(LPCSTR pszPath
, LPSTR pszUrl
, LPDWORD pcchUrl
, DWORD dwReserved
)
2157 WCHAR bufW
[INTERNET_MAX_URL_LENGTH
];
2159 UNICODE_STRING pathW
;
2161 DWORD lenW
= sizeof(bufW
)/sizeof(WCHAR
), lenA
;
2163 if(!RtlCreateUnicodeStringFromAsciiz(&pathW
, pszPath
))
2164 return E_INVALIDARG
;
2165 if((ret
= UrlCreateFromPathW(pathW
.Buffer
, urlW
, &lenW
, dwReserved
)) == E_POINTER
) {
2166 urlW
= HeapAlloc(GetProcessHeap(), 0, lenW
* sizeof(WCHAR
));
2167 ret
= UrlCreateFromPathW(pathW
.Buffer
, urlW
, &lenW
, dwReserved
);
2169 if(ret
== S_OK
|| ret
== S_FALSE
) {
2170 RtlUnicodeToMultiByteSize(&lenA
, urlW
, lenW
* sizeof(WCHAR
));
2171 if(*pcchUrl
> lenA
) {
2172 RtlUnicodeToMultiByteN(pszUrl
, *pcchUrl
- 1, &lenA
, urlW
, lenW
* sizeof(WCHAR
));
2176 *pcchUrl
= lenA
+ 1;
2180 if(urlW
!= bufW
) HeapFree(GetProcessHeap(), 0, urlW
);
2181 RtlFreeUnicodeString(&pathW
);
2185 /*************************************************************************
2186 * UrlCreateFromPathW [SHLWAPI.@]
2188 * Create a Url from a file path.
2191 * pszPath [I] Path to convert
2192 * pszUrl [O] Destination for the converted Url
2193 * pcchUrl [I/O] Length of pszUrl
2194 * dwReserved [I] Reserved, must be 0
2197 * Success: S_OK pszUrl contains the converted path, S_FALSE if the path is already a Url
2198 * Failure: An HRESULT error code.
2200 HRESULT WINAPI
UrlCreateFromPathW(LPCWSTR pszPath
, LPWSTR pszUrl
, LPDWORD pcchUrl
, DWORD dwReserved
)
2205 WCHAR file_colonW
[] = {'f','i','l','e',':',0};
2206 WCHAR three_slashesW
[] = {'/','/','/',0};
2207 PARSEDURLW parsed_url
;
2209 TRACE("(%s, %p, %p, 0x%08lx)\n", debugstr_w(pszPath
), pszUrl
, pcchUrl
, dwReserved
);
2211 /* Validate arguments */
2212 if (dwReserved
!= 0)
2213 return E_INVALIDARG
;
2214 if (!pszUrl
|| !pcchUrl
)
2215 return E_INVALIDARG
;
2218 parsed_url
.cbSize
= sizeof(parsed_url
);
2219 if(ParseURLW(pszPath
, &parsed_url
) == S_OK
) {
2220 if(parsed_url
.nScheme
!= URL_SCHEME_INVALID
&& parsed_url
.cchProtocol
> 1) {
2221 needed
= strlenW(pszPath
);
2222 if (needed
>= *pcchUrl
) {
2223 *pcchUrl
= needed
+ 1;
2227 strcpyW(pszUrl
, pszPath
);
2233 pszNewUrl
= HeapAlloc(GetProcessHeap(), 0, (strlenW(pszPath
) + 9) * sizeof(WCHAR
)); /* "file:///" + pszPath_len + 1 */
2234 strcpyW(pszNewUrl
, file_colonW
);
2235 if(isalphaW(pszPath
[0]) && pszPath
[1] == ':')
2236 strcatW(pszNewUrl
, three_slashesW
);
2237 strcatW(pszNewUrl
, pszPath
);
2238 ret
= UrlEscapeW(pszNewUrl
, pszUrl
, pcchUrl
, URL_ESCAPE_PERCENT
);
2240 HeapFree(GetProcessHeap(), 0, pszNewUrl
);
2244 /*************************************************************************
2245 * SHAutoComplete [SHLWAPI.@]
2247 * Enable auto-completion for an edit control.
2250 * hwndEdit [I] Handle of control to enable auto-completion for
2251 * dwFlags [I] SHACF_ flags from "shlwapi.h"
2254 * Success: S_OK. Auto-completion is enabled for the control.
2255 * Failure: An HRESULT error code indicating the error.
2257 HRESULT WINAPI
SHAutoComplete(HWND hwndEdit
, DWORD dwFlags
)
2259 FIXME("SHAutoComplete stub\n");
2263 /*************************************************************************
2264 * MLBuildResURLA [SHLWAPI.405]
2266 * Create a Url pointing to a resource in a module.
2269 * lpszLibName [I] Name of the module containing the resource
2270 * hMod [I] Callers module handle
2271 * dwFlags [I] Undocumented flags for loading the module
2272 * lpszRes [I] Resource name
2273 * lpszDest [O] Destination for resulting Url
2274 * dwDestLen [I] Length of lpszDest
2277 * Success: S_OK. lpszDest constains the resource Url.
2278 * Failure: E_INVALIDARG, if any argument is invalid, or
2279 * E_FAIL if dwDestLen is too small.
2281 HRESULT WINAPI
MLBuildResURLA(LPCSTR lpszLibName
, HMODULE hMod
, DWORD dwFlags
,
2282 LPCSTR lpszRes
, LPSTR lpszDest
, DWORD dwDestLen
)
2284 WCHAR szLibName
[MAX_PATH
], szRes
[MAX_PATH
], szDest
[MAX_PATH
];
2288 MultiByteToWideChar(CP_ACP
, 0, lpszLibName
, -1, szLibName
, sizeof(szLibName
)/sizeof(WCHAR
));
2291 MultiByteToWideChar(CP_ACP
, 0, lpszRes
, -1, szRes
, sizeof(szRes
)/sizeof(WCHAR
));
2293 if (dwDestLen
> sizeof(szLibName
)/sizeof(WCHAR
))
2294 dwDestLen
= sizeof(szLibName
)/sizeof(WCHAR
);
2296 hRet
= MLBuildResURLW(lpszLibName
? szLibName
: NULL
, hMod
, dwFlags
,
2297 lpszRes
? szRes
: NULL
, lpszDest
? szDest
: NULL
, dwDestLen
);
2298 if (SUCCEEDED(hRet
) && lpszDest
)
2299 WideCharToMultiByte(CP_ACP
, 0, szDest
, -1, lpszDest
, dwDestLen
, 0, 0);
2304 /*************************************************************************
2305 * MLBuildResURLA [SHLWAPI.406]
2307 * See MLBuildResURLA.
2309 HRESULT WINAPI
MLBuildResURLW(LPCWSTR lpszLibName
, HMODULE hMod
, DWORD dwFlags
,
2310 LPCWSTR lpszRes
, LPWSTR lpszDest
, DWORD dwDestLen
)
2312 static const WCHAR szRes
[] = { 'r','e','s',':','/','/','\0' };
2313 #define szResLen ((sizeof(szRes) - sizeof(WCHAR))/sizeof(WCHAR))
2314 HRESULT hRet
= E_FAIL
;
2316 TRACE("(%s,%p,0x%08lx,%s,%p,%ld)\n", debugstr_w(lpszLibName
), hMod
, dwFlags
,
2317 debugstr_w(lpszRes
), lpszDest
, dwDestLen
);
2319 if (!lpszLibName
|| !hMod
|| hMod
== INVALID_HANDLE_VALUE
|| !lpszRes
||
2320 !lpszDest
|| (dwFlags
&& dwFlags
!= 2))
2321 return E_INVALIDARG
;
2323 if (dwDestLen
>= szResLen
+ 1)
2325 dwDestLen
-= (szResLen
+ 1);
2326 memcpy(lpszDest
, szRes
, sizeof(szRes
));
2328 hMod
= MLLoadLibraryW(lpszLibName
, hMod
, dwFlags
);
2332 WCHAR szBuff
[MAX_PATH
];
2335 len
= GetModuleFileNameW(hMod
, szBuff
, sizeof(szBuff
)/sizeof(WCHAR
));
2336 if (len
&& len
< sizeof(szBuff
)/sizeof(WCHAR
))
2338 DWORD dwPathLen
= strlenW(szBuff
) + 1;
2340 if (dwDestLen
>= dwPathLen
)
2344 dwDestLen
-= dwPathLen
;
2345 memcpy(lpszDest
+ szResLen
, szBuff
, dwPathLen
* sizeof(WCHAR
));
2347 dwResLen
= strlenW(lpszRes
) + 1;
2348 if (dwDestLen
>= dwResLen
+ 1)
2350 lpszDest
[szResLen
+ dwPathLen
+ dwResLen
] = '/';
2351 memcpy(lpszDest
+ szResLen
+ dwPathLen
, lpszRes
, dwResLen
* sizeof(WCHAR
));
2356 MLFreeLibrary(hMod
);