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"
33 #define NO_SHLWAPI_STREAM
35 #include "wine/debug.h"
37 HMODULE WINAPI
MLLoadLibraryW(LPCWSTR
,HMODULE
,DWORD
);
38 BOOL WINAPI
MLFreeLibrary(HMODULE
);
39 HRESULT WINAPI
MLBuildResURLW(LPCWSTR
,HMODULE
,DWORD
,LPCWSTR
,LPWSTR
,DWORD
);
41 WINE_DEFAULT_DEBUG_CHANNEL(shell
);
43 /* The following schemes were identified in the native version of
44 * SHLWAPI.DLL version 5.50
47 URL_SCHEME_INVALID
= -1,
48 URL_SCHEME_UNKNOWN
= 0,
63 URL_SCHEME_JAVASCRIPT
,
71 URL_SCHEME scheme_number
;
75 static const SHL_2_inet_scheme shlwapi_schemes
[] = {
76 {URL_SCHEME_FTP
, "ftp"},
77 {URL_SCHEME_HTTP
, "http"},
78 {URL_SCHEME_GOPHER
, "gopher"},
79 {URL_SCHEME_MAILTO
, "mailto"},
80 {URL_SCHEME_NEWS
, "news"},
81 {URL_SCHEME_NNTP
, "nntp"},
82 {URL_SCHEME_TELNET
, "telnet"},
83 {URL_SCHEME_WAIS
, "wais"},
84 {URL_SCHEME_FILE
, "file"},
85 {URL_SCHEME_MK
, "mk"},
86 {URL_SCHEME_HTTPS
, "https"},
87 {URL_SCHEME_SHELL
, "shell"},
88 {URL_SCHEME_SNEWS
, "snews"},
89 {URL_SCHEME_LOCAL
, "local"},
90 {URL_SCHEME_JAVASCRIPT
, "javascript"},
91 {URL_SCHEME_VBSCRIPT
, "vbscript"},
92 {URL_SCHEME_ABOUT
, "about"},
93 {URL_SCHEME_RES
, "res"},
98 LPCWSTR pScheme
; /* [out] start of scheme */
99 DWORD szScheme
; /* [out] size of scheme (until colon) */
100 LPCWSTR pUserName
; /* [out] start of Username */
101 DWORD szUserName
; /* [out] size of Username (until ":" or "@") */
102 LPCWSTR pPassword
; /* [out] start of Password */
103 DWORD szPassword
; /* [out] size of Password (until "@") */
104 LPCWSTR pHostName
; /* [out] start of Hostname */
105 DWORD szHostName
; /* [out] size of Hostname (until ":" or "/") */
106 LPCWSTR pPort
; /* [out] start of Port */
107 DWORD szPort
; /* [out] size of Port (until "/" or eos) */
108 LPCWSTR pQuery
; /* [out] start of Query */
109 DWORD szQuery
; /* [out] size of Query (until eos) */
117 } WINE_URL_SCAN_TYPE
;
120 INT size
; /* [in] (always 0x18) */
121 LPCSTR ap1
; /* [out] start of scheme */
122 INT sizep1
; /* [out] size of scheme (until colon) */
123 LPCSTR ap2
; /* [out] pointer following first colon */
124 INT sizep2
; /* [out] size of remainder */
125 INT fcncde
; /* [out] function match of p1 (0 if unknown) */
129 INT size
; /* [in] (always 0x18) */
130 LPCWSTR ap1
; /* [out] start of scheme */
131 INT sizep1
; /* [out] size of scheme (until colon) */
132 LPCWSTR ap2
; /* [out] pointer following first colon */
133 INT sizep2
; /* [out] size of remainder */
134 INT fcncde
; /* [out] function match of p1 (0 if unknown) */
137 static const CHAR hexDigits
[] = "0123456789ABCDEF";
139 static const WCHAR fileW
[] = {'f','i','l','e','\0'};
141 static const unsigned char HashDataLookup
[256] = {
142 0x01, 0x0E, 0x6E, 0x19, 0x61, 0xAE, 0x84, 0x77, 0x8A, 0xAA, 0x7D, 0x76, 0x1B,
143 0xE9, 0x8C, 0x33, 0x57, 0xC5, 0xB1, 0x6B, 0xEA, 0xA9, 0x38, 0x44, 0x1E, 0x07,
144 0xAD, 0x49, 0xBC, 0x28, 0x24, 0x41, 0x31, 0xD5, 0x68, 0xBE, 0x39, 0xD3, 0x94,
145 0xDF, 0x30, 0x73, 0x0F, 0x02, 0x43, 0xBA, 0xD2, 0x1C, 0x0C, 0xB5, 0x67, 0x46,
146 0x16, 0x3A, 0x4B, 0x4E, 0xB7, 0xA7, 0xEE, 0x9D, 0x7C, 0x93, 0xAC, 0x90, 0xB0,
147 0xA1, 0x8D, 0x56, 0x3C, 0x42, 0x80, 0x53, 0x9C, 0xF1, 0x4F, 0x2E, 0xA8, 0xC6,
148 0x29, 0xFE, 0xB2, 0x55, 0xFD, 0xED, 0xFA, 0x9A, 0x85, 0x58, 0x23, 0xCE, 0x5F,
149 0x74, 0xFC, 0xC0, 0x36, 0xDD, 0x66, 0xDA, 0xFF, 0xF0, 0x52, 0x6A, 0x9E, 0xC9,
150 0x3D, 0x03, 0x59, 0x09, 0x2A, 0x9B, 0x9F, 0x5D, 0xA6, 0x50, 0x32, 0x22, 0xAF,
151 0xC3, 0x64, 0x63, 0x1A, 0x96, 0x10, 0x91, 0x04, 0x21, 0x08, 0xBD, 0x79, 0x40,
152 0x4D, 0x48, 0xD0, 0xF5, 0x82, 0x7A, 0x8F, 0x37, 0x69, 0x86, 0x1D, 0xA4, 0xB9,
153 0xC2, 0xC1, 0xEF, 0x65, 0xF2, 0x05, 0xAB, 0x7E, 0x0B, 0x4A, 0x3B, 0x89, 0xE4,
154 0x6C, 0xBF, 0xE8, 0x8B, 0x06, 0x18, 0x51, 0x14, 0x7F, 0x11, 0x5B, 0x5C, 0xFB,
155 0x97, 0xE1, 0xCF, 0x15, 0x62, 0x71, 0x70, 0x54, 0xE2, 0x12, 0xD6, 0xC7, 0xBB,
156 0x0D, 0x20, 0x5E, 0xDC, 0xE0, 0xD4, 0xF7, 0xCC, 0xC4, 0x2B, 0xF9, 0xEC, 0x2D,
157 0xF4, 0x6F, 0xB6, 0x99, 0x88, 0x81, 0x5A, 0xD9, 0xCA, 0x13, 0xA5, 0xE7, 0x47,
158 0xE6, 0x8E, 0x60, 0xE3, 0x3E, 0xB3, 0xF6, 0x72, 0xA2, 0x35, 0xA0, 0xD7, 0xCD,
159 0xB4, 0x2F, 0x6D, 0x2C, 0x26, 0x1F, 0x95, 0x87, 0x00, 0xD8, 0x34, 0x3F, 0x17,
160 0x25, 0x45, 0x27, 0x75, 0x92, 0xB8, 0xA3, 0xC8, 0xDE, 0xEB, 0xF8, 0xF3, 0xDB,
161 0x0A, 0x98, 0x83, 0x7B, 0xE5, 0xCB, 0x4C, 0x78, 0xD1 };
163 static inline BOOL
URL_NeedEscapeA(CHAR ch
, DWORD dwFlags
)
169 if(dwFlags
& URL_ESCAPE_SPACES_ONLY
) {
176 if ((dwFlags
& URL_ESCAPE_PERCENT
) && (ch
== '%'))
179 if (ch
<= 31 || ch
>= 127)
201 if (dwFlags
& URL_ESCAPE_SEGMENT_ONLY
) return TRUE
;
208 static inline BOOL
URL_NeedEscapeW(WCHAR ch
, DWORD dwFlags
)
214 if(dwFlags
& URL_ESCAPE_SPACES_ONLY
) {
221 if ((dwFlags
& URL_ESCAPE_PERCENT
) && (ch
== L
'%'))
224 if (ch
<= 31 || ch
>= 127)
246 if (dwFlags
& URL_ESCAPE_SEGMENT_ONLY
) return TRUE
;
253 static BOOL
URL_JustLocation(LPCWSTR str
)
255 while(*str
&& (*str
== L
'/')) str
++;
257 while (*str
&& ((*str
== L
'-') ||
259 isalnumW(*str
))) str
++;
260 if (*str
== L
'/') return FALSE
;
266 /*************************************************************************
269 * Parse a Url into its constituent parts.
273 * y [O] Undocumented structure holding the parsed information
276 * Success: S_OK. y contains the parsed Url details.
277 * Failure: An HRESULT error code.
279 DWORD WINAPI
ParseURLA(LPCSTR x
, UNKNOWN_SHLWAPI_1
*y
)
282 const SHL_2_inet_scheme
*inet_pro
;
284 y
->fcncde
= URL_SCHEME_INVALID
;
285 if (y
->size
!= 0x18) return E_INVALIDARG
;
286 /* FIXME: leading white space generates error of 0x80041001 which
289 if (*x
<= ' ') return 0x80041001;
304 /* check for no scheme in string start */
305 /* (apparently schemes *must* be larger than a single character) */
306 if ((*x
== '\0') || (y
->sizep1
<= 1)) {
311 /* found scheme, set length of remainder */
312 y
->sizep2
= lstrlenA(y
->ap2
);
314 /* see if known scheme and return indicator number */
315 y
->fcncde
= URL_SCHEME_UNKNOWN
;
316 inet_pro
= shlwapi_schemes
;
317 while (inet_pro
->scheme_name
) {
318 if (!strncasecmp(inet_pro
->scheme_name
, y
->ap1
,
319 min(y
->sizep1
, lstrlenA(inet_pro
->scheme_name
)))) {
320 y
->fcncde
= inet_pro
->scheme_number
;
328 /*************************************************************************
331 * Unicode version of ParseURLA.
333 DWORD WINAPI
ParseURLW(LPCWSTR x
, UNKNOWN_SHLWAPI_2
*y
)
336 const SHL_2_inet_scheme
*inet_pro
;
340 y
->fcncde
= URL_SCHEME_INVALID
;
341 if (y
->size
!= 0x18) return E_INVALIDARG
;
342 /* FIXME: leading white space generates error of 0x80041001 which
345 if (*x
<= L
' ') return 0x80041001;
360 /* check for no scheme in string start */
361 /* (apparently schemes *must* be larger than a single character) */
362 if ((*x
== L
'\0') || (y
->sizep1
<= 1)) {
367 /* found scheme, set length of remainder */
368 y
->sizep2
= lstrlenW(y
->ap2
);
370 /* see if known scheme and return indicator number */
371 len
= WideCharToMultiByte(0, 0, y
->ap1
, y
->sizep1
, 0, 0, 0, 0);
372 cmpstr
= (LPSTR
)HeapAlloc(GetProcessHeap(), 0, len
);
373 WideCharToMultiByte(0, 0, y
->ap1
, y
->sizep1
, cmpstr
, len
, 0, 0);
374 y
->fcncde
= URL_SCHEME_UNKNOWN
;
375 inet_pro
= shlwapi_schemes
;
376 while (inet_pro
->scheme_name
) {
377 if (!strncasecmp(inet_pro
->scheme_name
, cmpstr
,
378 min(len
, lstrlenA(inet_pro
->scheme_name
)))) {
379 y
->fcncde
= inet_pro
->scheme_number
;
384 HeapFree(GetProcessHeap(), 0, cmpstr
);
388 /*************************************************************************
389 * UrlCanonicalizeA [SHLWAPI.@]
391 * Canonicalize a Url.
394 * pszUrl [I] Url to cCanonicalize
395 * pszCanonicalized [O] Destination for converted Url.
396 * pcchCanonicalized [I/O] Length of pszUrl, destination for length of pszCanonicalized
397 * dwFlags [I] Flags controlling the conversion.
400 * Success: S_OK. The pszCanonicalized contains the converted Url.
401 * Failure: E_POINTER, if *pcchCanonicalized is too small.
403 * MSDN incorrectly describes the flags for this function. They should be:
404 *| URL_DONT_ESCAPE_EXTRA_INFO 0x02000000
405 *| URL_ESCAPE_SPACES_ONLY 0x04000000
406 *| URL_ESCAPE_PERCENT 0x00001000
407 *| URL_ESCAPE_UNSAFE 0x10000000
408 *| URL_UNESCAPE 0x10000000
409 *| URL_DONT_SIMPLIFY 0x08000000
410 *| URL_ESCAPE_SEGMENT_ONLY 0x00002000
412 HRESULT WINAPI
UrlCanonicalizeA(LPCSTR pszUrl
, LPSTR pszCanonicalized
,
413 LPDWORD pcchCanonicalized
, DWORD dwFlags
)
415 LPWSTR base
, canonical
;
416 DWORD ret
, len
, len2
;
418 TRACE("(%s %p %p 0x%08lx) using W version\n",
419 debugstr_a(pszUrl
), pszCanonicalized
,
420 pcchCanonicalized
, dwFlags
);
422 if(!pszUrl
|| !pszCanonicalized
|| !pcchCanonicalized
)
425 base
= HeapAlloc(GetProcessHeap(), 0,
426 (2*INTERNET_MAX_URL_LENGTH
) * sizeof(WCHAR
));
427 canonical
= base
+ INTERNET_MAX_URL_LENGTH
;
429 MultiByteToWideChar(0, 0, pszUrl
, -1, base
, INTERNET_MAX_URL_LENGTH
);
430 len
= INTERNET_MAX_URL_LENGTH
;
432 ret
= UrlCanonicalizeW(base
, canonical
, &len
, dwFlags
);
434 HeapFree(GetProcessHeap(), 0, base
);
438 len2
= WideCharToMultiByte(0, 0, canonical
, len
, 0, 0, 0, 0);
439 if (len2
> *pcchCanonicalized
) {
440 *pcchCanonicalized
= len
;
441 HeapFree(GetProcessHeap(), 0, base
);
444 WideCharToMultiByte(0, 0, canonical
, len
+1, pszCanonicalized
,
445 *pcchCanonicalized
, 0, 0);
446 *pcchCanonicalized
= len2
;
447 HeapFree(GetProcessHeap(), 0, base
);
451 /*************************************************************************
452 * UrlCanonicalizeW [SHLWAPI.@]
454 * See UrlCanonicalizeA.
456 HRESULT WINAPI
UrlCanonicalizeW(LPCWSTR pszUrl
, LPWSTR pszCanonicalized
,
457 LPDWORD pcchCanonicalized
, DWORD dwFlags
)
461 LPWSTR lpszUrlCpy
, wk1
, wk2
, mp
, root
;
462 INT nLen
, nByteLen
, state
;
464 TRACE("(%s %p %p 0x%08lx)\n", debugstr_w(pszUrl
), pszCanonicalized
,
465 pcchCanonicalized
, dwFlags
);
467 if(!pszUrl
|| !pszCanonicalized
|| !pcchCanonicalized
)
470 nByteLen
= (lstrlenW(pszUrl
) + 1) * sizeof(WCHAR
); /* length in bytes */
471 lpszUrlCpy
= HeapAlloc(GetProcessHeap(), 0, nByteLen
);
473 if (dwFlags
& URL_DONT_SIMPLIFY
)
474 memcpy(lpszUrlCpy
, pszUrl
, nByteLen
);
480 * 1 have 2[+] alnum 2,3
481 * 2 have scheme (found :) 4,6,3
482 * 3 failed (no location)
484 * 5 have 1[+] alnum 6,3
485 * 6 have location (found /) save root location
488 wk1
= (LPWSTR
)pszUrl
;
494 if (!isalnumW(*wk1
)) {state
= 3; break;}
496 if (!isalnumW(*wk1
)) {state
= 3; break;}
502 if (*wk1
++ == L
':') state
= 2;
505 if (*wk1
!= L
'/') {state
= 3; break;}
507 if (*wk1
!= L
'/') {state
= 6; break;}
517 if (!isalnumW(*wk1
) && (*wk1
!= L
'-') && (*wk1
!= L
'.')) {state
= 3; break;}
518 while(isalnumW(*wk1
) || (*wk1
== L
'-') || (*wk1
== L
'.')) *wk2
++ = *wk1
++;
522 if (*wk1
!= L
'/') {state
= 3; break;}
527 /* Now at root location, cannot back up any more. */
528 /* "root" will point at the '/' */
531 TRACE("wk1=%c\n", (CHAR
)*wk1
);
532 mp
= strchrW(wk1
, L
'/');
540 strncpyW(wk2
, wk1
, nLen
);
544 TRACE("found '/.'\n");
545 if (*(wk1
+1) == L
'/') {
546 /* case of /./ -> skip the ./ */
549 else if (*(wk1
+1) == L
'.') {
550 /* found /.. look for next / */
551 TRACE("found '/..'\n");
552 if (*(wk1
+2) == L
'/' || *(wk1
+2) == L
'?' || *(wk1
+2) == L
'#' || *(wk1
+2) == 0) {
553 /* case /../ -> need to backup wk2 */
554 TRACE("found '/../'\n");
555 *(wk2
-1) = L
'\0'; /* set end of string */
556 mp
= strrchrW(root
, L
'/');
557 if (mp
&& (mp
>= root
)) {
558 /* found valid backup point */
566 /* did not find point, restore '/' */
576 FIXME("how did we get here - state=%d\n", state
);
581 TRACE("Simplified, orig <%s>, simple <%s>\n",
582 debugstr_w(pszUrl
), debugstr_w(lpszUrlCpy
));
584 nLen
= lstrlenW(lpszUrlCpy
);
585 while ((nLen
> 0) && ((lpszUrlCpy
[nLen
-1] == '\r')||(lpszUrlCpy
[nLen
-1] == '\n')))
586 lpszUrlCpy
[--nLen
]=0;
588 if(dwFlags
& URL_UNESCAPE
)
589 UrlUnescapeW(lpszUrlCpy
, NULL
, NULL
, URL_UNESCAPE_INPLACE
);
591 if((EscapeFlags
= dwFlags
& (URL_ESCAPE_UNSAFE
|
592 URL_ESCAPE_SPACES_ONLY
|
594 URL_DONT_ESCAPE_EXTRA_INFO
|
595 URL_ESCAPE_SEGMENT_ONLY
))) {
596 EscapeFlags
&= ~URL_ESCAPE_UNSAFE
;
597 hr
= UrlEscapeW(lpszUrlCpy
, pszCanonicalized
, pcchCanonicalized
,
599 } else { /* No escaping needed, just copy the string */
600 nLen
= lstrlenW(lpszUrlCpy
);
601 if(nLen
< *pcchCanonicalized
)
602 memcpy(pszCanonicalized
, lpszUrlCpy
, (nLen
+ 1)*sizeof(WCHAR
));
607 *pcchCanonicalized
= nLen
;
610 HeapFree(GetProcessHeap(), 0, lpszUrlCpy
);
613 TRACE("result %s\n", debugstr_w(pszCanonicalized
));
618 /*************************************************************************
619 * UrlCombineA [SHLWAPI.@]
624 * pszBase [I] Base Url
625 * pszRelative [I] Url to combine with pszBase
626 * pszCombined [O] Destination for combined Url
627 * pcchCombined [O] Destination for length of pszCombined
628 * dwFlags [I] URL_ flags from "shlwapi.h"
631 * Success: S_OK. pszCombined contains the combined Url, pcchCombined
632 * contains its length.
633 * Failure: An HRESULT error code indicating the error.
635 HRESULT WINAPI
UrlCombineA(LPCSTR pszBase
, LPCSTR pszRelative
,
636 LPSTR pszCombined
, LPDWORD pcchCombined
,
639 LPWSTR base
, relative
, combined
;
640 DWORD ret
, len
, len2
;
642 TRACE("(base %s, Relative %s, Combine size %ld, flags %08lx) using W version\n",
643 debugstr_a(pszBase
),debugstr_a(pszRelative
),
644 pcchCombined
?*pcchCombined
:0,dwFlags
);
646 if(!pszBase
|| !pszRelative
|| !pcchCombined
)
649 base
= (LPWSTR
) HeapAlloc(GetProcessHeap(), 0,
650 (3*INTERNET_MAX_URL_LENGTH
) * sizeof(WCHAR
));
651 relative
= base
+ INTERNET_MAX_URL_LENGTH
;
652 combined
= relative
+ INTERNET_MAX_URL_LENGTH
;
654 MultiByteToWideChar(0, 0, pszBase
, -1, base
, INTERNET_MAX_URL_LENGTH
);
655 MultiByteToWideChar(0, 0, pszRelative
, -1, relative
, INTERNET_MAX_URL_LENGTH
);
658 ret
= UrlCombineW(base
, relative
, pszCombined
?combined
:NULL
, &len
, dwFlags
);
661 HeapFree(GetProcessHeap(), 0, base
);
665 len2
= WideCharToMultiByte(0, 0, combined
, len
, 0, 0, 0, 0);
666 if (len2
> *pcchCombined
) {
667 *pcchCombined
= len2
;
668 HeapFree(GetProcessHeap(), 0, base
);
671 WideCharToMultiByte(0, 0, combined
, len
+1, pszCombined
, (*pcchCombined
)+1,
673 *pcchCombined
= len2
;
674 HeapFree(GetProcessHeap(), 0, base
);
678 /*************************************************************************
679 * UrlCombineW [SHLWAPI.@]
683 HRESULT WINAPI
UrlCombineW(LPCWSTR pszBase
, LPCWSTR pszRelative
,
684 LPWSTR pszCombined
, LPDWORD pcchCombined
,
687 UNKNOWN_SHLWAPI_2 base
, relative
;
688 DWORD myflags
, sizeloc
= 0;
689 DWORD len
, res1
, res2
, process_case
= 0;
690 LPWSTR work
, preliminary
, mbase
, mrelative
;
691 static const WCHAR myfilestr
[] = {'f','i','l','e',':','/','/','/','\0'};
692 static const WCHAR single_slash
[] = {'/','\0'};
695 TRACE("(base %s, Relative %s, Combine size %ld, flags %08lx)\n",
696 debugstr_w(pszBase
),debugstr_w(pszRelative
),
697 pcchCombined
?*pcchCombined
:0,dwFlags
);
699 if(!pszBase
|| !pszRelative
|| !pcchCombined
)
705 /* Get space for duplicates of the input and the output */
706 preliminary
= HeapAlloc(GetProcessHeap(), 0, (3*INTERNET_MAX_URL_LENGTH
) *
708 mbase
= preliminary
+ INTERNET_MAX_URL_LENGTH
;
709 mrelative
= mbase
+ INTERNET_MAX_URL_LENGTH
;
710 *preliminary
= L
'\0';
712 /* Canonicalize the base input prior to looking for the scheme */
713 myflags
= dwFlags
& (URL_DONT_SIMPLIFY
| URL_UNESCAPE
);
714 len
= INTERNET_MAX_URL_LENGTH
;
715 ret
= UrlCanonicalizeW(pszBase
, mbase
, &len
, myflags
);
717 /* Canonicalize the relative input prior to looking for the scheme */
718 len
= INTERNET_MAX_URL_LENGTH
;
719 ret
= UrlCanonicalizeW(pszRelative
, mrelative
, &len
, myflags
);
721 /* See if the base has a scheme */
722 res1
= ParseURLW(mbase
, &base
);
724 /* if pszBase has no scheme, then return pszRelative */
725 TRACE("no scheme detected in Base\n");
730 /* get size of location field (if it exists) */
731 work
= (LPWSTR
)base
.ap2
;
733 if (*work
++ == L
'/') {
734 if (*work
++ == L
'/') {
735 /* At this point have start of location and
736 * it ends at next '/' or end of string.
738 while(*work
&& (*work
!= L
'/')) work
++;
739 sizeloc
= (DWORD
)(work
- base
.ap2
);
743 /* Change .sizep2 to not have the last leaf in it,
744 * Note: we need to start after the location (if it exists)
746 work
= strrchrW((base
.ap2
+sizeloc
), L
'/');
748 len
= (DWORD
)(work
- base
.ap2
+ 1);
753 * .ap2 points to location (starting with '//')
754 * .sizep2 length of location (above) and rest less the last
756 * sizeloc length of location (above) up to but not including
760 res2
= ParseURLW(mrelative
, &relative
);
762 /* no scheme in pszRelative */
763 TRACE("no scheme detected in Relative\n");
764 relative
.ap2
= mrelative
; /* case 3,4,5 depends on this */
765 relative
.sizep2
= strlenW(mrelative
);
766 if (*pszRelative
== L
':') {
767 /* case that is either left alone or uses pszBase */
768 if (dwFlags
& URL_PLUGGABLE_PROTOCOL
) {
775 if (isalnum(*mrelative
) && (*(mrelative
+ 1) == L
':')) {
776 /* case that becomes "file:///" */
777 strcpyW(preliminary
, myfilestr
);
781 if ((*mrelative
== L
'/') && (*(mrelative
+1) == L
'/')) {
782 /* pszRelative has location and rest */
786 if (*mrelative
== L
'/') {
787 /* case where pszRelative is root to location */
791 process_case
= (*base
.ap2
== L
'/') ? 5 : 3;
795 /* handle cases where pszRelative has scheme */
796 if ((base
.sizep1
== relative
.sizep1
) &&
797 (strncmpW(base
.ap1
, relative
.ap1
, base
.sizep1
) == 0)) {
799 /* since the schemes are the same */
800 if ((*relative
.ap2
== L
'/') && (*(relative
.ap2
+1) == L
'/')) {
801 /* case where pszRelative replaces location and following */
805 if (*relative
.ap2
== L
'/') {
806 /* case where pszRelative is root to location */
810 /* case where scheme is followed by document path */
814 if ((*relative
.ap2
== L
'/') && (*(relative
.ap2
+1) == L
'/')) {
815 /* case where pszRelative replaces scheme, location,
816 * and following and handles PLUGGABLE
823 } while(FALSE
); /* a litte trick to allow easy exit from nested if's */
827 switch (process_case
) {
830 * Return pszRelative appended to what ever is in pszCombined,
831 * (which may the string "file:///"
833 strcatW(preliminary
, mrelative
);
837 * Same as case 1, but if URL_PLUGGABLE_PROTOCOL was specified
838 * and pszRelative starts with "//", then append a "/"
840 strcpyW(preliminary
, mrelative
);
841 if (!(dwFlags
& URL_PLUGGABLE_PROTOCOL
) &&
842 URL_JustLocation(relative
.ap2
))
843 strcatW(preliminary
, single_slash
);
847 * Return the pszBase scheme with pszRelative. Basically
848 * keeps the scheme and replaces the domain and following.
850 strncpyW(preliminary
, base
.ap1
, base
.sizep1
+ 1);
851 work
= preliminary
+ base
.sizep1
+ 1;
852 strcpyW(work
, relative
.ap2
);
853 if (!(dwFlags
& URL_PLUGGABLE_PROTOCOL
) &&
854 URL_JustLocation(relative
.ap2
))
855 strcatW(work
, single_slash
);
859 * Return the pszBase scheme and location but everything
860 * after the location is pszRelative. (Replace document
863 strncpyW(preliminary
, base
.ap1
, base
.sizep1
+1+sizeloc
);
864 work
= preliminary
+ base
.sizep1
+ 1 + sizeloc
;
865 if (dwFlags
& URL_PLUGGABLE_PROTOCOL
)
867 strcpyW(work
, relative
.ap2
);
871 * Return the pszBase without its document (if any) and
872 * append pszRelative after its scheme.
874 strncpyW(preliminary
, base
.ap1
, base
.sizep1
+1+base
.sizep2
);
875 work
= preliminary
+ base
.sizep1
+1+base
.sizep2
- 1;
878 strcpyW(work
, relative
.ap2
);
882 FIXME("How did we get here????? process_case=%ld\n", process_case
);
887 /* Reuse mrelative as temp storage as its already allocated and not needed anymore */
888 ret
= UrlCanonicalizeW(preliminary
, mrelative
, pcchCombined
, dwFlags
);
889 if(SUCCEEDED(ret
) && pszCombined
) {
890 lstrcpyW(pszCombined
, mrelative
);
892 TRACE("return-%ld len=%ld, %s\n",
893 process_case
, *pcchCombined
, debugstr_w(pszCombined
));
895 HeapFree(GetProcessHeap(), 0, preliminary
);
899 /*************************************************************************
900 * UrlEscapeA [SHLWAPI.@]
902 * Converts unsafe characters in a Url into escape sequences.
905 * pszUrl [I] Url to modify
906 * pszEscaped [O] Destination for modified Url
907 * pcchEscaped [I/O] Length of pszUrl, destination for length of pszEscaped
908 * dwFlags [I] URL_ flags from "shlwapi.h"
911 * Success: S_OK. pszEscaped contains the escaped Url, pcchEscaped
912 * contains its length.
913 * Failure: E_POINTER, if pszEscaped is not large enough. In this case
914 * pcchEscaped is set to the required length.
916 * Converts unsafe characters into their escape sequences.
919 * - By default this function stops converting at the first '?' or
920 * '#' character (MSDN does not document this).
921 * - If dwFlags contains URL_ESCAPE_SPACES_ONLY then only spaces are
922 * converted, but the conversion continues past a '?' or '#'.
923 * - Note that this function did not work well (or at all) in shlwapi version 4.
926 * Only the following flags are implemented:
927 *| URL_ESCAPE_SPACES_ONLY
928 *| URL_DONT_ESCAPE_EXTRA_INFO
929 *| URL_ESCAPE_SEGMENT_ONLY
930 *| URL_ESCAPE_PERCENT
932 HRESULT WINAPI
UrlEscapeA(
939 DWORD needed
= 0, ret
;
940 BOOL stop_escaping
= FALSE
;
941 char next
[3], *dst
= pszEscaped
;
944 TRACE("(%s %p %lx 0x%08lx)\n", debugstr_a(pszUrl
), pszEscaped
,
945 pcchEscaped
?*pcchEscaped
:0, dwFlags
);
947 if(!pszUrl
|| !pszEscaped
|| !pcchEscaped
)
950 if(dwFlags
& ~(URL_ESCAPE_SPACES_ONLY
|
951 URL_ESCAPE_SEGMENT_ONLY
|
952 URL_DONT_ESCAPE_EXTRA_INFO
|
954 FIXME("Unimplemented flags: %08lx\n", dwFlags
);
957 if (dwFlags
& URL_ESCAPE_SPACES_ONLY
)
958 /* if SPACES_ONLY specified, reset the other controls */
959 dwFlags
&= ~(URL_DONT_ESCAPE_EXTRA_INFO
|
961 URL_ESCAPE_SEGMENT_ONLY
);
964 /* if SPACES_ONLY *not* specified then assume DONT_ESCAPE_EXTRA_INFO */
965 dwFlags
|= URL_DONT_ESCAPE_EXTRA_INFO
;
967 for(src
= pszUrl
; *src
; src
++) {
968 if(!(dwFlags
& URL_ESCAPE_SEGMENT_ONLY
) &&
969 (dwFlags
& URL_DONT_ESCAPE_EXTRA_INFO
) &&
970 (*src
== '#' || *src
== '?'))
971 stop_escaping
= TRUE
;
973 if(URL_NeedEscapeA(*src
, dwFlags
) && stop_escaping
== FALSE
) {
974 /* TRACE("escaping %c\n", *src); */
976 next
[1] = hexDigits
[(*src
>> 4) & 0xf];
977 next
[2] = hexDigits
[*src
& 0xf];
980 /* TRACE("passing %c\n", *src); */
985 if(needed
+ len
<= *pcchEscaped
) {
986 memcpy(dst
, next
, len
);
992 if(needed
< *pcchEscaped
) {
996 needed
++; /* add one for the '\0' */
999 *pcchEscaped
= needed
;
1003 /*************************************************************************
1004 * UrlEscapeW [SHLWAPI.@]
1008 HRESULT WINAPI
UrlEscapeW(
1011 LPDWORD pcchEscaped
,
1015 DWORD needed
= 0, ret
;
1016 BOOL stop_escaping
= FALSE
;
1017 WCHAR next
[5], *dst
= pszEscaped
;
1020 TRACE("(%s %p %p 0x%08lx)\n", debugstr_w(pszUrl
), pszEscaped
,
1021 pcchEscaped
, dwFlags
);
1023 if(!pszUrl
|| !pszEscaped
|| !pcchEscaped
)
1024 return E_INVALIDARG
;
1026 if(dwFlags
& ~(URL_ESCAPE_SPACES_ONLY
|
1027 URL_ESCAPE_SEGMENT_ONLY
|
1028 URL_DONT_ESCAPE_EXTRA_INFO
|
1029 URL_ESCAPE_PERCENT
))
1030 FIXME("Unimplemented flags: %08lx\n", dwFlags
);
1033 if (dwFlags
& URL_ESCAPE_SPACES_ONLY
)
1034 /* if SPACES_ONLY specified, reset the other controls */
1035 dwFlags
&= ~(URL_DONT_ESCAPE_EXTRA_INFO
|
1036 URL_ESCAPE_PERCENT
|
1037 URL_ESCAPE_SEGMENT_ONLY
);
1040 /* if SPACES_ONLY *not* specified the assume DONT_ESCAPE_EXTRA_INFO */
1041 dwFlags
|= URL_DONT_ESCAPE_EXTRA_INFO
;
1043 for(src
= pszUrl
; *src
; src
++) {
1045 * if(!(dwFlags & URL_ESCAPE_SPACES_ONLY) &&
1046 * (*src == L'#' || *src == L'?'))
1047 * stop_escaping = TRUE;
1049 if(!(dwFlags
& URL_ESCAPE_SEGMENT_ONLY
) &&
1050 (dwFlags
& URL_DONT_ESCAPE_EXTRA_INFO
) &&
1051 (*src
== L
'#' || *src
== L
'?'))
1052 stop_escaping
= TRUE
;
1054 if(URL_NeedEscapeW(*src
, dwFlags
) && stop_escaping
== FALSE
) {
1055 /* TRACE("escaping %c\n", *src); */
1058 * I would have assumed that the W form would escape
1059 * the character with 4 hex digits (or even 8),
1060 * however, experiments show that native shlwapi escapes
1061 * with only 2 hex digits.
1062 * next[1] = hexDigits[(*src >> 12) & 0xf];
1063 * next[2] = hexDigits[(*src >> 8) & 0xf];
1064 * next[3] = hexDigits[(*src >> 4) & 0xf];
1065 * next[4] = hexDigits[*src & 0xf];
1068 next
[1] = hexDigits
[(*src
>> 4) & 0xf];
1069 next
[2] = hexDigits
[*src
& 0xf];
1072 /* TRACE("passing %c\n", *src); */
1077 if(needed
+ len
<= *pcchEscaped
) {
1078 memcpy(dst
, next
, len
*sizeof(WCHAR
));
1084 if(needed
< *pcchEscaped
) {
1088 needed
++; /* add one for the '\0' */
1091 *pcchEscaped
= needed
;
1096 /*************************************************************************
1097 * UrlUnescapeA [SHLWAPI.@]
1099 * Converts Url escape sequences back to ordinary characters.
1102 * pszUrl [I/O] Url to convert
1103 * pszUnescaped [O] Destination for converted Url
1104 * pcchUnescaped [I/O] Size of output string
1105 * dwFlags [I] URL_ESCAPE_ Flags from "shlwapi.h"
1108 * Success: S_OK. The converted value is in pszUnescaped, or in pszUrl if
1109 * dwFlags includes URL_ESCAPE_INPLACE.
1110 * Failure: E_POINTER if the converted Url is bigger than pcchUnescaped. In
1111 * this case pcchUnescaped is set to the size required.
1113 * If dwFlags includes URL_DONT_ESCAPE_EXTRA_INFO, the conversion stops at
1114 * the first occurrence of either a '?' or '#' character.
1116 HRESULT WINAPI
UrlUnescapeA(
1119 LPDWORD pcchUnescaped
,
1126 BOOL stop_unescaping
= FALSE
;
1128 TRACE("(%s, %p, %p, 0x%08lx)\n", debugstr_a(pszUrl
), pszUnescaped
,
1129 pcchUnescaped
, dwFlags
);
1131 if(!pszUrl
|| !pszUnescaped
|| !pcchUnescaped
)
1132 return E_INVALIDARG
;
1134 if(dwFlags
& URL_UNESCAPE_INPLACE
)
1139 for(src
= pszUrl
, needed
= 0; *src
; src
++, needed
++) {
1140 if(dwFlags
& URL_DONT_UNESCAPE_EXTRA_INFO
&&
1141 (*src
== '#' || *src
== '?')) {
1142 stop_unescaping
= TRUE
;
1144 } else if(*src
== '%' && isxdigit(*(src
+ 1)) && isxdigit(*(src
+ 2))
1145 && stop_unescaping
== FALSE
) {
1148 memcpy(buf
, src
+ 1, 2);
1150 ih
= strtol(buf
, NULL
, 16);
1152 src
+= 2; /* Advance to end of escape */
1156 if(dwFlags
& URL_UNESCAPE_INPLACE
|| needed
< *pcchUnescaped
)
1160 if(dwFlags
& URL_UNESCAPE_INPLACE
|| needed
< *pcchUnescaped
) {
1164 needed
++; /* add one for the '\0' */
1167 if(!(dwFlags
& URL_UNESCAPE_INPLACE
))
1168 *pcchUnescaped
= needed
;
1171 TRACE("result %s\n", (dwFlags
& URL_UNESCAPE_INPLACE
) ?
1172 debugstr_a(pszUrl
) : debugstr_a(pszUnescaped
));
1178 /*************************************************************************
1179 * UrlUnescapeW [SHLWAPI.@]
1183 HRESULT WINAPI
UrlUnescapeW(
1185 LPWSTR pszUnescaped
,
1186 LPDWORD pcchUnescaped
,
1193 BOOL stop_unescaping
= FALSE
;
1195 TRACE("(%s, %p, %p, 0x%08lx)\n", debugstr_w(pszUrl
), pszUnescaped
,
1196 pcchUnescaped
, dwFlags
);
1198 if(!pszUrl
|| !pszUnescaped
|| !pcchUnescaped
)
1199 return E_INVALIDARG
;
1201 if(dwFlags
& URL_UNESCAPE_INPLACE
)
1206 for(src
= pszUrl
, needed
= 0; *src
; src
++, needed
++) {
1207 if(dwFlags
& URL_DONT_UNESCAPE_EXTRA_INFO
&&
1208 (*src
== L
'#' || *src
== L
'?')) {
1209 stop_unescaping
= TRUE
;
1211 } else if(*src
== L
'%' && isxdigitW(*(src
+ 1)) && isxdigitW(*(src
+ 2))
1212 && stop_unescaping
== FALSE
) {
1215 memcpy(buf
, src
+ 1, 2*sizeof(WCHAR
));
1217 ih
= StrToIntW(buf
);
1219 src
+= 2; /* Advance to end of escape */
1223 if(dwFlags
& URL_UNESCAPE_INPLACE
|| needed
< *pcchUnescaped
)
1227 if(dwFlags
& URL_UNESCAPE_INPLACE
|| needed
< *pcchUnescaped
) {
1231 needed
++; /* add one for the '\0' */
1234 if(!(dwFlags
& URL_UNESCAPE_INPLACE
))
1235 *pcchUnescaped
= needed
;
1238 TRACE("result %s\n", (dwFlags
& URL_UNESCAPE_INPLACE
) ?
1239 debugstr_w(pszUrl
) : debugstr_w(pszUnescaped
));
1245 /*************************************************************************
1246 * UrlGetLocationA [SHLWAPI.@]
1248 * Get the location from a Url.
1251 * pszUrl [I] Url to get the location from
1254 * A pointer to the start of the location in pszUrl, or NULL if there is
1258 * - MSDN erroneously states that "The location is the segment of the Url
1259 * starting with a '?' or '#' character". Neither V4 nor V5 of shlwapi.dll
1260 * stop at '?' and always return a NULL in this case.
1261 * - MSDN also erroneously states that "If a file URL has a query string,
1262 * the returned string is the query string". In all tested cases, if the
1263 * Url starts with "fi" then a NULL is returned. V5 gives the following results:
1266 *| NULL file://aa/b/cd#hohoh
1267 *| #hohoh http://aa/b/cd#hohoh
1268 *| NULL fi://aa/b/cd#hohoh
1269 *| #hohoh ff://aa/b/cd#hohoh
1271 LPCSTR WINAPI
UrlGetLocationA(
1274 UNKNOWN_SHLWAPI_1 base
;
1278 res1
= ParseURLA(pszUrl
, &base
);
1279 if (res1
) return NULL
; /* invalid scheme */
1281 /* if scheme is file: then never return pointer */
1282 if (strncmp(base
.ap1
, "file", min(4,base
.sizep1
)) == 0) return NULL
;
1284 /* Look for '#' and return its addr */
1285 return strchr(base
.ap2
, '#');
1288 /*************************************************************************
1289 * UrlGetLocationW [SHLWAPI.@]
1291 * See UrlGetLocationA.
1293 LPCWSTR WINAPI
UrlGetLocationW(
1296 UNKNOWN_SHLWAPI_2 base
;
1300 res1
= ParseURLW(pszUrl
, &base
);
1301 if (res1
) return NULL
; /* invalid scheme */
1303 /* if scheme is file: then never return pointer */
1304 if (strncmpW(base
.ap1
, fileW
, min(4,base
.sizep1
)) == 0) return NULL
;
1306 /* Look for '#' and return its addr */
1307 return strchrW(base
.ap2
, L
'#');
1310 /*************************************************************************
1311 * UrlCompareA [SHLWAPI.@]
1316 * pszUrl1 [I] First Url to compare
1317 * pszUrl2 [I] Url to compare to pszUrl1
1318 * fIgnoreSlash [I] TRUE = compare only up to a final slash
1321 * less than zero, zero, or greater than zero indicating pszUrl2 is greater
1322 * than, equal to, or less than pszUrl1 respectively.
1324 INT WINAPI
UrlCompareA(
1329 INT ret
, len
, len1
, len2
;
1332 return strcmp(pszUrl1
, pszUrl2
);
1333 len1
= strlen(pszUrl1
);
1334 if (pszUrl1
[len1
-1] == '/') len1
--;
1335 len2
= strlen(pszUrl2
);
1336 if (pszUrl2
[len2
-1] == '/') len2
--;
1338 return strncmp(pszUrl1
, pszUrl2
, len1
);
1339 len
= min(len1
, len2
);
1340 ret
= strncmp(pszUrl1
, pszUrl2
, len
);
1341 if (ret
) return ret
;
1342 if (len1
> len2
) return 1;
1346 /*************************************************************************
1347 * UrlCompareW [SHLWAPI.@]
1351 INT WINAPI
UrlCompareW(
1357 size_t len
, len1
, len2
;
1360 return strcmpW(pszUrl1
, pszUrl2
);
1361 len1
= strlenW(pszUrl1
);
1362 if (pszUrl1
[len1
-1] == '/') len1
--;
1363 len2
= strlenW(pszUrl2
);
1364 if (pszUrl2
[len2
-1] == '/') len2
--;
1366 return strncmpW(pszUrl1
, pszUrl2
, len1
);
1367 len
= min(len1
, len2
);
1368 ret
= strncmpW(pszUrl1
, pszUrl2
, len
);
1369 if (ret
) return ret
;
1370 if (len1
> len2
) return 1;
1374 /*************************************************************************
1375 * HashData [SHLWAPI.@]
1377 * Hash an input block into a variable sized digest.
1380 * lpSrc [I] Input block
1381 * nSrcLen [I] Length of lpSrc
1382 * lpDest [I] Output for hash digest
1383 * nDestLen [I] Length of lpDest
1386 * Success: TRUE. lpDest is filled with the computed hash value.
1387 * Failure: FALSE, if any argument is invalid.
1389 HRESULT WINAPI
HashData(const unsigned char *lpSrc
, DWORD nSrcLen
,
1390 unsigned char *lpDest
, DWORD nDestLen
)
1392 INT srcCount
= nSrcLen
- 1, destCount
= nDestLen
- 1;
1394 if (IsBadReadPtr(lpSrc
, nSrcLen
) ||
1395 IsBadWritePtr(lpDest
, nDestLen
))
1396 return E_INVALIDARG
;
1398 while (destCount
>= 0)
1400 lpDest
[destCount
] = (destCount
& 0xff);
1404 while (srcCount
>= 0)
1406 destCount
= nDestLen
- 1;
1407 while (destCount
>= 0)
1409 lpDest
[destCount
] = HashDataLookup
[lpSrc
[srcCount
] ^ lpDest
[destCount
]];
1417 /*************************************************************************
1418 * UrlHashA [SHLWAPI.@]
1420 * Produce a Hash from a Url.
1423 * pszUrl [I] Url to hash
1424 * lpDest [O] Destinationh for hash
1425 * nDestLen [I] Length of lpDest
1428 * Success: S_OK. lpDest is filled with the computed hash value.
1429 * Failure: E_INVALIDARG, if any argument is invalid.
1431 HRESULT WINAPI
UrlHashA(LPCSTR pszUrl
, unsigned char *lpDest
, DWORD nDestLen
)
1433 if (IsBadStringPtrA(pszUrl
, -1) || IsBadWritePtr(lpDest
, nDestLen
))
1434 return E_INVALIDARG
;
1436 HashData((PBYTE
)pszUrl
, (int)strlen(pszUrl
), lpDest
, nDestLen
);
1440 /*************************************************************************
1441 * UrlHashW [SHLWAPI.@]
1445 HRESULT WINAPI
UrlHashW(LPCWSTR pszUrl
, unsigned char *lpDest
, DWORD nDestLen
)
1447 char szUrl
[MAX_PATH
];
1449 TRACE("(%s,%p,%ld)\n",debugstr_w(pszUrl
), lpDest
, nDestLen
);
1451 if (IsBadStringPtrW(pszUrl
, -1) || IsBadWritePtr(lpDest
, nDestLen
))
1452 return E_INVALIDARG
;
1454 /* Win32 hashes the data as an ASCII string, presumably so that both A+W
1455 * return the same digests for the same URL.
1457 WideCharToMultiByte(0, 0, pszUrl
, -1, szUrl
, MAX_PATH
, 0, 0);
1458 HashData((PBYTE
)szUrl
, (int)strlen(szUrl
), lpDest
, nDestLen
);
1462 /*************************************************************************
1463 * UrlApplySchemeA [SHLWAPI.@]
1465 * Apply a scheme to a Url.
1468 * pszIn [I] Url to apply scheme to
1469 * pszOut [O] Destination for modified Url
1470 * pcchOut [I/O] Length of pszOut/destination for length of pszOut
1471 * dwFlags [I] URL_ flags from "shlwapi.h"
1474 * Success: S_OK: pszOut contains the modified Url, pcchOut contains its length.
1475 * Failure: An HRESULT error code describing the error.
1477 HRESULT WINAPI
UrlApplySchemeA(LPCSTR pszIn
, LPSTR pszOut
, LPDWORD pcchOut
, DWORD dwFlags
)
1480 DWORD ret
, len
, len2
;
1482 TRACE("(in %s, out size %ld, flags %08lx) using W version\n",
1483 debugstr_a(pszIn
), *pcchOut
, dwFlags
);
1485 in
= HeapAlloc(GetProcessHeap(), 0,
1486 (2*INTERNET_MAX_URL_LENGTH
) * sizeof(WCHAR
));
1487 out
= in
+ INTERNET_MAX_URL_LENGTH
;
1489 MultiByteToWideChar(0, 0, pszIn
, -1, in
, INTERNET_MAX_URL_LENGTH
);
1490 len
= INTERNET_MAX_URL_LENGTH
;
1492 ret
= UrlApplySchemeW(in
, out
, &len
, dwFlags
);
1493 if ((ret
!= S_OK
) && (ret
!= S_FALSE
)) {
1494 HeapFree(GetProcessHeap(), 0, in
);
1498 len2
= WideCharToMultiByte(0, 0, out
, len
+1, 0, 0, 0, 0);
1499 if (len2
> *pcchOut
) {
1501 HeapFree(GetProcessHeap(), 0, in
);
1504 WideCharToMultiByte(0, 0, out
, len
+1, pszOut
, *pcchOut
, 0, 0);
1506 HeapFree(GetProcessHeap(), 0, in
);
1510 static HRESULT
URL_GuessScheme(LPCWSTR pszIn
, LPWSTR pszOut
, LPDWORD pcchOut
)
1515 DWORD value_len
, data_len
, dwType
, i
;
1516 WCHAR reg_path
[MAX_PATH
];
1517 WCHAR value
[MAX_PATH
], data
[MAX_PATH
];
1520 MultiByteToWideChar(0, 0,
1521 "Software\\Microsoft\\Windows\\CurrentVersion\\URL\\Prefixes",
1522 -1, reg_path
, MAX_PATH
);
1523 RegOpenKeyExW(HKEY_LOCAL_MACHINE
, reg_path
, 0, 1, &newkey
);
1525 while(value_len
= data_len
= MAX_PATH
,
1526 RegEnumValueW(newkey
, index
, value
, &value_len
,
1527 0, &dwType
, (LPVOID
)data
, &data_len
) == 0) {
1528 TRACE("guess %d %s is %s\n",
1529 index
, debugstr_w(value
), debugstr_w(data
));
1532 for(i
=0; i
<value_len
; i
++) {
1535 /* remember that TRUE is not-equal */
1536 j
= ChrCmpIW(Wxx
, Wyy
);
1539 if ((i
== value_len
) && !j
) {
1540 if (strlenW(data
) + strlenW(pszIn
) + 1 > *pcchOut
) {
1541 *pcchOut
= strlenW(data
) + strlenW(pszIn
) + 1;
1542 RegCloseKey(newkey
);
1545 strcpyW(pszOut
, data
);
1546 strcatW(pszOut
, pszIn
);
1547 *pcchOut
= strlenW(pszOut
);
1548 TRACE("matched and set to %s\n", debugstr_w(pszOut
));
1549 RegCloseKey(newkey
);
1554 RegCloseKey(newkey
);
1558 static HRESULT
URL_ApplyDefault(LPCWSTR pszIn
, LPWSTR pszOut
, LPDWORD pcchOut
)
1561 DWORD data_len
, dwType
;
1562 WCHAR reg_path
[MAX_PATH
];
1563 WCHAR value
[MAX_PATH
], data
[MAX_PATH
];
1565 /* get and prepend default */
1566 MultiByteToWideChar(0, 0,
1567 "Software\\Microsoft\\Windows\\CurrentVersion\\URL\\DefaultPrefix",
1568 -1, reg_path
, MAX_PATH
);
1569 RegOpenKeyExW(HKEY_LOCAL_MACHINE
, reg_path
, 0, 1, &newkey
);
1570 data_len
= MAX_PATH
;
1573 RegQueryValueExW(newkey
, value
, 0, &dwType
, (LPBYTE
)data
, &data_len
);
1574 RegCloseKey(newkey
);
1575 if (strlenW(data
) + strlenW(pszIn
) + 1 > *pcchOut
) {
1576 *pcchOut
= strlenW(data
) + strlenW(pszIn
) + 1;
1579 strcpyW(pszOut
, data
);
1580 strcatW(pszOut
, pszIn
);
1581 *pcchOut
= strlenW(pszOut
);
1582 TRACE("used default %s\n", debugstr_w(pszOut
));
1586 /*************************************************************************
1587 * UrlApplySchemeW [SHLWAPI.@]
1589 * See UrlApplySchemeA.
1591 HRESULT WINAPI
UrlApplySchemeW(LPCWSTR pszIn
, LPWSTR pszOut
, LPDWORD pcchOut
, DWORD dwFlags
)
1593 UNKNOWN_SHLWAPI_2 in_scheme
;
1597 TRACE("(in %s, out size %ld, flags %08lx)\n",
1598 debugstr_w(pszIn
), *pcchOut
, dwFlags
);
1600 if (dwFlags
& URL_APPLY_GUESSFILE
) {
1601 FIXME("(%s %p %p(%ld) 0x%08lx): stub URL_APPLY_GUESSFILE not implemented\n",
1602 debugstr_w(pszIn
), pszOut
, pcchOut
, *pcchOut
, dwFlags
);
1603 strcpyW(pszOut
, pszIn
);
1604 *pcchOut
= strlenW(pszOut
);
1608 in_scheme
.size
= 24;
1609 /* See if the base has a scheme */
1610 res1
= ParseURLW(pszIn
, &in_scheme
);
1612 /* no scheme in input, need to see if we need to guess */
1613 if (dwFlags
& URL_APPLY_GUESSSCHEME
) {
1614 if ((ret
= URL_GuessScheme(pszIn
, pszOut
, pcchOut
)) != -1)
1619 /* we have a scheme, see if valid (known scheme) */
1620 if (in_scheme
.fcncde
) {
1621 /* have valid scheme, so just copy and exit */
1622 if (strlenW(pszIn
) + 1 > *pcchOut
) {
1623 *pcchOut
= strlenW(pszIn
) + 1;
1626 strcpyW(pszOut
, pszIn
);
1627 *pcchOut
= strlenW(pszOut
);
1628 TRACE("valid scheme, returing copy\n");
1633 /* If we are here, then either invalid scheme,
1634 * or no scheme and can't/failed guess.
1636 if ( ( ((res1
== 0) && (dwFlags
& URL_APPLY_FORCEAPPLY
)) ||
1638 (dwFlags
& URL_APPLY_DEFAULT
)) {
1639 /* find and apply default scheme */
1640 return URL_ApplyDefault(pszIn
, pszOut
, pcchOut
);
1643 /* just copy and give proper return code */
1644 if (strlenW(pszIn
) + 1 > *pcchOut
) {
1645 *pcchOut
= strlenW(pszIn
) + 1;
1648 strcpyW(pszOut
, pszIn
);
1649 *pcchOut
= strlenW(pszOut
);
1650 TRACE("returning copy, left alone\n");
1654 /*************************************************************************
1655 * UrlIsA [SHLWAPI.@]
1657 * Determine if a Url is of a certain class.
1660 * pszUrl [I] Url to check
1661 * Urlis [I] URLIS_ constant from "shlwapi.h"
1664 * TRUE if pszUrl belongs to the class type in Urlis.
1667 BOOL WINAPI
UrlIsA(LPCSTR pszUrl
, URLIS Urlis
)
1669 UNKNOWN_SHLWAPI_1 base
;
1677 res1
= ParseURLA(pszUrl
, &base
);
1678 if (res1
) return FALSE
; /* invalid scheme */
1679 if ((*base
.ap2
== '/') && (*(base
.ap2
+1) == '/'))
1680 /* has scheme followed by 2 '/' */
1685 return !StrCmpNA("file://", pszUrl
, 7);
1687 case URLIS_DIRECTORY
:
1688 last
= pszUrl
+ strlen(pszUrl
) - 1;
1689 return (last
>= pszUrl
&& (*last
== '/' || *last
== '\\' ));
1692 case URLIS_NOHISTORY
:
1693 case URLIS_APPLIABLE
:
1694 case URLIS_HASQUERY
:
1696 FIXME("(%s %d): stub\n", debugstr_a(pszUrl
), Urlis
);
1701 /*************************************************************************
1702 * UrlIsW [SHLWAPI.@]
1706 BOOL WINAPI
UrlIsW(LPCWSTR pszUrl
, URLIS Urlis
)
1708 static const WCHAR stemp
[] = { 'f','i','l','e',':','/','/',0 };
1709 UNKNOWN_SHLWAPI_2 base
;
1717 res1
= ParseURLW(pszUrl
, &base
);
1718 if (res1
) return FALSE
; /* invalid scheme */
1719 if ((*base
.ap2
== '/') && (*(base
.ap2
+1) == '/'))
1720 /* has scheme followed by 2 '/' */
1725 return !strncmpW(stemp
, pszUrl
, 7);
1727 case URLIS_DIRECTORY
:
1728 last
= pszUrl
+ strlenW(pszUrl
) - 1;
1729 return (last
>= pszUrl
&& (*last
== '/' || *last
== '\\'));
1732 case URLIS_NOHISTORY
:
1733 case URLIS_APPLIABLE
:
1734 case URLIS_HASQUERY
:
1736 FIXME("(%s %d): stub\n", debugstr_w(pszUrl
), Urlis
);
1741 /*************************************************************************
1742 * UrlIsNoHistoryA [SHLWAPI.@]
1744 * Determine if a Url should not be stored in the users history list.
1747 * pszUrl [I] Url to check
1750 * TRUE, if pszUrl should be excluded from the history list,
1753 BOOL WINAPI
UrlIsNoHistoryA(LPCSTR pszUrl
)
1755 return UrlIsA(pszUrl
, URLIS_NOHISTORY
);
1758 /*************************************************************************
1759 * UrlIsNoHistoryW [SHLWAPI.@]
1761 * See UrlIsNoHistoryA.
1763 BOOL WINAPI
UrlIsNoHistoryW(LPCWSTR pszUrl
)
1765 return UrlIsW(pszUrl
, URLIS_NOHISTORY
);
1768 /*************************************************************************
1769 * UrlIsOpaqueA [SHLWAPI.@]
1771 * Determine if a Url is opaque.
1774 * pszUrl [I] Url to check
1777 * TRUE if pszUrl is opaque,
1781 * An opaque Url is one that does not start with "<protocol>://".
1783 BOOL WINAPI
UrlIsOpaqueA(LPCSTR pszUrl
)
1785 return UrlIsA(pszUrl
, URLIS_OPAQUE
);
1788 /*************************************************************************
1789 * UrlIsOpaqueW [SHLWAPI.@]
1793 BOOL WINAPI
UrlIsOpaqueW(LPCWSTR pszUrl
)
1795 return UrlIsW(pszUrl
, URLIS_OPAQUE
);
1798 /*************************************************************************
1799 * Scans for characters of type "type" and when not matching found,
1800 * returns pointer to it and length in size.
1802 * Characters tested based on RFC 1738
1804 static LPCWSTR
URL_ScanID(LPCWSTR start
, LPDWORD size
, WINE_URL_SCAN_TYPE type
)
1806 static DWORD alwayszero
= 0;
1815 if ( (islowerW(*start
) && isalphaW(*start
)) ||
1830 if ( isalphaW(*start
) ||
1832 /* user/password only characters */
1837 /* *extra* characters */
1840 (*start
== L
'\'') ||
1844 /* *safe* characters */
1852 } else if (*start
== L
'%') {
1853 if (isxdigitW(*(start
+1)) &&
1854 isxdigitW(*(start
+2))) {
1866 if (isdigitW(*start
)) {
1877 if (isalnumW(*start
) ||
1879 (*start
== L
'.') ) {
1888 FIXME("unknown type %d\n", type
);
1889 return (LPWSTR
)&alwayszero
;
1891 /* TRACE("scanned %ld characters next char %p<%c>\n",
1892 *size, start, *start); */
1896 /*************************************************************************
1897 * Attempt to parse URL into pieces.
1899 static LONG
URL_ParseUrl(LPCWSTR pszUrl
, WINE_PARSE_URL
*pl
)
1903 memset(pl
, 0, sizeof(WINE_PARSE_URL
));
1904 pl
->pScheme
= pszUrl
;
1905 work
= URL_ScanID(pl
->pScheme
, &pl
->szScheme
, SCHEME
);
1906 if (!*work
|| (*work
!= L
':')) goto ErrorExit
;
1908 if ((*work
!= L
'/') || (*(work
+1) != L
'/')) goto ErrorExit
;
1909 pl
->pUserName
= work
+ 2;
1910 work
= URL_ScanID(pl
->pUserName
, &pl
->szUserName
, USERPASS
);
1911 if (*work
== L
':' ) {
1912 /* parse password */
1914 pl
->pPassword
= work
;
1915 work
= URL_ScanID(pl
->pPassword
, &pl
->szPassword
, USERPASS
);
1916 if (*work
!= L
'@') {
1917 /* what we just parsed must be the hostname and port
1918 * so reset pointers and clear then let it parse */
1919 pl
->szUserName
= pl
->szPassword
= 0;
1920 work
= pl
->pUserName
- 1;
1921 pl
->pUserName
= pl
->pPassword
= 0;
1923 } else if (*work
== L
'@') {
1927 } else if (!*work
|| (*work
== L
'/') || (*work
== L
'.')) {
1928 /* what was parsed was hostname, so reset pointers and let it parse */
1929 pl
->szUserName
= pl
->szPassword
= 0;
1930 work
= pl
->pUserName
- 1;
1931 pl
->pUserName
= pl
->pPassword
= 0;
1932 } else goto ErrorExit
;
1934 /* now start parsing hostname or hostnumber */
1936 pl
->pHostName
= work
;
1937 work
= URL_ScanID(pl
->pHostName
, &pl
->szHostName
, HOST
);
1938 if (*work
== L
':') {
1942 work
= URL_ScanID(pl
->pPort
, &pl
->szPort
, PORT
);
1944 if (*work
== L
'/') {
1945 /* see if query string */
1946 pl
->pQuery
= strchrW(work
, L
'?');
1947 if (pl
->pQuery
) pl
->szQuery
= strlenW(pl
->pQuery
);
1949 TRACE("parse successful: scheme=%p(%ld), user=%p(%ld), pass=%p(%ld), host=%p(%ld), port=%p(%ld), query=%p(%ld)\n",
1950 pl
->pScheme
, pl
->szScheme
,
1951 pl
->pUserName
, pl
->szUserName
,
1952 pl
->pPassword
, pl
->szPassword
,
1953 pl
->pHostName
, pl
->szHostName
,
1954 pl
->pPort
, pl
->szPort
,
1955 pl
->pQuery
, pl
->szQuery
);
1958 FIXME("failed to parse %s\n", debugstr_w(pszUrl
));
1959 return E_INVALIDARG
;
1962 /*************************************************************************
1963 * UrlGetPartA [SHLWAPI.@]
1965 * Retrieve part of a Url.
1968 * pszIn [I] Url to parse
1969 * pszOut [O] Destination for part of pszIn requested
1970 * pcchOut [I/O] Length of pszOut/destination for length of pszOut
1971 * dwPart [I] URL_PART_ enum from "shlwapi.h"
1972 * dwFlags [I] URL_ flags from "shlwapi.h"
1975 * Success: S_OK. pszOut contains the part requested, pcchOut contains its length.
1976 * Failure: An HRESULT error code describing the error.
1978 HRESULT WINAPI
UrlGetPartA(LPCSTR pszIn
, LPSTR pszOut
, LPDWORD pcchOut
,
1979 DWORD dwPart
, DWORD dwFlags
)
1982 DWORD ret
, len
, len2
;
1984 in
= HeapAlloc(GetProcessHeap(), 0,
1985 (2*INTERNET_MAX_URL_LENGTH
) * sizeof(WCHAR
));
1986 out
= in
+ INTERNET_MAX_URL_LENGTH
;
1988 MultiByteToWideChar(0, 0, pszIn
, -1, in
, INTERNET_MAX_URL_LENGTH
);
1990 len
= INTERNET_MAX_URL_LENGTH
;
1991 ret
= UrlGetPartW(in
, out
, &len
, dwPart
, dwFlags
);
1994 HeapFree(GetProcessHeap(), 0, in
);
1998 len2
= WideCharToMultiByte(0, 0, out
, len
, 0, 0, 0, 0);
1999 if (len2
> *pcchOut
) {
2001 HeapFree(GetProcessHeap(), 0, in
);
2004 WideCharToMultiByte(0, 0, out
, len
+1, pszOut
, *pcchOut
, 0, 0);
2006 HeapFree(GetProcessHeap(), 0, in
);
2010 /*************************************************************************
2011 * UrlGetPartW [SHLWAPI.@]
2015 HRESULT WINAPI
UrlGetPartW(LPCWSTR pszIn
, LPWSTR pszOut
, LPDWORD pcchOut
,
2016 DWORD dwPart
, DWORD dwFlags
)
2020 DWORD size
, schsize
;
2021 LPCWSTR addr
, schaddr
;
2024 TRACE("(%s %p %p(%ld) %08lx %08lx)\n",
2025 debugstr_w(pszIn
), pszOut
, pcchOut
, *pcchOut
, dwPart
, dwFlags
);
2027 ret
= URL_ParseUrl(pszIn
, &pl
);
2029 schaddr
= pl
.pScheme
;
2030 schsize
= pl
.szScheme
;
2033 case URL_PART_SCHEME
:
2034 if (!pl
.szScheme
) return E_INVALIDARG
;
2039 case URL_PART_HOSTNAME
:
2040 if (!pl
.szHostName
) return E_INVALIDARG
;
2041 addr
= pl
.pHostName
;
2042 size
= pl
.szHostName
;
2045 case URL_PART_USERNAME
:
2046 if (!pl
.szUserName
) return E_INVALIDARG
;
2047 addr
= pl
.pUserName
;
2048 size
= pl
.szUserName
;
2051 case URL_PART_PASSWORD
:
2052 if (!pl
.szPassword
) return E_INVALIDARG
;
2053 addr
= pl
.pPassword
;
2054 size
= pl
.szPassword
;
2058 if (!pl
.szPort
) return E_INVALIDARG
;
2063 case URL_PART_QUERY
:
2064 if (!pl
.szQuery
) return E_INVALIDARG
;
2070 return E_INVALIDARG
;
2073 if (dwFlags
== URL_PARTFLAG_KEEPSCHEME
) {
2074 if (*pcchOut
< size
+ schsize
+ 2) {
2075 *pcchOut
= size
+ schsize
+ 2;
2078 strncpyW(pszOut
, schaddr
, schsize
);
2079 work
= pszOut
+ schsize
;
2081 strncpyW(work
+1, addr
, size
);
2082 *pcchOut
= size
+ schsize
+ 1;
2087 if (*pcchOut
< size
+ 1) {*pcchOut
= size
+1; return E_POINTER
;}
2088 strncpyW(pszOut
, addr
, size
);
2090 work
= pszOut
+ size
;
2093 TRACE("len=%ld %s\n", *pcchOut
, debugstr_w(pszOut
));
2098 /*************************************************************************
2099 * PathIsURLA [SHLWAPI.@]
2101 * Check if the given path is a Url.
2104 * lpszPath [I] Path to check.
2107 * TRUE if lpszPath is a Url.
2108 * FALSE if lpszPath is NULL or not a Url.
2110 BOOL WINAPI
PathIsURLA(LPCSTR lpstrPath
)
2112 UNKNOWN_SHLWAPI_1 base
;
2115 if (!lpstrPath
|| !*lpstrPath
) return FALSE
;
2118 base
.size
= sizeof(base
);
2119 res1
= ParseURLA(lpstrPath
, &base
);
2120 return (base
.fcncde
> 0);
2123 /*************************************************************************
2124 * PathIsURLW [SHLWAPI.@]
2128 BOOL WINAPI
PathIsURLW(LPCWSTR lpstrPath
)
2130 UNKNOWN_SHLWAPI_2 base
;
2133 if (!lpstrPath
|| !*lpstrPath
) return FALSE
;
2136 base
.size
= sizeof(base
);
2137 res1
= ParseURLW(lpstrPath
, &base
);
2138 return (base
.fcncde
> 0);
2141 /*************************************************************************
2142 * UrlCreateFromPathA [SHLWAPI.@]
2144 * Create a Url from a file path.
2147 * pszPath [I] Path to convert
2148 * pszUrl [O] Destination for the converted Url
2149 * pcchUrl [I/O] Length of pszUrl
2150 * dwReserved [I] Reserved, must be 0
2153 * Success: S_OK. pszUrl contains the converted path.
2154 * Failure: An HRESULT error code.
2156 HRESULT WINAPI
UrlCreateFromPathA(LPCSTR pszPath
, LPSTR pszUrl
, LPDWORD pcchUrl
, DWORD dwReserved
)
2158 DWORD nCharBeforeColon
= 0;
2160 DWORD dwChRequired
= 0;
2161 LPSTR pszNewUrl
= NULL
;
2162 LPCSTR pszConstPointer
= NULL
;
2163 LPSTR pszPointer
= NULL
;
2167 TRACE("(%s, %p, %p, 0x%08lx)\n", debugstr_a(pszPath
), pszUrl
, pcchUrl
, dwReserved
);
2169 /* Validate arguments */
2170 if (dwReserved
!= 0)
2172 FIXME("dwReserved should be 0: 0x%08lx\n", dwReserved
);
2173 return E_INVALIDARG
;
2175 if (!pszUrl
|| !pcchUrl
|| !pszUrl
)
2177 ERR("Invalid argument\n");
2178 return E_INVALIDARG
;
2181 for (pszConstPointer
= pszPath
; *pszConstPointer
; pszConstPointer
++)
2183 if (isalpha(*pszConstPointer
) || isdigit(*pszConstPointer
) ||
2184 *pszConstPointer
== '.' || *pszConstPointer
== '-')
2188 if (*pszConstPointer
== ':') /* then already in URL format, so copy */
2190 dwChRequired
= lstrlenA(pszPath
);
2191 if (dwChRequired
> *pcchUrl
)
2193 *pcchUrl
= dwChRequired
;
2198 *pcchUrl
= dwChRequired
;
2199 StrCpyA(pszUrl
, pszPath
);
2203 /* then must need converting to file: format */
2205 /* Strip off leading slashes */
2206 while (*pszPath
== '\\' || *pszPath
== '/')
2212 dwChRequired
= *pcchUrl
; /* UrlEscape will fill this in with the correct amount */
2213 TRACE("pszUrl: %s\n", debugstr_a(pszPath
));
2214 pszNewUrl
= HeapAlloc(GetProcessHeap(), 0, dwChRequired
+ 1);
2215 ret
= UrlEscapeA(pszPath
, pszNewUrl
, &dwChRequired
, URL_ESCAPE_PERCENT
);
2216 TRACE("ret: 0x%08lx, pszUrl: %s\n", ret
, debugstr_a(pszNewUrl
));
2217 TRACE("%ld\n", dwChRequired
);
2218 if (ret
!= E_POINTER
&& FAILED(ret
))
2220 dwChRequired
+= 5; /* "file:" */
2221 if ((lstrlenA(pszUrl
) > 1) && isalpha(pszUrl
[0]) && (pszUrl
[1] == ':'))
2223 dwChRequired
+= 3; /* "///" */
2229 case 0: /* no slashes */
2231 case 2: /* two slashes */
2238 default: /* three slashes */
2243 if (dwChRequired
> *pcchUrl
)
2245 *pcchUrl
= dwChRequired
; /* Return number of chars required (not including termination) */
2246 StrCpyA(pszUrl
, "file:");
2247 pszPointer
= pszUrl
+ lstrlenA(pszUrl
);
2248 for (i
=0; i
< nSlashes
; i
++)
2253 StrCpyA(pszPointer
, pszNewUrl
);
2254 TRACE("<- %s\n", debugstr_a(pszUrl
));
2258 /*************************************************************************
2259 * UrlCreateFromPathW [SHLWAPI.@]
2261 * See UrlCreateFromPathA.
2263 HRESULT WINAPI
UrlCreateFromPathW(LPCWSTR pszPath
, LPWSTR pszUrl
, LPDWORD pcchUrl
, DWORD dwReserved
)
2265 DWORD nCharBeforeColon
= 0;
2267 DWORD dwChRequired
= 0;
2268 LPWSTR pszNewUrl
= NULL
;
2269 LPCWSTR pszConstPointer
= NULL
;
2270 LPWSTR pszPointer
= NULL
;
2274 TRACE("(%s, %p, %p, 0x%08lx)\n", debugstr_w(pszPath
), pszUrl
, pcchUrl
, dwReserved
);
2276 /* Validate arguments */
2277 if (dwReserved
!= 0)
2278 return E_INVALIDARG
;
2279 if (!pszUrl
|| !pcchUrl
|| !pszUrl
)
2280 return E_INVALIDARG
;
2282 for (pszConstPointer
= pszPath
; *pszConstPointer
; pszConstPointer
++)
2284 if (isalphaW(*pszConstPointer
) || isdigitW(*pszConstPointer
) ||
2285 *pszConstPointer
== '.' || *pszConstPointer
== '-')
2289 if (*pszConstPointer
== ':') /* then already in URL format, so copy */
2291 dwChRequired
= lstrlenW(pszPath
);
2292 *pcchUrl
= dwChRequired
;
2293 if (dwChRequired
> *pcchUrl
)
2297 StrCpyW(pszUrl
, pszPath
);
2301 /* then must need converting to file: format */
2303 /* Strip off leading slashes */
2304 while (*pszPath
== '\\' || *pszPath
== '/')
2310 dwChRequired
= *pcchUrl
; /* UrlEscape will fill this in with the correct amount */
2311 ret
= UrlEscapeW(pszPath
, pszUrl
, &dwChRequired
, URL_ESCAPE_PERCENT
);
2312 if (ret
!= E_POINTER
&& FAILED(ret
))
2314 dwChRequired
+= 5; /* "file:" */
2315 if ((lstrlenW(pszUrl
) > 1) && isalphaW(pszUrl
[0]) && (pszUrl
[1] == ':'))
2317 dwChRequired
+= 3; /* "///" */
2323 case 0: /* no slashes */
2325 case 2: /* two slashes */
2332 default: /* three slashes */
2337 *pcchUrl
= dwChRequired
; /* Return number of chars required (not including termination) */
2338 if (dwChRequired
> *pcchUrl
)
2340 pszNewUrl
= HeapAlloc(GetProcessHeap(), 0, (dwChRequired
+ 1) * sizeof(WCHAR
));
2341 StrCpyW(pszNewUrl
, fileW
);
2342 pszPointer
= pszNewUrl
+ 4;
2345 for (i
=0; i
< nSlashes
; i
++)
2350 StrCpyW(pszPointer
, pszPath
);
2351 StrCpyW(pszUrl
, pszNewUrl
);
2355 /*************************************************************************
2356 * SHAutoComplete [SHLWAPI.@]
2358 * Enable auto-completion for an edit control.
2361 * hwndEdit [I] Handle of control to enable auto-completion for
2362 * dwFlags [I] SHACF_ flags from "shlwapi.h"
2365 * Success: S_OK. Auto-completion is enabled for the control.
2366 * Failure: An HRESULT error code indicating the error.
2368 HRESULT WINAPI
SHAutoComplete(HWND hwndEdit
, DWORD dwFlags
)
2370 FIXME("SHAutoComplete stub\n");
2374 /*************************************************************************
2375 * MLBuildResURLA [SHLWAPI.405]
2377 * Create a Url pointing to a resource in a module.
2380 * lpszLibName [I] Name of the module containing the resource
2381 * hMod [I] Callers module handle
2382 * dwFlags [I] Undocumented flags for loading the module
2383 * lpszRes [I] Resource name
2384 * lpszDest [O] Destination for resulting Url
2385 * dwDestLen [I] Length of lpszDest
2388 * Success: S_OK. lpszDest constains the resource Url.
2389 * Failure: E_INVALIDARG, if any argument is invalid, or
2390 * E_FAIL if dwDestLen is too small.
2392 HRESULT WINAPI
MLBuildResURLA(LPCSTR lpszLibName
, HMODULE hMod
, DWORD dwFlags
,
2393 LPCSTR lpszRes
, LPSTR lpszDest
, DWORD dwDestLen
)
2395 WCHAR szLibName
[MAX_PATH
], szRes
[MAX_PATH
], szDest
[MAX_PATH
];
2399 MultiByteToWideChar(CP_ACP
, 0, lpszLibName
, -1, szLibName
, sizeof(szLibName
)/sizeof(WCHAR
));
2402 MultiByteToWideChar(CP_ACP
, 0, lpszRes
, -1, szRes
, sizeof(szRes
)/sizeof(WCHAR
));
2404 if (dwDestLen
> sizeof(szLibName
)/sizeof(WCHAR
))
2405 dwDestLen
= sizeof(szLibName
)/sizeof(WCHAR
);
2407 hRet
= MLBuildResURLW(lpszLibName
? szLibName
: NULL
, hMod
, dwFlags
,
2408 lpszRes
? szRes
: NULL
, lpszDest
? szDest
: NULL
, dwDestLen
);
2409 if (SUCCEEDED(hRet
) && lpszDest
)
2410 WideCharToMultiByte(CP_ACP
, 0, szDest
, -1, lpszDest
, dwDestLen
, 0, 0);
2415 /*************************************************************************
2416 * MLBuildResURLA [SHLWAPI.406]
2418 * See MLBuildResURLA.
2420 HRESULT WINAPI
MLBuildResURLW(LPCWSTR lpszLibName
, HMODULE hMod
, DWORD dwFlags
,
2421 LPCWSTR lpszRes
, LPWSTR lpszDest
, DWORD dwDestLen
)
2423 static const WCHAR szRes
[] = { 'r','e','s',':','/','/','\0' };
2424 #define szResLen ((sizeof(szRes) - sizeof(WCHAR))/sizeof(WCHAR))
2425 HRESULT hRet
= E_FAIL
;
2427 TRACE("(%s,%p,0x%08lx,%s,%p,%ld)\n", debugstr_w(lpszLibName
), hMod
, dwFlags
,
2428 debugstr_w(lpszRes
), lpszDest
, dwDestLen
);
2430 if (!lpszLibName
|| !hMod
|| hMod
== INVALID_HANDLE_VALUE
|| !lpszRes
||
2431 !lpszDest
|| (dwFlags
&& dwFlags
!= 2))
2432 return E_INVALIDARG
;
2434 if (dwDestLen
>= szResLen
+ 1)
2436 dwDestLen
-= (szResLen
+ 1);
2437 memcpy(lpszDest
, szRes
, sizeof(szRes
));
2439 hMod
= MLLoadLibraryW(lpszLibName
, hMod
, dwFlags
);
2443 WCHAR szBuff
[MAX_PATH
];
2446 len
= GetModuleFileNameW(hMod
, szBuff
, sizeof(szBuff
)/sizeof(WCHAR
));
2447 if (len
&& len
< sizeof(szBuff
)/sizeof(WCHAR
))
2449 DWORD dwPathLen
= strlenW(szBuff
) + 1;
2451 if (dwDestLen
>= dwPathLen
)
2455 dwDestLen
-= dwPathLen
;
2456 memcpy(lpszDest
+ szResLen
, szBuff
, dwPathLen
* sizeof(WCHAR
));
2458 dwResLen
= strlenW(lpszRes
) + 1;
2459 if (dwDestLen
>= dwResLen
+ 1)
2461 lpszDest
[szResLen
+ dwPathLen
+ dwResLen
] = '/';
2462 memcpy(lpszDest
+ szResLen
+ dwPathLen
, lpszRes
, dwResLen
* sizeof(WCHAR
));
2467 MLFreeLibrary(hMod
);