shlwapi: Initialise id field of ConPt object in connection point tests.
[wine] / dlls / shlwapi / url.c
1 /*
2  * Url functions
3  *
4  * Copyright 2000 Huw D M Davies for CodeWeavers.
5  *
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.
10  *
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.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  */
20
21 #include "config.h"
22 #include "wine/port.h"
23 #include <stdarg.h>
24 #include <string.h>
25 #include <stdlib.h>
26 #include "windef.h"
27 #include "winbase.h"
28 #include "winnls.h"
29 #include "winerror.h"
30 #include "wine/unicode.h"
31 #include "wininet.h"
32 #include "winreg.h"
33 #include "winternl.h"
34 #define NO_SHLWAPI_STREAM
35 #include "shlwapi.h"
36 #include "wine/debug.h"
37
38 HMODULE WINAPI MLLoadLibraryW(LPCWSTR,HMODULE,DWORD);
39 BOOL    WINAPI MLFreeLibrary(HMODULE);
40 HRESULT WINAPI MLBuildResURLW(LPCWSTR,HMODULE,DWORD,LPCWSTR,LPWSTR,DWORD);
41
42 WINE_DEFAULT_DEBUG_CHANNEL(shell);
43
44 /* The following schemes were identified in the native version of
45  * SHLWAPI.DLL version 5.50
46  */
47 static const struct {
48     URL_SCHEME  scheme_number;
49     WCHAR scheme_name[12];
50 } shlwapi_schemes[] = {
51   {URL_SCHEME_FTP,        {'f','t','p',0}},
52   {URL_SCHEME_HTTP,       {'h','t','t','p',0}},
53   {URL_SCHEME_GOPHER,     {'g','o','p','h','e','r',0}},
54   {URL_SCHEME_MAILTO,     {'m','a','i','l','t','o',0}},
55   {URL_SCHEME_NEWS,       {'n','e','w','s',0}},
56   {URL_SCHEME_NNTP,       {'n','n','t','p',0}},
57   {URL_SCHEME_TELNET,     {'t','e','l','n','e','t',0}},
58   {URL_SCHEME_WAIS,       {'w','a','i','s',0}},
59   {URL_SCHEME_FILE,       {'f','i','l','e',0}},
60   {URL_SCHEME_MK,         {'m','k',0}},
61   {URL_SCHEME_HTTPS,      {'h','t','t','p','s',0}},
62   {URL_SCHEME_SHELL,      {'s','h','e','l','l',0}},
63   {URL_SCHEME_SNEWS,      {'s','n','e','w','s',0}},
64   {URL_SCHEME_LOCAL,      {'l','o','c','a','l',0}},
65   {URL_SCHEME_JAVASCRIPT, {'j','a','v','a','s','c','r','i','p','t',0}},
66   {URL_SCHEME_VBSCRIPT,   {'v','b','s','c','r','i','p','t',0}},
67   {URL_SCHEME_ABOUT,      {'a','b','o','u','t',0}},
68   {URL_SCHEME_RES,        {'r','e','s',0}},
69 };
70
71 typedef struct {
72     LPCWSTR pScheme;      /* [out] start of scheme                     */
73     DWORD   szScheme;     /* [out] size of scheme (until colon)        */
74     LPCWSTR pUserName;    /* [out] start of Username                   */
75     DWORD   szUserName;   /* [out] size of Username (until ":" or "@") */
76     LPCWSTR pPassword;    /* [out] start of Password                   */
77     DWORD   szPassword;   /* [out] size of Password (until "@")        */
78     LPCWSTR pHostName;    /* [out] start of Hostname                   */
79     DWORD   szHostName;   /* [out] size of Hostname (until ":" or "/") */
80     LPCWSTR pPort;        /* [out] start of Port                       */
81     DWORD   szPort;       /* [out] size of Port (until "/" or eos)     */
82     LPCWSTR pQuery;       /* [out] start of Query                      */
83     DWORD   szQuery;      /* [out] size of Query (until eos)           */
84 } WINE_PARSE_URL;
85
86 typedef enum {
87     SCHEME,
88     HOST,
89     PORT,
90     USERPASS,
91 } WINE_URL_SCAN_TYPE;
92
93 static const CHAR hexDigits[] = "0123456789ABCDEF";
94
95 static const WCHAR fileW[] = {'f','i','l','e','\0'};
96
97 static const unsigned char HashDataLookup[256] = {
98  0x01, 0x0E, 0x6E, 0x19, 0x61, 0xAE, 0x84, 0x77, 0x8A, 0xAA, 0x7D, 0x76, 0x1B,
99  0xE9, 0x8C, 0x33, 0x57, 0xC5, 0xB1, 0x6B, 0xEA, 0xA9, 0x38, 0x44, 0x1E, 0x07,
100  0xAD, 0x49, 0xBC, 0x28, 0x24, 0x41, 0x31, 0xD5, 0x68, 0xBE, 0x39, 0xD3, 0x94,
101  0xDF, 0x30, 0x73, 0x0F, 0x02, 0x43, 0xBA, 0xD2, 0x1C, 0x0C, 0xB5, 0x67, 0x46,
102  0x16, 0x3A, 0x4B, 0x4E, 0xB7, 0xA7, 0xEE, 0x9D, 0x7C, 0x93, 0xAC, 0x90, 0xB0,
103  0xA1, 0x8D, 0x56, 0x3C, 0x42, 0x80, 0x53, 0x9C, 0xF1, 0x4F, 0x2E, 0xA8, 0xC6,
104  0x29, 0xFE, 0xB2, 0x55, 0xFD, 0xED, 0xFA, 0x9A, 0x85, 0x58, 0x23, 0xCE, 0x5F,
105  0x74, 0xFC, 0xC0, 0x36, 0xDD, 0x66, 0xDA, 0xFF, 0xF0, 0x52, 0x6A, 0x9E, 0xC9,
106  0x3D, 0x03, 0x59, 0x09, 0x2A, 0x9B, 0x9F, 0x5D, 0xA6, 0x50, 0x32, 0x22, 0xAF,
107  0xC3, 0x64, 0x63, 0x1A, 0x96, 0x10, 0x91, 0x04, 0x21, 0x08, 0xBD, 0x79, 0x40,
108  0x4D, 0x48, 0xD0, 0xF5, 0x82, 0x7A, 0x8F, 0x37, 0x69, 0x86, 0x1D, 0xA4, 0xB9,
109  0xC2, 0xC1, 0xEF, 0x65, 0xF2, 0x05, 0xAB, 0x7E, 0x0B, 0x4A, 0x3B, 0x89, 0xE4,
110  0x6C, 0xBF, 0xE8, 0x8B, 0x06, 0x18, 0x51, 0x14, 0x7F, 0x11, 0x5B, 0x5C, 0xFB,
111  0x97, 0xE1, 0xCF, 0x15, 0x62, 0x71, 0x70, 0x54, 0xE2, 0x12, 0xD6, 0xC7, 0xBB,
112  0x0D, 0x20, 0x5E, 0xDC, 0xE0, 0xD4, 0xF7, 0xCC, 0xC4, 0x2B, 0xF9, 0xEC, 0x2D,
113  0xF4, 0x6F, 0xB6, 0x99, 0x88, 0x81, 0x5A, 0xD9, 0xCA, 0x13, 0xA5, 0xE7, 0x47,
114  0xE6, 0x8E, 0x60, 0xE3, 0x3E, 0xB3, 0xF6, 0x72, 0xA2, 0x35, 0xA0, 0xD7, 0xCD,
115  0xB4, 0x2F, 0x6D, 0x2C, 0x26, 0x1F, 0x95, 0x87, 0x00, 0xD8, 0x34, 0x3F, 0x17,
116  0x25, 0x45, 0x27, 0x75, 0x92, 0xB8, 0xA3, 0xC8, 0xDE, 0xEB, 0xF8, 0xF3, 0xDB,
117  0x0A, 0x98, 0x83, 0x7B, 0xE5, 0xCB, 0x4C, 0x78, 0xD1 };
118
119 static DWORD get_scheme_code(LPCWSTR scheme, DWORD scheme_len)
120 {
121     unsigned int i;
122
123     for(i=0; i < sizeof(shlwapi_schemes)/sizeof(shlwapi_schemes[0]); i++) {
124         if(scheme_len == strlenW(shlwapi_schemes[i].scheme_name)
125            && !memcmp(scheme, shlwapi_schemes[i].scheme_name, scheme_len*sizeof(WCHAR)))
126             return shlwapi_schemes[i].scheme_number;
127     }
128
129     return URL_SCHEME_UNKNOWN;
130 }
131
132 /*************************************************************************
133  *      @       [SHLWAPI.1]
134  *
135  * Parse a Url into its constituent parts.
136  *
137  * PARAMS
138  *  x [I] Url to parse
139  *  y [O] Undocumented structure holding the parsed information
140  *
141  * RETURNS
142  *  Success: S_OK. y contains the parsed Url details.
143  *  Failure: An HRESULT error code.
144  */
145 HRESULT WINAPI ParseURLA(LPCSTR x, PARSEDURLA *y)
146 {
147     WCHAR scheme[INTERNET_MAX_SCHEME_LENGTH];
148     const char *ptr = x;
149     int len;
150
151     TRACE("%s %p\n", debugstr_a(x), y);
152
153     if(y->cbSize != sizeof(*y))
154         return E_INVALIDARG;
155
156     while(*ptr && (isalnum(*ptr) || *ptr == '-'))
157         ptr++;
158
159     if (*ptr != ':' || ptr <= x+1) {
160         y->pszProtocol = NULL;
161         return 0x80041001;
162     }
163
164     y->pszProtocol = x;
165     y->cchProtocol = ptr-x;
166     y->pszSuffix = ptr+1;
167     y->cchSuffix = strlen(y->pszSuffix);
168
169     len = MultiByteToWideChar(CP_ACP, 0, x, ptr-x,
170             scheme, sizeof(scheme)/sizeof(WCHAR));
171     y->nScheme = get_scheme_code(scheme, len);
172
173     return S_OK;
174 }
175
176 /*************************************************************************
177  *      @       [SHLWAPI.2]
178  *
179  * Unicode version of ParseURLA.
180  */
181 HRESULT WINAPI ParseURLW(LPCWSTR x, PARSEDURLW *y)
182 {
183     const WCHAR *ptr = x;
184
185     TRACE("%s %p\n", debugstr_w(x), y);
186
187     if(y->cbSize != sizeof(*y))
188         return E_INVALIDARG;
189
190     while(*ptr && (isalnumW(*ptr) || *ptr == '-'))
191         ptr++;
192
193     if (*ptr != ':' || ptr <= x+1) {
194         y->pszProtocol = NULL;
195         return 0x80041001;
196     }
197
198     y->pszProtocol = x;
199     y->cchProtocol = ptr-x;
200     y->pszSuffix = ptr+1;
201     y->cchSuffix = strlenW(y->pszSuffix);
202     y->nScheme = get_scheme_code(x, ptr-x);
203
204     return S_OK;
205 }
206
207 /*************************************************************************
208  *        UrlCanonicalizeA     [SHLWAPI.@]
209  *
210  * Canonicalize a Url.
211  *
212  * PARAMS
213  *  pszUrl            [I]   Url to cCanonicalize
214  *  pszCanonicalized  [O]   Destination for converted Url.
215  *  pcchCanonicalized [I/O] Length of pszUrl, destination for length of pszCanonicalized
216  *  dwFlags           [I]   Flags controlling the conversion.
217  *
218  * RETURNS
219  *  Success: S_OK. The pszCanonicalized contains the converted Url.
220  *  Failure: E_POINTER, if *pcchCanonicalized is too small.
221  *
222  * MSDN incorrectly describes the flags for this function. They should be:
223  *|    URL_DONT_ESCAPE_EXTRA_INFO    0x02000000
224  *|    URL_ESCAPE_SPACES_ONLY        0x04000000
225  *|    URL_ESCAPE_PERCENT            0x00001000
226  *|    URL_ESCAPE_UNSAFE             0x10000000
227  *|    URL_UNESCAPE                  0x10000000
228  *|    URL_DONT_SIMPLIFY             0x08000000
229  *|    URL_ESCAPE_SEGMENT_ONLY       0x00002000
230  */
231 HRESULT WINAPI UrlCanonicalizeA(LPCSTR pszUrl, LPSTR pszCanonicalized,
232         LPDWORD pcchCanonicalized, DWORD dwFlags)
233 {
234     LPWSTR base, canonical;
235     HRESULT ret;
236     DWORD   len, len2;
237
238     TRACE("(%s, %p, %p, 0x%08x) *pcchCanonicalized: %d\n", debugstr_a(pszUrl), pszCanonicalized,
239         pcchCanonicalized, dwFlags, pcchCanonicalized ? *pcchCanonicalized : -1);
240
241     if(!pszUrl || !pszCanonicalized || !pcchCanonicalized)
242         return E_INVALIDARG;
243
244     base = HeapAlloc(GetProcessHeap(), 0,
245                               (2*INTERNET_MAX_URL_LENGTH) * sizeof(WCHAR));
246     canonical = base + INTERNET_MAX_URL_LENGTH;
247
248     MultiByteToWideChar(0, 0, pszUrl, -1, base, INTERNET_MAX_URL_LENGTH);
249     len = INTERNET_MAX_URL_LENGTH;
250
251     ret = UrlCanonicalizeW(base, canonical, &len, dwFlags);
252     if (ret != S_OK) {
253         *pcchCanonicalized = len * 2;
254         HeapFree(GetProcessHeap(), 0, base);
255         return ret;
256     }
257
258     len2 = WideCharToMultiByte(0, 0, canonical, -1, 0, 0, 0, 0);
259     if (len2 > *pcchCanonicalized) {
260         *pcchCanonicalized = len2;
261         HeapFree(GetProcessHeap(), 0, base);
262         return E_POINTER;
263     }
264     WideCharToMultiByte(0, 0, canonical, -1, pszCanonicalized, *pcchCanonicalized, 0, 0);
265     *pcchCanonicalized = len;
266     HeapFree(GetProcessHeap(), 0, base);
267     return S_OK;
268 }
269
270 /*************************************************************************
271  *        UrlCanonicalizeW     [SHLWAPI.@]
272  *
273  * See UrlCanonicalizeA.
274  */
275 HRESULT WINAPI UrlCanonicalizeW(LPCWSTR pszUrl, LPWSTR pszCanonicalized,
276                                 LPDWORD pcchCanonicalized, DWORD dwFlags)
277 {
278     HRESULT hr = S_OK;
279     DWORD EscapeFlags;
280     LPWSTR lpszUrlCpy, wk1, wk2, mp, mp2, root;
281     INT state;
282     DWORD nByteLen, nLen, nWkLen;
283     WCHAR slash = '/';
284
285     static const WCHAR wszFile[] = {'f','i','l','e',':'};
286     static const WCHAR wszLocalhost[] = {'l','o','c','a','l','h','o','s','t'};
287
288     TRACE("(%s, %p, %p, 0x%08x) *pcchCanonicalized: %d\n", debugstr_w(pszUrl), pszCanonicalized,
289         pcchCanonicalized, dwFlags, pcchCanonicalized ? *pcchCanonicalized : -1);
290
291     if(!pszUrl || !pszCanonicalized || !pcchCanonicalized)
292         return E_INVALIDARG;
293
294     if(!*pszUrl) {
295         *pszCanonicalized = 0;
296         return S_OK;
297     }
298
299     nByteLen = (strlenW(pszUrl) + 1) * sizeof(WCHAR); /* length in bytes */
300     lpszUrlCpy = HeapAlloc(GetProcessHeap(), 0,
301                            INTERNET_MAX_URL_LENGTH * sizeof(WCHAR));
302
303     if((dwFlags & URL_FILE_USE_PATHURL) && nByteLen >= sizeof(wszFile)
304             && !memcmp(wszFile, pszUrl, sizeof(wszFile)))
305         slash = '\\';
306
307     /*
308      * state =
309      *         0   initial  1,3
310      *         1   have 2[+] alnum  2,3
311      *         2   have scheme (found :)  4,6,3
312      *         3   failed (no location)
313      *         4   have //  5,3
314      *         5   have 1[+] alnum  6,3
315      *         6   have location (found /) save root location
316      */
317
318     wk1 = (LPWSTR)pszUrl;
319     wk2 = lpszUrlCpy;
320     state = 0;
321
322     if(pszUrl[1] == ':') { /* Assume path */
323         static const WCHAR wszFilePrefix[] = {'f','i','l','e',':','/','/','/'};
324
325         memcpy(wk2, wszFilePrefix, sizeof(wszFilePrefix));
326         wk2 += sizeof(wszFilePrefix)/sizeof(WCHAR);
327         if (dwFlags & URL_FILE_USE_PATHURL)
328         {
329             slash = '\\';
330             --wk2;
331         }
332         else
333             dwFlags |= URL_ESCAPE_UNSAFE;
334         state = 5;
335     }
336
337     while (*wk1) {
338         switch (state) {
339         case 0:
340             if (!isalnumW(*wk1)) {state = 3; break;}
341             *wk2++ = *wk1++;
342             if (!isalnumW(*wk1)) {state = 3; break;}
343             *wk2++ = *wk1++;
344             state = 1;
345             break;
346         case 1:
347             *wk2++ = *wk1;
348             if (*wk1++ == ':') state = 2;
349             break;
350         case 2:
351             *wk2++ = *wk1++;
352             if (*wk1 != '/') {state = 6; break;}
353             *wk2++ = *wk1++;
354             if((dwFlags & URL_FILE_USE_PATHURL) && nByteLen >= sizeof(wszLocalhost)
355                         && !memcmp(wszLocalhost, wk1, sizeof(wszLocalhost))){
356                 wk1 += sizeof(wszLocalhost)/sizeof(WCHAR);
357                 while(*wk1 == '\\' && (dwFlags & URL_FILE_USE_PATHURL))
358                     wk1++;
359             }
360             if(*wk1 == '/' && (dwFlags & URL_FILE_USE_PATHURL))
361                 wk1++;
362             state = 4;
363             break;
364         case 3:
365             nWkLen = strlenW(wk1);
366             memcpy(wk2, wk1, (nWkLen + 1) * sizeof(WCHAR));
367             mp = wk2;
368             wk1 += nWkLen;
369             wk2 += nWkLen;
370
371             while(mp < wk2) {
372                 if(*mp == '/' || *mp == '\\')
373                     *mp = slash;
374                 mp++;
375             }
376             break;
377         case 4:
378             if (!isalnumW(*wk1) && (*wk1 != '-') && (*wk1 != '.') && (*wk1 != ':'))
379                 {state = 3; break;}
380             while(isalnumW(*wk1) || (*wk1 == '-') || (*wk1 == '.') || (*wk1 == ':'))
381                 *wk2++ = *wk1++;
382             state = 5;
383             if (!*wk1)
384                 *wk2++ = slash;
385             break;
386         case 5:
387             if (*wk1 != '/' && *wk1 != '\\') {state = 3; break;}
388             while(*wk1 == '/' || *wk1 == '\\') {
389                 *wk2++ = slash;
390                 wk1++;
391             }
392             state = 6;
393             break;
394         case 6:
395             if(dwFlags & URL_DONT_SIMPLIFY) {
396                 state = 3;
397                 break;
398             }
399  
400             /* Now at root location, cannot back up any more. */
401             /* "root" will point at the '/' */
402
403             root = wk2-1;
404             while (*wk1) {
405                 mp = strchrW(wk1, '/');
406                 mp2 = strchrW(wk1, '\\');
407                 if(mp2 && (!mp || mp2 < mp))
408                     mp = mp2;
409                 if (!mp) {
410                     nWkLen = strlenW(wk1);
411                     memcpy(wk2, wk1, (nWkLen + 1) * sizeof(WCHAR));
412                     wk1 += nWkLen;
413                     wk2 += nWkLen;
414                     continue;
415                 }
416                 nLen = mp - wk1;
417                 if(nLen) {
418                     memcpy(wk2, wk1, nLen * sizeof(WCHAR));
419                     wk2 += nLen;
420                     wk1 += nLen;
421                 }
422                 *wk2++ = slash;
423                 wk1++;
424
425                 if (*wk1 == '.') {
426                     TRACE("found '/.'\n");
427                     if (wk1[1] == '/' || wk1[1] == '\\') {
428                         /* case of /./ -> skip the ./ */
429                         wk1 += 2;
430                     }
431                     else if (wk1[1] == '.') {
432                         /* found /..  look for next / */
433                         TRACE("found '/..'\n");
434                         if (wk1[2] == '/' || wk1[2] == '\\' ||wk1[2] == '?'
435                             || wk1[2] == '#' || !wk1[2]) {
436                             /* case /../ -> need to backup wk2 */
437                             TRACE("found '/../'\n");
438                             *(wk2-1) = '\0';  /* set end of string */
439                             mp = strrchrW(root, slash);
440                             if (mp && (mp >= root)) {
441                                 /* found valid backup point */
442                                 wk2 = mp + 1;
443                                 if(wk1[2] != '/' && wk1[2] != '\\')
444                                     wk1 += 2;
445                                 else
446                                     wk1 += 3;
447                             }
448                             else {
449                                 /* did not find point, restore '/' */
450                                 *(wk2-1) = slash;
451                             }
452                         }
453                     }
454                 }
455             }
456             *wk2 = '\0';
457             break;
458         default:
459             FIXME("how did we get here - state=%d\n", state);
460             HeapFree(GetProcessHeap(), 0, lpszUrlCpy);
461             return E_INVALIDARG;
462         }
463         *wk2 = '\0';
464         TRACE("Simplified, orig <%s>, simple <%s>\n",
465               debugstr_w(pszUrl), debugstr_w(lpszUrlCpy));
466     }
467     nLen = lstrlenW(lpszUrlCpy);
468     while ((nLen > 0) && ((lpszUrlCpy[nLen-1] <= ' ')))
469         lpszUrlCpy[--nLen]=0;
470
471     if(dwFlags & (URL_UNESCAPE | URL_FILE_USE_PATHURL))
472         UrlUnescapeW(lpszUrlCpy, NULL, &nLen, URL_UNESCAPE_INPLACE);
473
474     if((EscapeFlags = dwFlags & (URL_ESCAPE_UNSAFE |
475                                  URL_ESCAPE_SPACES_ONLY |
476                                  URL_ESCAPE_PERCENT |
477                                  URL_DONT_ESCAPE_EXTRA_INFO |
478                                  URL_ESCAPE_SEGMENT_ONLY ))) {
479         EscapeFlags &= ~URL_ESCAPE_UNSAFE;
480         hr = UrlEscapeW(lpszUrlCpy, pszCanonicalized, pcchCanonicalized,
481                         EscapeFlags);
482     } else { /* No escaping needed, just copy the string */
483         nLen = lstrlenW(lpszUrlCpy);
484         if(nLen < *pcchCanonicalized)
485             memcpy(pszCanonicalized, lpszUrlCpy, (nLen + 1)*sizeof(WCHAR));
486         else {
487             hr = E_POINTER;
488             nLen++;
489         }
490         *pcchCanonicalized = nLen;
491     }
492
493     HeapFree(GetProcessHeap(), 0, lpszUrlCpy);
494
495     if (hr == S_OK)
496         TRACE("result %s\n", debugstr_w(pszCanonicalized));
497
498     return hr;
499 }
500
501 /*************************************************************************
502  *        UrlCombineA     [SHLWAPI.@]
503  *
504  * Combine two Urls.
505  *
506  * PARAMS
507  *  pszBase      [I] Base Url
508  *  pszRelative  [I] Url to combine with pszBase
509  *  pszCombined  [O] Destination for combined Url
510  *  pcchCombined [O] Destination for length of pszCombined
511  *  dwFlags      [I] URL_ flags from "shlwapi.h"
512  *
513  * RETURNS
514  *  Success: S_OK. pszCombined contains the combined Url, pcchCombined
515  *           contains its length.
516  *  Failure: An HRESULT error code indicating the error.
517  */
518 HRESULT WINAPI UrlCombineA(LPCSTR pszBase, LPCSTR pszRelative,
519                            LPSTR pszCombined, LPDWORD pcchCombined,
520                            DWORD dwFlags)
521 {
522     LPWSTR base, relative, combined;
523     DWORD ret, len, len2;
524
525     TRACE("(base %s, Relative %s, Combine size %d, flags %08x) using W version\n",
526           debugstr_a(pszBase),debugstr_a(pszRelative),
527           pcchCombined?*pcchCombined:0,dwFlags);
528
529     if(!pszBase || !pszRelative || !pcchCombined)
530         return E_INVALIDARG;
531
532     base = HeapAlloc(GetProcessHeap(), 0,
533                               (3*INTERNET_MAX_URL_LENGTH) * sizeof(WCHAR));
534     relative = base + INTERNET_MAX_URL_LENGTH;
535     combined = relative + INTERNET_MAX_URL_LENGTH;
536
537     MultiByteToWideChar(0, 0, pszBase, -1, base, INTERNET_MAX_URL_LENGTH);
538     MultiByteToWideChar(0, 0, pszRelative, -1, relative, INTERNET_MAX_URL_LENGTH);
539     len = *pcchCombined;
540
541     ret = UrlCombineW(base, relative, pszCombined?combined:NULL, &len, dwFlags);
542     if (ret != S_OK) {
543         *pcchCombined = len;
544         HeapFree(GetProcessHeap(), 0, base);
545         return ret;
546     }
547
548     len2 = WideCharToMultiByte(0, 0, combined, len, 0, 0, 0, 0);
549     if (len2 > *pcchCombined) {
550         *pcchCombined = len2;
551         HeapFree(GetProcessHeap(), 0, base);
552         return E_POINTER;
553     }
554     WideCharToMultiByte(0, 0, combined, len+1, pszCombined, (*pcchCombined)+1,
555                         0, 0);
556     *pcchCombined = len2;
557     HeapFree(GetProcessHeap(), 0, base);
558     return S_OK;
559 }
560
561 /*************************************************************************
562  *        UrlCombineW     [SHLWAPI.@]
563  *
564  * See UrlCombineA.
565  */
566 HRESULT WINAPI UrlCombineW(LPCWSTR pszBase, LPCWSTR pszRelative,
567                            LPWSTR pszCombined, LPDWORD pcchCombined,
568                            DWORD dwFlags)
569 {
570     PARSEDURLW base, relative;
571     DWORD myflags, sizeloc = 0;
572     DWORD len, res1, res2, process_case = 0;
573     LPWSTR work, preliminary, mbase, mrelative;
574     static const WCHAR myfilestr[] = {'f','i','l','e',':','/','/','/','\0'};
575     HRESULT ret;
576
577     TRACE("(base %s, Relative %s, Combine size %d, flags %08x)\n",
578           debugstr_w(pszBase),debugstr_w(pszRelative),
579           pcchCombined?*pcchCombined:0,dwFlags);
580
581     if(!pszBase || !pszRelative || !pcchCombined)
582         return E_INVALIDARG;
583
584     base.cbSize = sizeof(base);
585     relative.cbSize = sizeof(relative);
586
587     /* Get space for duplicates of the input and the output */
588     preliminary = HeapAlloc(GetProcessHeap(), 0, (3*INTERNET_MAX_URL_LENGTH) *
589                             sizeof(WCHAR));
590     mbase = preliminary + INTERNET_MAX_URL_LENGTH;
591     mrelative = mbase + INTERNET_MAX_URL_LENGTH;
592     *preliminary = '\0';
593
594     /* Canonicalize the base input prior to looking for the scheme */
595     myflags = dwFlags & (URL_DONT_SIMPLIFY | URL_UNESCAPE);
596     len = INTERNET_MAX_URL_LENGTH;
597     ret = UrlCanonicalizeW(pszBase, mbase, &len, myflags);
598
599     /* Canonicalize the relative input prior to looking for the scheme */
600     len = INTERNET_MAX_URL_LENGTH;
601     ret = UrlCanonicalizeW(pszRelative, mrelative, &len, myflags);
602
603     /* See if the base has a scheme */
604     res1 = ParseURLW(mbase, &base);
605     if (res1) {
606         /* if pszBase has no scheme, then return pszRelative */
607         TRACE("no scheme detected in Base\n");
608         process_case = 1;
609     }
610     else do {
611         /* mk is a special case */
612         if(base.nScheme == URL_SCHEME_MK) {
613             static const WCHAR wsz[] = {':',':',0};
614
615             WCHAR *ptr = strstrW(base.pszSuffix, wsz);
616             if(ptr) {
617                 int delta;
618
619                 ptr += 2;
620                 delta = ptr-base.pszSuffix;
621                 base.cchProtocol += delta;
622                 base.pszSuffix += delta;
623                 base.cchSuffix -= delta;
624             }
625         }else {
626             /* get size of location field (if it exists) */
627             work = (LPWSTR)base.pszSuffix;
628             sizeloc = 0;
629             if (*work++ == '/') {
630                 if (*work++ == '/') {
631                     /* At this point have start of location and
632                      * it ends at next '/' or end of string.
633                      */
634                     while(*work && (*work != '/')) work++;
635                     sizeloc = (DWORD)(work - base.pszSuffix);
636                 }
637             }
638         }
639
640         /* Change .sizep2 to not have the last leaf in it,
641          * Note: we need to start after the location (if it exists)
642          */
643         work = strrchrW((base.pszSuffix+sizeloc), '/');
644         if (work) {
645             len = (DWORD)(work - base.pszSuffix + 1);
646             base.cchSuffix = len;
647         }
648
649         /*
650          * At this point:
651          *    .pszSuffix   points to location (starting with '//')
652          *    .cchSuffix   length of location (above) and rest less the last
653          *                 leaf (if any)
654          *    sizeloc   length of location (above) up to but not including
655          *              the last '/'
656          */
657
658         res2 = ParseURLW(mrelative, &relative);
659         if (res2) {
660             /* no scheme in pszRelative */
661             TRACE("no scheme detected in Relative\n");
662             relative.pszSuffix = mrelative;  /* case 3,4,5 depends on this */
663             relative.cchSuffix = strlenW(mrelative);
664             if (*pszRelative  == ':') {
665                 /* case that is either left alone or uses pszBase */
666                 if (dwFlags & URL_PLUGGABLE_PROTOCOL) {
667                     process_case = 5;
668                     break;
669                 }
670                 process_case = 1;
671                 break;
672             }
673             if (isalnum(*mrelative) && (*(mrelative + 1) == ':')) {
674                 /* case that becomes "file:///" */
675                 strcpyW(preliminary, myfilestr);
676                 process_case = 1;
677                 break;
678             }
679             if ((*mrelative == '/') && (*(mrelative+1) == '/')) {
680                 /* pszRelative has location and rest */
681                 process_case = 3;
682                 break;
683             }
684             if (*mrelative == '/') {
685                 /* case where pszRelative is root to location */
686                 process_case = 4;
687                 break;
688             }
689             process_case = (*base.pszSuffix == '/' || base.nScheme == URL_SCHEME_MK) ? 5 : 3;
690             break;
691         }
692
693         /* handle cases where pszRelative has scheme */
694         if ((base.cchProtocol == relative.cchProtocol) &&
695             (strncmpW(base.pszProtocol, relative.pszProtocol, base.cchProtocol) == 0)) {
696
697             /* since the schemes are the same */
698             if ((*relative.pszSuffix == '/') && (*(relative.pszSuffix+1) == '/')) {
699                 /* case where pszRelative replaces location and following */
700                 process_case = 3;
701                 break;
702             }
703             if (*relative.pszSuffix == '/') {
704                 /* case where pszRelative is root to location */
705                 process_case = 4;
706                 break;
707             }
708             /* replace either just location if base's location starts with a
709              * slash or otherwise everything */
710             process_case = (*base.pszSuffix == '/') ? 5 : 1;
711             break;
712         }
713         if ((*relative.pszSuffix == '/') && (*(relative.pszSuffix+1) == '/')) {
714             /* case where pszRelative replaces scheme, location,
715              * and following and handles PLUGGABLE
716              */
717             process_case = 2;
718             break;
719         }
720         process_case = 1;
721         break;
722     } while(FALSE); /* a little trick to allow easy exit from nested if's */
723
724     ret = S_OK;
725     switch (process_case) {
726
727     case 1:  /*
728               * Return pszRelative appended to what ever is in pszCombined,
729               * (which may the string "file:///"
730               */
731         strcatW(preliminary, mrelative);
732         break;
733
734     case 2:  /* case where pszRelative replaces scheme, and location */
735         strcpyW(preliminary, mrelative);
736         break;
737
738     case 3:  /*
739               * Return the pszBase scheme with pszRelative. Basically
740               * keeps the scheme and replaces the domain and following.
741               */
742         memcpy(preliminary, base.pszProtocol, (base.cchProtocol + 1)*sizeof(WCHAR));
743         work = preliminary + base.cchProtocol + 1;
744         strcpyW(work, relative.pszSuffix);
745         break;
746
747     case 4:  /*
748               * Return the pszBase scheme and location but everything
749               * after the location is pszRelative. (Replace document
750               * from root on.)
751               */
752         memcpy(preliminary, base.pszProtocol, (base.cchProtocol+1+sizeloc)*sizeof(WCHAR));
753         work = preliminary + base.cchProtocol + 1 + sizeloc;
754         if (dwFlags & URL_PLUGGABLE_PROTOCOL)
755             *(work++) = '/';
756         strcpyW(work, relative.pszSuffix);
757         break;
758
759     case 5:  /*
760               * Return the pszBase without its document (if any) and
761               * append pszRelative after its scheme.
762               */
763         memcpy(preliminary, base.pszProtocol,
764                (base.cchProtocol+1+base.cchSuffix)*sizeof(WCHAR));
765         work = preliminary + base.cchProtocol+1+base.cchSuffix - 1;
766         if (*work++ != '/')
767             *(work++) = '/';
768         strcpyW(work, relative.pszSuffix);
769         break;
770
771     default:
772         FIXME("How did we get here????? process_case=%d\n", process_case);
773         ret = E_INVALIDARG;
774     }
775
776     if (ret == S_OK) {
777         /* Reuse mrelative as temp storage as its already allocated and not needed anymore */
778         ret = UrlCanonicalizeW(preliminary, mrelative, pcchCombined, (dwFlags & ~URL_FILE_USE_PATHURL));
779         if(SUCCEEDED(ret) && pszCombined) {
780             lstrcpyW(pszCombined, mrelative);
781         }
782         TRACE("return-%d len=%d, %s\n",
783               process_case, *pcchCombined, debugstr_w(pszCombined));
784     }
785     HeapFree(GetProcessHeap(), 0, preliminary);
786     return ret;
787 }
788
789 /*************************************************************************
790  *      UrlEscapeA      [SHLWAPI.@]
791  */
792
793 HRESULT WINAPI UrlEscapeA(
794         LPCSTR pszUrl,
795         LPSTR pszEscaped,
796         LPDWORD pcchEscaped,
797         DWORD dwFlags)
798 {
799     WCHAR bufW[INTERNET_MAX_URL_LENGTH];
800     WCHAR *escapedW = bufW;
801     UNICODE_STRING urlW;
802     HRESULT ret;
803     DWORD lenW = sizeof(bufW)/sizeof(WCHAR), lenA;
804
805     if (!pszEscaped || !pcchEscaped || !*pcchEscaped)
806         return E_INVALIDARG;
807
808     if(!RtlCreateUnicodeStringFromAsciiz(&urlW, pszUrl))
809         return E_INVALIDARG;
810     if((ret = UrlEscapeW(urlW.Buffer, escapedW, &lenW, dwFlags)) == E_POINTER) {
811         escapedW = HeapAlloc(GetProcessHeap(), 0, lenW * sizeof(WCHAR));
812         ret = UrlEscapeW(urlW.Buffer, escapedW, &lenW, dwFlags);
813     }
814     if(ret == S_OK) {
815         RtlUnicodeToMultiByteSize(&lenA, escapedW, lenW * sizeof(WCHAR));
816         if(*pcchEscaped > lenA) {
817             RtlUnicodeToMultiByteN(pszEscaped, *pcchEscaped - 1, &lenA, escapedW, lenW * sizeof(WCHAR));
818             pszEscaped[lenA] = 0;
819             *pcchEscaped = lenA;
820         } else {
821             *pcchEscaped = lenA + 1;
822             ret = E_POINTER;
823         }
824     }
825     if(escapedW != bufW) HeapFree(GetProcessHeap(), 0, escapedW);
826     RtlFreeUnicodeString(&urlW);
827     return ret;
828 }
829
830 #define WINE_URL_BASH_AS_SLASH    0x01
831 #define WINE_URL_COLLAPSE_SLASHES 0x02
832 #define WINE_URL_ESCAPE_SLASH     0x04
833 #define WINE_URL_ESCAPE_HASH      0x08
834 #define WINE_URL_ESCAPE_QUESTION  0x10
835 #define WINE_URL_STOP_ON_HASH     0x20
836 #define WINE_URL_STOP_ON_QUESTION 0x40
837
838 static inline BOOL URL_NeedEscapeW(WCHAR ch, DWORD dwFlags, DWORD int_flags)
839 {
840
841     if (isalnumW(ch))
842         return FALSE;
843
844     if(dwFlags & URL_ESCAPE_SPACES_ONLY) {
845         if(ch == ' ')
846             return TRUE;
847         else
848             return FALSE;
849     }
850
851     if ((dwFlags & URL_ESCAPE_PERCENT) && (ch == '%'))
852         return TRUE;
853
854     if (ch <= 31 || ch >= 127)
855         return TRUE;
856
857     else {
858         switch (ch) {
859         case ' ':
860         case '<':
861         case '>':
862         case '\"':
863         case '{':
864         case '}':
865         case '|':
866         case '\\':
867         case '^':
868         case ']':
869         case '[':
870         case '`':
871         case '&':
872             return TRUE;
873
874         case '/':
875             if (int_flags & WINE_URL_ESCAPE_SLASH) return TRUE;
876             return FALSE;
877
878         case '?':
879             if (int_flags & WINE_URL_ESCAPE_QUESTION) return TRUE;
880             return FALSE;
881
882         case '#':
883             if (int_flags & WINE_URL_ESCAPE_HASH) return TRUE;
884             return FALSE;
885
886         default:
887             return FALSE;
888         }
889     }
890 }
891
892
893 /*************************************************************************
894  *      UrlEscapeW      [SHLWAPI.@]
895  *
896  * Converts unsafe characters in a Url into escape sequences.
897  *
898  * PARAMS
899  *  pszUrl      [I]   Url to modify
900  *  pszEscaped  [O]   Destination for modified Url
901  *  pcchEscaped [I/O] Length of pszUrl, destination for length of pszEscaped
902  *  dwFlags     [I]   URL_ flags from "shlwapi.h"
903  *
904  * RETURNS
905  *  Success: S_OK. pszEscaped contains the escaped Url, pcchEscaped
906  *           contains its length.
907  *  Failure: E_POINTER, if pszEscaped is not large enough. In this case
908  *           pcchEscaped is set to the required length.
909  *
910  * Converts unsafe characters into their escape sequences.
911  *
912  * NOTES
913  * - By default this function stops converting at the first '?' or
914  *  '#' character.
915  * - If dwFlags contains URL_ESCAPE_SPACES_ONLY then only spaces are
916  *   converted, but the conversion continues past a '?' or '#'.
917  * - Note that this function did not work well (or at all) in shlwapi version 4.
918  *
919  * BUGS
920  *  Only the following flags are implemented:
921  *|     URL_ESCAPE_SPACES_ONLY
922  *|     URL_DONT_ESCAPE_EXTRA_INFO
923  *|     URL_ESCAPE_SEGMENT_ONLY
924  *|     URL_ESCAPE_PERCENT
925  */
926 HRESULT WINAPI UrlEscapeW(
927         LPCWSTR pszUrl,
928         LPWSTR pszEscaped,
929         LPDWORD pcchEscaped,
930         DWORD dwFlags)
931 {
932     LPCWSTR src;
933     DWORD needed = 0, ret;
934     BOOL stop_escaping = FALSE;
935     WCHAR next[5], *dst = pszEscaped;
936     INT len;
937     PARSEDURLW parsed_url;
938     DWORD int_flags;
939     DWORD slashes = 0;
940     static const WCHAR localhost[] = {'l','o','c','a','l','h','o','s','t',0};
941
942     TRACE("(%s %p %p 0x%08x)\n", debugstr_w(pszUrl), pszEscaped,
943           pcchEscaped, dwFlags);
944
945     if(!pszUrl || !pcchEscaped)
946         return E_INVALIDARG;
947
948     if(dwFlags & ~(URL_ESCAPE_SPACES_ONLY |
949                    URL_ESCAPE_SEGMENT_ONLY |
950                    URL_DONT_ESCAPE_EXTRA_INFO |
951                    URL_ESCAPE_PERCENT))
952         FIXME("Unimplemented flags: %08x\n", dwFlags);
953
954     /* fix up flags */
955     if (dwFlags & URL_ESCAPE_SPACES_ONLY)
956         /* if SPACES_ONLY specified, reset the other controls */
957         dwFlags &= ~(URL_DONT_ESCAPE_EXTRA_INFO |
958                      URL_ESCAPE_PERCENT |
959                      URL_ESCAPE_SEGMENT_ONLY);
960
961     else
962         /* if SPACES_ONLY *not* specified the assume DONT_ESCAPE_EXTRA_INFO */
963         dwFlags |= URL_DONT_ESCAPE_EXTRA_INFO;
964
965
966     int_flags = 0;
967     if(dwFlags & URL_ESCAPE_SEGMENT_ONLY) {
968         int_flags = WINE_URL_ESCAPE_QUESTION | WINE_URL_ESCAPE_HASH | WINE_URL_ESCAPE_SLASH;
969     } else {
970         parsed_url.cbSize = sizeof(parsed_url);
971         if(ParseURLW(pszUrl, &parsed_url) != S_OK)
972             parsed_url.nScheme = URL_SCHEME_INVALID;
973
974         TRACE("scheme = %d (%s)\n", parsed_url.nScheme, debugstr_wn(parsed_url.pszProtocol, parsed_url.cchProtocol));
975
976         if(dwFlags & URL_DONT_ESCAPE_EXTRA_INFO)
977             int_flags = WINE_URL_STOP_ON_HASH | WINE_URL_STOP_ON_QUESTION;
978
979         switch(parsed_url.nScheme) {
980         case URL_SCHEME_FILE:
981             int_flags |= WINE_URL_BASH_AS_SLASH | WINE_URL_COLLAPSE_SLASHES | WINE_URL_ESCAPE_HASH;
982             int_flags &= ~WINE_URL_STOP_ON_HASH;
983             break;
984
985         case URL_SCHEME_HTTP:
986         case URL_SCHEME_HTTPS:
987             int_flags |= WINE_URL_BASH_AS_SLASH;
988             if(parsed_url.pszSuffix[0] != '/' && parsed_url.pszSuffix[0] != '\\')
989                 int_flags |= WINE_URL_ESCAPE_SLASH;
990             break;
991
992         case URL_SCHEME_MAILTO:
993             int_flags |= WINE_URL_ESCAPE_SLASH | WINE_URL_ESCAPE_QUESTION | WINE_URL_ESCAPE_HASH;
994             int_flags &= ~(WINE_URL_STOP_ON_QUESTION | WINE_URL_STOP_ON_HASH);
995             break;
996
997         case URL_SCHEME_INVALID:
998             break;
999
1000         case URL_SCHEME_FTP:
1001         default:
1002             if(parsed_url.pszSuffix[0] != '/')
1003                 int_flags |= WINE_URL_ESCAPE_SLASH;
1004             break;
1005         }
1006     }
1007
1008     for(src = pszUrl; *src; ) {
1009         WCHAR cur = *src;
1010         len = 0;
1011         
1012         if((int_flags & WINE_URL_COLLAPSE_SLASHES) && src == pszUrl + parsed_url.cchProtocol + 1) {
1013             int localhost_len = sizeof(localhost)/sizeof(WCHAR) - 1;
1014             while(cur == '/' || cur == '\\') {
1015                 slashes++;
1016                 cur = *++src;
1017             }
1018             if(slashes == 2 && !strncmpiW(src, localhost, localhost_len)) { /* file://localhost/ -> file:/// */
1019                 if(*(src + localhost_len) == '/' || *(src + localhost_len) == '\\')
1020                 src += localhost_len + 1;
1021                 slashes = 3;
1022             }
1023
1024             switch(slashes) {
1025             case 1:
1026             case 3:
1027                 next[0] = next[1] = next[2] = '/';
1028                 len = 3;
1029                 break;
1030             case 0:
1031                 len = 0;
1032                 break;
1033             default:
1034                 next[0] = next[1] = '/';
1035                 len = 2;
1036                 break;
1037             }
1038         }
1039         if(len == 0) {
1040
1041             if(cur == '#' && (int_flags & WINE_URL_STOP_ON_HASH))
1042                 stop_escaping = TRUE;
1043
1044             if(cur == '?' && (int_flags & WINE_URL_STOP_ON_QUESTION))
1045                 stop_escaping = TRUE;
1046
1047             if(cur == '\\' && (int_flags & WINE_URL_BASH_AS_SLASH) && !stop_escaping) cur = '/';
1048
1049             if(URL_NeedEscapeW(cur, dwFlags, int_flags) && stop_escaping == FALSE) {
1050                 next[0] = '%';
1051                 next[1] = hexDigits[(cur >> 4) & 0xf];
1052                 next[2] = hexDigits[cur & 0xf];
1053                 len = 3;
1054             } else {
1055                 next[0] = cur;
1056                 len = 1;
1057             }
1058             src++;
1059         }
1060
1061         if(needed + len <= *pcchEscaped) {
1062             memcpy(dst, next, len*sizeof(WCHAR));
1063             dst += len;
1064         }
1065         needed += len;
1066     }
1067
1068     if(needed < *pcchEscaped) {
1069         *dst = '\0';
1070         ret = S_OK;
1071     } else {
1072         needed++; /* add one for the '\0' */
1073         ret = E_POINTER;
1074     }
1075     *pcchEscaped = needed;
1076     return ret;
1077 }
1078
1079
1080 /*************************************************************************
1081  *      UrlUnescapeA    [SHLWAPI.@]
1082  *
1083  * Converts Url escape sequences back to ordinary characters.
1084  *
1085  * PARAMS
1086  *  pszUrl        [I/O]  Url to convert
1087  *  pszUnescaped  [O]    Destination for converted Url
1088  *  pcchUnescaped [I/O]  Size of output string
1089  *  dwFlags       [I]    URL_ESCAPE_ Flags from "shlwapi.h"
1090  *
1091  * RETURNS
1092  *  Success: S_OK. The converted value is in pszUnescaped, or in pszUrl if
1093  *           dwFlags includes URL_ESCAPE_INPLACE.
1094  *  Failure: E_POINTER if the converted Url is bigger than pcchUnescaped. In
1095  *           this case pcchUnescaped is set to the size required.
1096  * NOTES
1097  *  If dwFlags includes URL_DONT_ESCAPE_EXTRA_INFO, the conversion stops at
1098  *  the first occurrence of either a '?' or '#' character.
1099  */
1100 HRESULT WINAPI UrlUnescapeA(
1101         LPSTR pszUrl,
1102         LPSTR pszUnescaped,
1103         LPDWORD pcchUnescaped,
1104         DWORD dwFlags)
1105 {
1106     char *dst, next;
1107     LPCSTR src;
1108     HRESULT ret;
1109     DWORD needed;
1110     BOOL stop_unescaping = FALSE;
1111
1112     TRACE("(%s, %p, %p, 0x%08x)\n", debugstr_a(pszUrl), pszUnescaped,
1113           pcchUnescaped, dwFlags);
1114
1115     if (!pszUrl) return E_INVALIDARG;
1116
1117     if(dwFlags & URL_UNESCAPE_INPLACE)
1118         dst = pszUrl;
1119     else
1120     {
1121         if (!pszUnescaped || !pcchUnescaped) return E_INVALIDARG;
1122         dst = pszUnescaped;
1123     }
1124
1125     for(src = pszUrl, needed = 0; *src; src++, needed++) {
1126         if(dwFlags & URL_DONT_UNESCAPE_EXTRA_INFO &&
1127            (*src == '#' || *src == '?')) {
1128             stop_unescaping = TRUE;
1129             next = *src;
1130         } else if(*src == '%' && isxdigit(*(src + 1)) && isxdigit(*(src + 2))
1131                   && stop_unescaping == FALSE) {
1132             INT ih;
1133             char buf[3];
1134             memcpy(buf, src + 1, 2);
1135             buf[2] = '\0';
1136             ih = strtol(buf, NULL, 16);
1137             next = (CHAR) ih;
1138             src += 2; /* Advance to end of escape */
1139         } else
1140             next = *src;
1141
1142         if(dwFlags & URL_UNESCAPE_INPLACE || needed < *pcchUnescaped)
1143             *dst++ = next;
1144     }
1145
1146     if(dwFlags & URL_UNESCAPE_INPLACE || needed < *pcchUnescaped) {
1147         *dst = '\0';
1148         ret = S_OK;
1149     } else {
1150         needed++; /* add one for the '\0' */
1151         ret = E_POINTER;
1152     }
1153     if(!(dwFlags & URL_UNESCAPE_INPLACE))
1154         *pcchUnescaped = needed;
1155
1156     if (ret == S_OK) {
1157         TRACE("result %s\n", (dwFlags & URL_UNESCAPE_INPLACE) ?
1158               debugstr_a(pszUrl) : debugstr_a(pszUnescaped));
1159     }
1160
1161     return ret;
1162 }
1163
1164 /*************************************************************************
1165  *      UrlUnescapeW    [SHLWAPI.@]
1166  *
1167  * See UrlUnescapeA.
1168  */
1169 HRESULT WINAPI UrlUnescapeW(
1170         LPWSTR pszUrl,
1171         LPWSTR pszUnescaped,
1172         LPDWORD pcchUnescaped,
1173         DWORD dwFlags)
1174 {
1175     WCHAR *dst, next;
1176     LPCWSTR src;
1177     HRESULT ret;
1178     DWORD needed;
1179     BOOL stop_unescaping = FALSE;
1180
1181     TRACE("(%s, %p, %p, 0x%08x)\n", debugstr_w(pszUrl), pszUnescaped,
1182           pcchUnescaped, dwFlags);
1183
1184     if(!pszUrl) return E_INVALIDARG;
1185
1186     if(dwFlags & URL_UNESCAPE_INPLACE)
1187         dst = pszUrl;
1188     else
1189     {
1190         if (!pszUnescaped || !pcchUnescaped) return E_INVALIDARG;
1191         dst = pszUnescaped;
1192     }
1193
1194     for(src = pszUrl, needed = 0; *src; src++, needed++) {
1195         if(dwFlags & URL_DONT_UNESCAPE_EXTRA_INFO &&
1196            (*src == '#' || *src == '?')) {
1197             stop_unescaping = TRUE;
1198             next = *src;
1199         } else if(*src == '%' && isxdigitW(*(src + 1)) && isxdigitW(*(src + 2))
1200                   && stop_unescaping == FALSE) {
1201             INT ih;
1202             WCHAR buf[5] = {'0','x',0};
1203             memcpy(buf + 2, src + 1, 2*sizeof(WCHAR));
1204             buf[4] = 0;
1205             StrToIntExW(buf, STIF_SUPPORT_HEX, &ih);
1206             next = (WCHAR) ih;
1207             src += 2; /* Advance to end of escape */
1208         } else
1209             next = *src;
1210
1211         if(dwFlags & URL_UNESCAPE_INPLACE || needed < *pcchUnescaped)
1212             *dst++ = next;
1213     }
1214
1215     if(dwFlags & URL_UNESCAPE_INPLACE || needed < *pcchUnescaped) {
1216         *dst = '\0';
1217         ret = S_OK;
1218     } else {
1219         needed++; /* add one for the '\0' */
1220         ret = E_POINTER;
1221     }
1222     if(!(dwFlags & URL_UNESCAPE_INPLACE))
1223         *pcchUnescaped = needed;
1224
1225     if (ret == S_OK) {
1226         TRACE("result %s\n", (dwFlags & URL_UNESCAPE_INPLACE) ?
1227               debugstr_w(pszUrl) : debugstr_w(pszUnescaped));
1228     }
1229
1230     return ret;
1231 }
1232
1233 /*************************************************************************
1234  *      UrlGetLocationA         [SHLWAPI.@]
1235  *
1236  * Get the location from a Url.
1237  *
1238  * PARAMS
1239  *  pszUrl [I] Url to get the location from
1240  *
1241  * RETURNS
1242  *  A pointer to the start of the location in pszUrl, or NULL if there is
1243  *  no location.
1244  *
1245  * NOTES
1246  *  - MSDN erroneously states that "The location is the segment of the Url
1247  *    starting with a '?' or '#' character". Neither V4 nor V5 of shlwapi.dll
1248  *    stop at '?' and always return a NULL in this case.
1249  *  - MSDN also erroneously states that "If a file URL has a query string,
1250  *    the returned string is the query string". In all tested cases, if the
1251  *    Url starts with "fi" then a NULL is returned. V5 gives the following results:
1252  *|       Result   Url
1253  *|       ------   ---
1254  *|       NULL     file://aa/b/cd#hohoh
1255  *|       #hohoh   http://aa/b/cd#hohoh
1256  *|       NULL     fi://aa/b/cd#hohoh
1257  *|       #hohoh   ff://aa/b/cd#hohoh
1258  */
1259 LPCSTR WINAPI UrlGetLocationA(
1260         LPCSTR pszUrl)
1261 {
1262     PARSEDURLA base;
1263     DWORD res1;
1264
1265     base.cbSize = sizeof(base);
1266     res1 = ParseURLA(pszUrl, &base);
1267     if (res1) return NULL;  /* invalid scheme */
1268
1269     /* if scheme is file: then never return pointer */
1270     if (strncmp(base.pszProtocol, "file", min(4,base.cchProtocol)) == 0) return NULL;
1271
1272     /* Look for '#' and return its addr */
1273     return strchr(base.pszSuffix, '#');
1274 }
1275
1276 /*************************************************************************
1277  *      UrlGetLocationW         [SHLWAPI.@]
1278  *
1279  * See UrlGetLocationA.
1280  */
1281 LPCWSTR WINAPI UrlGetLocationW(
1282         LPCWSTR pszUrl)
1283 {
1284     PARSEDURLW base;
1285     DWORD res1;
1286
1287     base.cbSize = sizeof(base);
1288     res1 = ParseURLW(pszUrl, &base);
1289     if (res1) return NULL;  /* invalid scheme */
1290
1291     /* if scheme is file: then never return pointer */
1292     if (strncmpW(base.pszProtocol, fileW, min(4,base.cchProtocol)) == 0) return NULL;
1293
1294     /* Look for '#' and return its addr */
1295     return strchrW(base.pszSuffix, '#');
1296 }
1297
1298 /*************************************************************************
1299  *      UrlCompareA     [SHLWAPI.@]
1300  *
1301  * Compare two Urls.
1302  *
1303  * PARAMS
1304  *  pszUrl1      [I] First Url to compare
1305  *  pszUrl2      [I] Url to compare to pszUrl1
1306  *  fIgnoreSlash [I] TRUE = compare only up to a final slash
1307  *
1308  * RETURNS
1309  *  less than zero, zero, or greater than zero indicating pszUrl2 is greater
1310  *  than, equal to, or less than pszUrl1 respectively.
1311  */
1312 INT WINAPI UrlCompareA(
1313         LPCSTR pszUrl1,
1314         LPCSTR pszUrl2,
1315         BOOL fIgnoreSlash)
1316 {
1317     INT ret, len, len1, len2;
1318
1319     if (!fIgnoreSlash)
1320         return strcmp(pszUrl1, pszUrl2);
1321     len1 = strlen(pszUrl1);
1322     if (pszUrl1[len1-1] == '/') len1--;
1323     len2 = strlen(pszUrl2);
1324     if (pszUrl2[len2-1] == '/') len2--;
1325     if (len1 == len2)
1326         return strncmp(pszUrl1, pszUrl2, len1);
1327     len = min(len1, len2);
1328     ret = strncmp(pszUrl1, pszUrl2, len);
1329     if (ret) return ret;
1330     if (len1 > len2) return 1;
1331     return -1;
1332 }
1333
1334 /*************************************************************************
1335  *      UrlCompareW     [SHLWAPI.@]
1336  *
1337  * See UrlCompareA.
1338  */
1339 INT WINAPI UrlCompareW(
1340         LPCWSTR pszUrl1,
1341         LPCWSTR pszUrl2,
1342         BOOL fIgnoreSlash)
1343 {
1344     INT ret;
1345     size_t len, len1, len2;
1346
1347     if (!fIgnoreSlash)
1348         return strcmpW(pszUrl1, pszUrl2);
1349     len1 = strlenW(pszUrl1);
1350     if (pszUrl1[len1-1] == '/') len1--;
1351     len2 = strlenW(pszUrl2);
1352     if (pszUrl2[len2-1] == '/') len2--;
1353     if (len1 == len2)
1354         return strncmpW(pszUrl1, pszUrl2, len1);
1355     len = min(len1, len2);
1356     ret = strncmpW(pszUrl1, pszUrl2, len);
1357     if (ret) return ret;
1358     if (len1 > len2) return 1;
1359     return -1;
1360 }
1361
1362 /*************************************************************************
1363  *      HashData        [SHLWAPI.@]
1364  *
1365  * Hash an input block into a variable sized digest.
1366  *
1367  * PARAMS
1368  *  lpSrc    [I] Input block
1369  *  nSrcLen  [I] Length of lpSrc
1370  *  lpDest   [I] Output for hash digest
1371  *  nDestLen [I] Length of lpDest
1372  *
1373  * RETURNS
1374  *  Success: TRUE. lpDest is filled with the computed hash value.
1375  *  Failure: FALSE, if any argument is invalid.
1376  */
1377 HRESULT WINAPI HashData(const unsigned char *lpSrc, DWORD nSrcLen,
1378                      unsigned char *lpDest, DWORD nDestLen)
1379 {
1380   INT srcCount = nSrcLen - 1, destCount = nDestLen - 1;
1381
1382   if (IsBadReadPtr(lpSrc, nSrcLen) ||
1383       IsBadWritePtr(lpDest, nDestLen))
1384     return E_INVALIDARG;
1385
1386   while (destCount >= 0)
1387   {
1388     lpDest[destCount] = (destCount & 0xff);
1389     destCount--;
1390   }
1391
1392   while (srcCount >= 0)
1393   {
1394     destCount = nDestLen - 1;
1395     while (destCount >= 0)
1396     {
1397       lpDest[destCount] = HashDataLookup[lpSrc[srcCount] ^ lpDest[destCount]];
1398       destCount--;
1399     }
1400     srcCount--;
1401   }
1402   return S_OK;
1403 }
1404
1405 /*************************************************************************
1406  *      UrlHashA        [SHLWAPI.@]
1407  *
1408  * Produce a Hash from a Url.
1409  *
1410  * PARAMS
1411  *  pszUrl   [I] Url to hash
1412  *  lpDest   [O] Destinationh for hash
1413  *  nDestLen [I] Length of lpDest
1414  * 
1415  * RETURNS
1416  *  Success: S_OK. lpDest is filled with the computed hash value.
1417  *  Failure: E_INVALIDARG, if any argument is invalid.
1418  */
1419 HRESULT WINAPI UrlHashA(LPCSTR pszUrl, unsigned char *lpDest, DWORD nDestLen)
1420 {
1421   if (IsBadStringPtrA(pszUrl, -1) || IsBadWritePtr(lpDest, nDestLen))
1422     return E_INVALIDARG;
1423
1424   HashData((const BYTE*)pszUrl, (int)strlen(pszUrl), lpDest, nDestLen);
1425   return S_OK;
1426 }
1427
1428 /*************************************************************************
1429  * UrlHashW     [SHLWAPI.@]
1430  *
1431  * See UrlHashA.
1432  */
1433 HRESULT WINAPI UrlHashW(LPCWSTR pszUrl, unsigned char *lpDest, DWORD nDestLen)
1434 {
1435   char szUrl[MAX_PATH];
1436
1437   TRACE("(%s,%p,%d)\n",debugstr_w(pszUrl), lpDest, nDestLen);
1438
1439   if (IsBadStringPtrW(pszUrl, -1) || IsBadWritePtr(lpDest, nDestLen))
1440     return E_INVALIDARG;
1441
1442   /* Win32 hashes the data as an ASCII string, presumably so that both A+W
1443    * return the same digests for the same URL.
1444    */
1445   WideCharToMultiByte(0, 0, pszUrl, -1, szUrl, MAX_PATH, 0, 0);
1446   HashData((const BYTE*)szUrl, (int)strlen(szUrl), lpDest, nDestLen);
1447   return S_OK;
1448 }
1449
1450 /*************************************************************************
1451  *      UrlApplySchemeA [SHLWAPI.@]
1452  *
1453  * Apply a scheme to a Url.
1454  *
1455  * PARAMS
1456  *  pszIn   [I]   Url to apply scheme to
1457  *  pszOut  [O]   Destination for modified Url
1458  *  pcchOut [I/O] Length of pszOut/destination for length of pszOut
1459  *  dwFlags [I]   URL_ flags from "shlwapi.h"
1460  *
1461  * RETURNS
1462  *  Success: S_OK: pszOut contains the modified Url, pcchOut contains its length.
1463  *  Failure: An HRESULT error code describing the error.
1464  */
1465 HRESULT WINAPI UrlApplySchemeA(LPCSTR pszIn, LPSTR pszOut, LPDWORD pcchOut, DWORD dwFlags)
1466 {
1467     LPWSTR in, out;
1468     HRESULT ret;
1469     DWORD len;
1470
1471     TRACE("(%s, %p, %p:out size %d, 0x%08x)\n", debugstr_a(pszIn),
1472             pszOut, pcchOut, pcchOut ? *pcchOut : 0, dwFlags);
1473
1474     if (!pszIn || !pszOut || !pcchOut) return E_INVALIDARG;
1475
1476     in = HeapAlloc(GetProcessHeap(), 0,
1477                   (2*INTERNET_MAX_URL_LENGTH) * sizeof(WCHAR));
1478     out = in + INTERNET_MAX_URL_LENGTH;
1479
1480     MultiByteToWideChar(CP_ACP, 0, pszIn, -1, in, INTERNET_MAX_URL_LENGTH);
1481     len = INTERNET_MAX_URL_LENGTH;
1482
1483     ret = UrlApplySchemeW(in, out, &len, dwFlags);
1484     if (ret != S_OK) {
1485         HeapFree(GetProcessHeap(), 0, in);
1486         return ret;
1487     }
1488
1489     len = WideCharToMultiByte(CP_ACP, 0, out, -1, NULL, 0, NULL, NULL);
1490     if (len > *pcchOut) {
1491         ret = E_POINTER;
1492         goto cleanup;
1493     }
1494
1495     WideCharToMultiByte(CP_ACP, 0, out, -1, pszOut, *pcchOut, NULL, NULL);
1496     len--;
1497
1498 cleanup:
1499     *pcchOut = len;
1500     HeapFree(GetProcessHeap(), 0, in);
1501     return ret;
1502 }
1503
1504 static HRESULT URL_GuessScheme(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut)
1505 {
1506     HKEY newkey;
1507     BOOL j;
1508     INT index;
1509     DWORD value_len, data_len, dwType, i;
1510     WCHAR reg_path[MAX_PATH];
1511     WCHAR value[MAX_PATH], data[MAX_PATH];
1512     WCHAR Wxx, Wyy;
1513
1514     MultiByteToWideChar(0, 0,
1515               "Software\\Microsoft\\Windows\\CurrentVersion\\URL\\Prefixes",
1516                         -1, reg_path, MAX_PATH);
1517     RegOpenKeyExW(HKEY_LOCAL_MACHINE, reg_path, 0, 1, &newkey);
1518     index = 0;
1519     while(value_len = data_len = MAX_PATH,
1520           RegEnumValueW(newkey, index, value, &value_len,
1521                         0, &dwType, (LPVOID)data, &data_len) == 0) {
1522         TRACE("guess %d %s is %s\n",
1523               index, debugstr_w(value), debugstr_w(data));
1524
1525         j = FALSE;
1526         for(i=0; i<value_len; i++) {
1527             Wxx = pszIn[i];
1528             Wyy = value[i];
1529             /* remember that TRUE is not-equal */
1530             j = ChrCmpIW(Wxx, Wyy);
1531             if (j) break;
1532         }
1533         if ((i == value_len) && !j) {
1534             if (strlenW(data) + strlenW(pszIn) + 1 > *pcchOut) {
1535                 *pcchOut = strlenW(data) + strlenW(pszIn) + 1;
1536                 RegCloseKey(newkey);
1537                 return E_POINTER;
1538             }
1539             strcpyW(pszOut, data);
1540             strcatW(pszOut, pszIn);
1541             *pcchOut = strlenW(pszOut);
1542             TRACE("matched and set to %s\n", debugstr_w(pszOut));
1543             RegCloseKey(newkey);
1544             return S_OK;
1545         }
1546         index++;
1547     }
1548     RegCloseKey(newkey);
1549     return E_FAIL;
1550 }
1551
1552 static HRESULT URL_ApplyDefault(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut)
1553 {
1554     HKEY newkey;
1555     DWORD data_len, dwType;
1556     WCHAR data[MAX_PATH];
1557
1558     static const WCHAR prefix_keyW[] =
1559         {'S','o','f','t','w','a','r','e',
1560          '\\','M','i','c','r','o','s','o','f','t',
1561          '\\','W','i','n','d','o','w','s',
1562          '\\','C','u','r','r','e','n','t','V','e','r','s','i','o','n',
1563          '\\','U','R','L',
1564          '\\','D','e','f','a','u','l','t','P','r','e','f','i','x',0};
1565
1566     /* get and prepend default */
1567     RegOpenKeyExW(HKEY_LOCAL_MACHINE, prefix_keyW, 0, 1, &newkey);
1568     data_len = sizeof(data);
1569     RegQueryValueExW(newkey, NULL, 0, &dwType, (LPBYTE)data, &data_len);
1570     RegCloseKey(newkey);
1571     if (strlenW(data) + strlenW(pszIn) + 1 > *pcchOut) {
1572         *pcchOut = strlenW(data) + strlenW(pszIn) + 1;
1573         return E_POINTER;
1574     }
1575     strcpyW(pszOut, data);
1576     strcatW(pszOut, pszIn);
1577     *pcchOut = strlenW(pszOut);
1578     TRACE("used default %s\n", debugstr_w(pszOut));
1579     return S_OK;
1580 }
1581
1582 /*************************************************************************
1583  *      UrlApplySchemeW [SHLWAPI.@]
1584  *
1585  * See UrlApplySchemeA.
1586  */
1587 HRESULT WINAPI UrlApplySchemeW(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut, DWORD dwFlags)
1588 {
1589     PARSEDURLW in_scheme;
1590     DWORD res1;
1591     HRESULT ret;
1592
1593     TRACE("(%s, %p, %p:out size %d, 0x%08x)\n", debugstr_w(pszIn),
1594             pszOut, pcchOut, pcchOut ? *pcchOut : 0, dwFlags);
1595
1596     if (!pszIn || !pszOut || !pcchOut) return E_INVALIDARG;
1597
1598     if (dwFlags & URL_APPLY_GUESSFILE) {
1599         FIXME("(%s %p %p(%d) 0x%08x): stub URL_APPLY_GUESSFILE not implemented\n",
1600               debugstr_w(pszIn), pszOut, pcchOut, *pcchOut, dwFlags);
1601         strcpyW(pszOut, pszIn);
1602         *pcchOut = strlenW(pszOut);
1603         return S_FALSE;
1604     }
1605
1606     in_scheme.cbSize = sizeof(in_scheme);
1607     /* See if the base has a scheme */
1608     res1 = ParseURLW(pszIn, &in_scheme);
1609     if (res1) {
1610         /* no scheme in input, need to see if we need to guess */
1611         if (dwFlags & URL_APPLY_GUESSSCHEME) {
1612             if ((ret = URL_GuessScheme(pszIn, pszOut, pcchOut)) != E_FAIL)
1613                 return ret;
1614         }
1615     }
1616     else {
1617         /* we have a scheme, see if valid (known scheme) */
1618         if (in_scheme.nScheme) {
1619             /* have valid scheme, so just copy and exit */
1620             if (strlenW(pszIn) + 1 > *pcchOut) {
1621                 *pcchOut = strlenW(pszIn) + 1;
1622                 return E_POINTER;
1623             }
1624             strcpyW(pszOut, pszIn);
1625             *pcchOut = strlenW(pszOut);
1626             TRACE("valid scheme, returning copy\n");
1627             return S_OK;
1628         }
1629     }
1630
1631     /* If we are here, then either invalid scheme,
1632      * or no scheme and can't/failed guess.
1633      */
1634     if ( ( ((res1 == 0) && (dwFlags & URL_APPLY_FORCEAPPLY)) ||
1635            ((res1 != 0)) ) &&
1636          (dwFlags & URL_APPLY_DEFAULT)) {
1637         /* find and apply default scheme */
1638         return URL_ApplyDefault(pszIn, pszOut, pcchOut);
1639     }
1640
1641     return S_FALSE;
1642 }
1643
1644 /*************************************************************************
1645  *      UrlIsA          [SHLWAPI.@]
1646  *
1647  * Determine if a Url is of a certain class.
1648  *
1649  * PARAMS
1650  *  pszUrl [I] Url to check
1651  *  Urlis  [I] URLIS_ constant from "shlwapi.h"
1652  *
1653  * RETURNS
1654  *  TRUE if pszUrl belongs to the class type in Urlis.
1655  *  FALSE Otherwise.
1656  */
1657 BOOL WINAPI UrlIsA(LPCSTR pszUrl, URLIS Urlis)
1658 {
1659     PARSEDURLA base;
1660     DWORD res1;
1661     LPCSTR last;
1662
1663     TRACE("(%s %d)\n", debugstr_a(pszUrl), Urlis);
1664
1665     switch (Urlis) {
1666
1667     case URLIS_OPAQUE:
1668         base.cbSize = sizeof(base);
1669         res1 = ParseURLA(pszUrl, &base);
1670         if (res1) return FALSE;  /* invalid scheme */
1671         switch (base.nScheme)
1672         {
1673         case URL_SCHEME_MAILTO:
1674         case URL_SCHEME_SHELL:
1675         case URL_SCHEME_JAVASCRIPT:
1676         case URL_SCHEME_VBSCRIPT:
1677         case URL_SCHEME_ABOUT:
1678             return TRUE;
1679         }
1680         return FALSE;
1681
1682     case URLIS_FILEURL:
1683         return !StrCmpNA("file:", pszUrl, 5);
1684
1685     case URLIS_DIRECTORY:
1686         last = pszUrl + strlen(pszUrl) - 1;
1687         return (last >= pszUrl && (*last == '/' || *last == '\\' ));
1688
1689     case URLIS_URL:
1690         return PathIsURLA(pszUrl);
1691
1692     case URLIS_NOHISTORY:
1693     case URLIS_APPLIABLE:
1694     case URLIS_HASQUERY:
1695     default:
1696         FIXME("(%s %d): stub\n", debugstr_a(pszUrl), Urlis);
1697     }
1698     return FALSE;
1699 }
1700
1701 /*************************************************************************
1702  *      UrlIsW          [SHLWAPI.@]
1703  *
1704  * See UrlIsA.
1705  */
1706 BOOL WINAPI UrlIsW(LPCWSTR pszUrl, URLIS Urlis)
1707 {
1708     static const WCHAR stemp[] = { 'f','i','l','e',':',0 };
1709     PARSEDURLW base;
1710     DWORD res1;
1711     LPCWSTR last;
1712
1713     TRACE("(%s %d)\n", debugstr_w(pszUrl), Urlis);
1714
1715     switch (Urlis) {
1716
1717     case URLIS_OPAQUE:
1718         base.cbSize = sizeof(base);
1719         res1 = ParseURLW(pszUrl, &base);
1720         if (res1) return FALSE;  /* invalid scheme */
1721         switch (base.nScheme)
1722         {
1723         case URL_SCHEME_MAILTO:
1724         case URL_SCHEME_SHELL:
1725         case URL_SCHEME_JAVASCRIPT:
1726         case URL_SCHEME_VBSCRIPT:
1727         case URL_SCHEME_ABOUT:
1728             return TRUE;
1729         }
1730         return FALSE;
1731
1732     case URLIS_FILEURL:
1733         return !strncmpW(stemp, pszUrl, 5);
1734
1735     case URLIS_DIRECTORY:
1736         last = pszUrl + strlenW(pszUrl) - 1;
1737         return (last >= pszUrl && (*last == '/' || *last == '\\'));
1738
1739     case URLIS_URL:
1740         return PathIsURLW(pszUrl);
1741
1742     case URLIS_NOHISTORY:
1743     case URLIS_APPLIABLE:
1744     case URLIS_HASQUERY:
1745     default:
1746         FIXME("(%s %d): stub\n", debugstr_w(pszUrl), Urlis);
1747     }
1748     return FALSE;
1749 }
1750
1751 /*************************************************************************
1752  *      UrlIsNoHistoryA         [SHLWAPI.@]
1753  *
1754  * Determine if a Url should not be stored in the users history list.
1755  *
1756  * PARAMS
1757  *  pszUrl [I] Url to check
1758  *
1759  * RETURNS
1760  *  TRUE, if pszUrl should be excluded from the history list,
1761  *  FALSE otherwise.
1762  */
1763 BOOL WINAPI UrlIsNoHistoryA(LPCSTR pszUrl)
1764 {
1765     return UrlIsA(pszUrl, URLIS_NOHISTORY);
1766 }
1767
1768 /*************************************************************************
1769  *      UrlIsNoHistoryW         [SHLWAPI.@]
1770  *
1771  * See UrlIsNoHistoryA.
1772  */
1773 BOOL WINAPI UrlIsNoHistoryW(LPCWSTR pszUrl)
1774 {
1775     return UrlIsW(pszUrl, URLIS_NOHISTORY);
1776 }
1777
1778 /*************************************************************************
1779  *      UrlIsOpaqueA    [SHLWAPI.@]
1780  *
1781  * Determine if a Url is opaque.
1782  *
1783  * PARAMS
1784  *  pszUrl [I] Url to check
1785  *
1786  * RETURNS
1787  *  TRUE if pszUrl is opaque,
1788  *  FALSE Otherwise.
1789  *
1790  * NOTES
1791  *  An opaque Url is one that does not start with "<protocol>://".
1792  */
1793 BOOL WINAPI UrlIsOpaqueA(LPCSTR pszUrl)
1794 {
1795     return UrlIsA(pszUrl, URLIS_OPAQUE);
1796 }
1797
1798 /*************************************************************************
1799  *      UrlIsOpaqueW    [SHLWAPI.@]
1800  *
1801  * See UrlIsOpaqueA.
1802  */
1803 BOOL WINAPI UrlIsOpaqueW(LPCWSTR pszUrl)
1804 {
1805     return UrlIsW(pszUrl, URLIS_OPAQUE);
1806 }
1807
1808 /*************************************************************************
1809  *  Scans for characters of type "type" and when not matching found,
1810  *  returns pointer to it and length in size.
1811  *
1812  * Characters tested based on RFC 1738
1813  */
1814 static LPCWSTR  URL_ScanID(LPCWSTR start, LPDWORD size, WINE_URL_SCAN_TYPE type)
1815 {
1816     static DWORD alwayszero = 0;
1817     BOOL cont = TRUE;
1818
1819     *size = 0;
1820
1821     switch(type){
1822
1823     case SCHEME:
1824         while (cont) {
1825             if ( (islowerW(*start) && isalphaW(*start)) ||
1826                  isdigitW(*start) ||
1827                  (*start == '+') ||
1828                  (*start == '-') ||
1829                  (*start == '.')) {
1830                 start++;
1831                 (*size)++;
1832             }
1833             else
1834                 cont = FALSE;
1835         }
1836         break;
1837
1838     case USERPASS:
1839         while (cont) {
1840             if ( isalphaW(*start) ||
1841                  isdigitW(*start) ||
1842                  /* user/password only characters */
1843                  (*start == ';') ||
1844                  (*start == '?') ||
1845                  (*start == '&') ||
1846                  (*start == '=') ||
1847                  /* *extra* characters */
1848                  (*start == '!') ||
1849                  (*start == '*') ||
1850                  (*start == '\'') ||
1851                  (*start == '(') ||
1852                  (*start == ')') ||
1853                  (*start == ',') ||
1854                  /* *safe* characters */
1855                  (*start == '$') ||
1856                  (*start == '_') ||
1857                  (*start == '+') ||
1858                  (*start == '-') ||
1859                  (*start == '.') ||
1860                  (*start == ' ')) {
1861                 start++;
1862                 (*size)++;
1863             } else if (*start == '%') {
1864                 if (isxdigitW(*(start+1)) &&
1865                     isxdigitW(*(start+2))) {
1866                     start += 3;
1867                     *size += 3;
1868                 } else
1869                     cont = FALSE;
1870             } else
1871                 cont = FALSE;
1872         }
1873         break;
1874
1875     case PORT:
1876         while (cont) {
1877             if (isdigitW(*start)) {
1878                 start++;
1879                 (*size)++;
1880             }
1881             else
1882                 cont = FALSE;
1883         }
1884         break;
1885
1886     case HOST:
1887         while (cont) {
1888             if (isalnumW(*start) ||
1889                 (*start == '-') ||
1890                 (*start == '.') ||
1891                 (*start == ' ') ) {
1892                 start++;
1893                 (*size)++;
1894             }
1895             else
1896                 cont = FALSE;
1897         }
1898         break;
1899     default:
1900         FIXME("unknown type %d\n", type);
1901         return (LPWSTR)&alwayszero;
1902     }
1903     /* TRACE("scanned %d characters next char %p<%c>\n",
1904      *size, start, *start); */
1905     return start;
1906 }
1907
1908 /*************************************************************************
1909  *  Attempt to parse URL into pieces.
1910  */
1911 static LONG URL_ParseUrl(LPCWSTR pszUrl, WINE_PARSE_URL *pl)
1912 {
1913     LPCWSTR work;
1914
1915     memset(pl, 0, sizeof(WINE_PARSE_URL));
1916     pl->pScheme = pszUrl;
1917     work = URL_ScanID(pl->pScheme, &pl->szScheme, SCHEME);
1918     if (!*work || (*work != ':')) goto ErrorExit;
1919     work++;
1920     if ((*work != '/') || (*(work+1) != '/')) goto ErrorExit;
1921     pl->pUserName = work + 2;
1922     work = URL_ScanID(pl->pUserName, &pl->szUserName, USERPASS);
1923     if (*work == ':' ) {
1924         /* parse password */
1925         work++;
1926         pl->pPassword = work;
1927         work = URL_ScanID(pl->pPassword, &pl->szPassword, USERPASS);
1928         if (*work != '@') {
1929             /* what we just parsed must be the hostname and port
1930              * so reset pointers and clear then let it parse */
1931             pl->szUserName = pl->szPassword = 0;
1932             work = pl->pUserName - 1;
1933             pl->pUserName = pl->pPassword = 0;
1934         }
1935     } else if (*work == '@') {
1936         /* no password */
1937         pl->szPassword = 0;
1938         pl->pPassword = 0;
1939     } else if (!*work || (*work == '/') || (*work == '.')) {
1940         /* what was parsed was hostname, so reset pointers and let it parse */
1941         pl->szUserName = pl->szPassword = 0;
1942         work = pl->pUserName - 1;
1943         pl->pUserName = pl->pPassword = 0;
1944     } else goto ErrorExit;
1945
1946     /* now start parsing hostname or hostnumber */
1947     work++;
1948     pl->pHostName = work;
1949     work = URL_ScanID(pl->pHostName, &pl->szHostName, HOST);
1950     if (*work == ':') {
1951         /* parse port */
1952         work++;
1953         pl->pPort = work;
1954         work = URL_ScanID(pl->pPort, &pl->szPort, PORT);
1955     }
1956     if (*work == '/') {
1957         /* see if query string */
1958         pl->pQuery = strchrW(work, '?');
1959         if (pl->pQuery) pl->szQuery = strlenW(pl->pQuery);
1960     }
1961     TRACE("parse successful: scheme=%p(%d), user=%p(%d), pass=%p(%d), host=%p(%d), port=%p(%d), query=%p(%d)\n",
1962           pl->pScheme, pl->szScheme,
1963           pl->pUserName, pl->szUserName,
1964           pl->pPassword, pl->szPassword,
1965           pl->pHostName, pl->szHostName,
1966           pl->pPort, pl->szPort,
1967           pl->pQuery, pl->szQuery);
1968     return S_OK;
1969   ErrorExit:
1970     FIXME("failed to parse %s\n", debugstr_w(pszUrl));
1971     return E_INVALIDARG;
1972 }
1973
1974 /*************************************************************************
1975  *      UrlGetPartA     [SHLWAPI.@]
1976  *
1977  * Retrieve part of a Url.
1978  *
1979  * PARAMS
1980  *  pszIn   [I]   Url to parse
1981  *  pszOut  [O]   Destination for part of pszIn requested
1982  *  pcchOut [I]   Size of pszOut
1983  *          [O]   length of pszOut string EXCLUDING '\0' if S_OK, otherwise
1984  *                needed size of pszOut INCLUDING '\0'.
1985  *  dwPart  [I]   URL_PART_ enum from "shlwapi.h"
1986  *  dwFlags [I]   URL_ flags from "shlwapi.h"
1987  *
1988  * RETURNS
1989  *  Success: S_OK. pszOut contains the part requested, pcchOut contains its length.
1990  *  Failure: An HRESULT error code describing the error.
1991  */
1992 HRESULT WINAPI UrlGetPartA(LPCSTR pszIn, LPSTR pszOut, LPDWORD pcchOut,
1993                            DWORD dwPart, DWORD dwFlags)
1994 {
1995     LPWSTR in, out;
1996     DWORD ret, len, len2;
1997
1998     in = HeapAlloc(GetProcessHeap(), 0,
1999                               (2*INTERNET_MAX_URL_LENGTH) * sizeof(WCHAR));
2000     out = in + INTERNET_MAX_URL_LENGTH;
2001
2002     MultiByteToWideChar(0, 0, pszIn, -1, in, INTERNET_MAX_URL_LENGTH);
2003
2004     len = INTERNET_MAX_URL_LENGTH;
2005     ret = UrlGetPartW(in, out, &len, dwPart, dwFlags);
2006
2007     if (FAILED(ret)) {
2008         HeapFree(GetProcessHeap(), 0, in);
2009         return ret;
2010     }
2011
2012     len2 = WideCharToMultiByte(0, 0, out, len, 0, 0, 0, 0);
2013     if (len2 > *pcchOut) {
2014         *pcchOut = len2;
2015         HeapFree(GetProcessHeap(), 0, in);
2016         return E_POINTER;
2017     }
2018     len2 = WideCharToMultiByte(0, 0, out, len+1, pszOut, *pcchOut, 0, 0);
2019     *pcchOut = len2-1;
2020     HeapFree(GetProcessHeap(), 0, in);
2021     return ret;
2022 }
2023
2024 /*************************************************************************
2025  *      UrlGetPartW     [SHLWAPI.@]
2026  *
2027  * See UrlGetPartA.
2028  */
2029 HRESULT WINAPI UrlGetPartW(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut,
2030                            DWORD dwPart, DWORD dwFlags)
2031 {
2032     WINE_PARSE_URL pl;
2033     HRESULT ret;
2034     DWORD scheme, size, schsize;
2035     LPCWSTR addr, schaddr;
2036
2037     TRACE("(%s %p %p(%d) %08x %08x)\n",
2038           debugstr_w(pszIn), pszOut, pcchOut, *pcchOut, dwPart, dwFlags);
2039
2040     addr = strchrW(pszIn, ':');
2041     if(!addr)
2042         return E_FAIL;
2043
2044     scheme = get_scheme_code(pszIn, addr-pszIn);
2045
2046     ret = URL_ParseUrl(pszIn, &pl);
2047     if (ret == S_OK) {
2048         schaddr = pl.pScheme;
2049         schsize = pl.szScheme;
2050
2051         switch (dwPart) {
2052         case URL_PART_SCHEME:
2053             if (!pl.szScheme) return E_INVALIDARG;
2054             addr = pl.pScheme;
2055             size = pl.szScheme;
2056             break;
2057
2058         case URL_PART_HOSTNAME:
2059             switch(scheme) {
2060             case URL_SCHEME_FTP:
2061             case URL_SCHEME_HTTP:
2062             case URL_SCHEME_GOPHER:
2063             case URL_SCHEME_TELNET:
2064             case URL_SCHEME_FILE:
2065             case URL_SCHEME_HTTPS:
2066                 break;
2067             default:
2068                 return E_FAIL;
2069             }
2070
2071             if(scheme==URL_SCHEME_FILE && (!pl.szHostName ||
2072                         (pl.szHostName==1 && *(pl.pHostName+1)==':'))) {
2073                 if(pcchOut)
2074                     *pszOut = '\0';
2075                 *pcchOut = 0;
2076                 return S_FALSE;
2077             }
2078
2079             if (!pl.szHostName) return E_INVALIDARG;
2080             addr = pl.pHostName;
2081             size = pl.szHostName;
2082             break;
2083
2084         case URL_PART_USERNAME:
2085             if (!pl.szUserName) return E_INVALIDARG;
2086             addr = pl.pUserName;
2087             size = pl.szUserName;
2088             break;
2089
2090         case URL_PART_PASSWORD:
2091             if (!pl.szPassword) return E_INVALIDARG;
2092             addr = pl.pPassword;
2093             size = pl.szPassword;
2094             break;
2095
2096         case URL_PART_PORT:
2097             if (!pl.szPort) return E_INVALIDARG;
2098             addr = pl.pPort;
2099             size = pl.szPort;
2100             break;
2101
2102         case URL_PART_QUERY:
2103             if (!pl.szQuery) return E_INVALIDARG;
2104             addr = pl.pQuery;
2105             size = pl.szQuery;
2106             break;
2107
2108         default:
2109             return E_INVALIDARG;
2110         }
2111
2112         if (dwFlags == URL_PARTFLAG_KEEPSCHEME) {
2113             if (*pcchOut < schsize + size + 2) {
2114                 *pcchOut = schsize + size + 2;
2115                 return E_POINTER;
2116             }
2117             memcpy(pszOut, schaddr, schsize*sizeof(WCHAR));
2118             pszOut[schsize] = ':';
2119             memcpy(pszOut+schsize+1, addr, size*sizeof(WCHAR));
2120             pszOut[schsize+1+size] = 0;
2121             *pcchOut = schsize + 1 + size;
2122         }
2123         else {
2124             if (*pcchOut < size + 1) {*pcchOut = size+1; return E_POINTER;}
2125             memcpy(pszOut, addr, size*sizeof(WCHAR));
2126             pszOut[size] = 0;
2127             *pcchOut = size;
2128         }
2129         TRACE("len=%d %s\n", *pcchOut, debugstr_w(pszOut));
2130     }else if(dwPart==URL_PART_HOSTNAME && scheme==URL_SCHEME_FILE) {
2131         if(*pcchOut)
2132             *pszOut = '\0';
2133         *pcchOut = 0;
2134         return S_FALSE;
2135     }
2136
2137     return ret;
2138 }
2139
2140 /*************************************************************************
2141  * PathIsURLA   [SHLWAPI.@]
2142  *
2143  * Check if the given path is a Url.
2144  *
2145  * PARAMS
2146  *  lpszPath [I] Path to check.
2147  *
2148  * RETURNS
2149  *  TRUE  if lpszPath is a Url.
2150  *  FALSE if lpszPath is NULL or not a Url.
2151  */
2152 BOOL WINAPI PathIsURLA(LPCSTR lpstrPath)
2153 {
2154     PARSEDURLA base;
2155     HRESULT hres;
2156
2157     TRACE("%s\n", debugstr_a(lpstrPath));
2158
2159     if (!lpstrPath || !*lpstrPath) return FALSE;
2160
2161     /* get protocol        */
2162     base.cbSize = sizeof(base);
2163     hres = ParseURLA(lpstrPath, &base);
2164     return hres == S_OK && (base.nScheme != URL_SCHEME_INVALID);
2165 }
2166
2167 /*************************************************************************
2168  * PathIsURLW   [SHLWAPI.@]
2169  *
2170  * See PathIsURLA.
2171  */
2172 BOOL WINAPI PathIsURLW(LPCWSTR lpstrPath)
2173 {
2174     PARSEDURLW base;
2175     HRESULT hres;
2176
2177     TRACE("%s\n", debugstr_w(lpstrPath));
2178
2179     if (!lpstrPath || !*lpstrPath) return FALSE;
2180
2181     /* get protocol        */
2182     base.cbSize = sizeof(base);
2183     hres = ParseURLW(lpstrPath, &base);
2184     return hres == S_OK && (base.nScheme != URL_SCHEME_INVALID);
2185 }
2186
2187 /*************************************************************************
2188  *      UrlCreateFromPathA      [SHLWAPI.@]
2189  * 
2190  * See UrlCreateFromPathW
2191  */
2192 HRESULT WINAPI UrlCreateFromPathA(LPCSTR pszPath, LPSTR pszUrl, LPDWORD pcchUrl, DWORD dwReserved)
2193 {
2194     WCHAR bufW[INTERNET_MAX_URL_LENGTH];
2195     WCHAR *urlW = bufW;
2196     UNICODE_STRING pathW;
2197     HRESULT ret;
2198     DWORD lenW = sizeof(bufW)/sizeof(WCHAR), lenA;
2199
2200     if(!RtlCreateUnicodeStringFromAsciiz(&pathW, pszPath))
2201         return E_INVALIDARG;
2202     if((ret = UrlCreateFromPathW(pathW.Buffer, urlW, &lenW, dwReserved)) == E_POINTER) {
2203         urlW = HeapAlloc(GetProcessHeap(), 0, lenW * sizeof(WCHAR));
2204         ret = UrlCreateFromPathW(pathW.Buffer, urlW, &lenW, dwReserved);
2205     }
2206     if(ret == S_OK || ret == S_FALSE) {
2207         RtlUnicodeToMultiByteSize(&lenA, urlW, lenW * sizeof(WCHAR));
2208         if(*pcchUrl > lenA) {
2209             RtlUnicodeToMultiByteN(pszUrl, *pcchUrl - 1, &lenA, urlW, lenW * sizeof(WCHAR));
2210             pszUrl[lenA] = 0;
2211             *pcchUrl = lenA;
2212         } else {
2213             *pcchUrl = lenA + 1;
2214             ret = E_POINTER;
2215         }
2216     }
2217     if(urlW != bufW) HeapFree(GetProcessHeap(), 0, urlW);
2218     RtlFreeUnicodeString(&pathW);
2219     return ret;
2220 }
2221
2222 /*************************************************************************
2223  *      UrlCreateFromPathW      [SHLWAPI.@]
2224  *
2225  * Create a Url from a file path.
2226  *
2227  * PARAMS
2228  *  pszPath [I]    Path to convert
2229  *  pszUrl  [O]    Destination for the converted Url
2230  *  pcchUrl [I/O]  Length of pszUrl
2231  *  dwReserved [I] Reserved, must be 0
2232  *
2233  * RETURNS
2234  *  Success: S_OK pszUrl contains the converted path, S_FALSE if the path is already a Url
2235  *  Failure: An HRESULT error code.
2236  */
2237 HRESULT WINAPI UrlCreateFromPathW(LPCWSTR pszPath, LPWSTR pszUrl, LPDWORD pcchUrl, DWORD dwReserved)
2238 {
2239     DWORD needed;
2240     HRESULT ret;
2241     WCHAR *pszNewUrl;
2242     WCHAR file_colonW[] = {'f','i','l','e',':',0};
2243     WCHAR three_slashesW[] = {'/','/','/',0};
2244     PARSEDURLW parsed_url;
2245
2246     TRACE("(%s, %p, %p, 0x%08x)\n", debugstr_w(pszPath), pszUrl, pcchUrl, dwReserved);
2247
2248     /* Validate arguments */
2249     if (dwReserved != 0)
2250         return E_INVALIDARG;
2251     if (!pszUrl || !pcchUrl)
2252         return E_INVALIDARG;
2253
2254
2255     parsed_url.cbSize = sizeof(parsed_url);
2256     if(ParseURLW(pszPath, &parsed_url) == S_OK) {
2257         if(parsed_url.nScheme != URL_SCHEME_INVALID && parsed_url.cchProtocol > 1) {
2258             needed = strlenW(pszPath);
2259             if (needed >= *pcchUrl) {
2260                 *pcchUrl = needed + 1;
2261                 return E_POINTER;
2262             } else {
2263                 *pcchUrl = needed;
2264                 strcpyW(pszUrl, pszPath);
2265                 return S_FALSE;
2266             }
2267         }
2268     }
2269
2270     pszNewUrl = HeapAlloc(GetProcessHeap(), 0, (strlenW(pszPath) + 9) * sizeof(WCHAR)); /* "file:///" + pszPath_len + 1 */
2271     strcpyW(pszNewUrl, file_colonW);
2272     if(isalphaW(pszPath[0]) && pszPath[1] == ':')
2273         strcatW(pszNewUrl, three_slashesW);
2274     strcatW(pszNewUrl, pszPath);
2275     ret = UrlEscapeW(pszNewUrl, pszUrl, pcchUrl, URL_ESCAPE_PERCENT);
2276
2277     HeapFree(GetProcessHeap(), 0, pszNewUrl);
2278     return ret;
2279 }
2280
2281 /*************************************************************************
2282  *      SHAutoComplete          [SHLWAPI.@]
2283  *
2284  * Enable auto-completion for an edit control.
2285  *
2286  * PARAMS
2287  *  hwndEdit [I] Handle of control to enable auto-completion for
2288  *  dwFlags  [I] SHACF_ flags from "shlwapi.h"
2289  *
2290  * RETURNS
2291  *  Success: S_OK. Auto-completion is enabled for the control.
2292  *  Failure: An HRESULT error code indicating the error.
2293  */
2294 HRESULT WINAPI SHAutoComplete(HWND hwndEdit, DWORD dwFlags)
2295 {
2296   FIXME("SHAutoComplete stub\n");
2297   return S_FALSE;
2298 }
2299
2300 /*************************************************************************
2301  *  MLBuildResURLA      [SHLWAPI.405]
2302  *
2303  * Create a Url pointing to a resource in a module.
2304  *
2305  * PARAMS
2306  *  lpszLibName [I] Name of the module containing the resource
2307  *  hMod        [I] Callers module handle
2308  *  dwFlags     [I] Undocumented flags for loading the module
2309  *  lpszRes     [I] Resource name
2310  *  lpszDest    [O] Destination for resulting Url
2311  *  dwDestLen   [I] Length of lpszDest
2312  *
2313  * RETURNS
2314  *  Success: S_OK. lpszDest contains the resource Url.
2315  *  Failure: E_INVALIDARG, if any argument is invalid, or
2316  *           E_FAIL if dwDestLen is too small.
2317  */
2318 HRESULT WINAPI MLBuildResURLA(LPCSTR lpszLibName, HMODULE hMod, DWORD dwFlags,
2319                               LPCSTR lpszRes, LPSTR lpszDest, DWORD dwDestLen)
2320 {
2321   WCHAR szLibName[MAX_PATH], szRes[MAX_PATH], szDest[MAX_PATH];
2322   HRESULT hRet;
2323
2324   if (lpszLibName)
2325     MultiByteToWideChar(CP_ACP, 0, lpszLibName, -1, szLibName, sizeof(szLibName)/sizeof(WCHAR));
2326
2327   if (lpszRes)
2328     MultiByteToWideChar(CP_ACP, 0, lpszRes, -1, szRes, sizeof(szRes)/sizeof(WCHAR));
2329
2330   if (dwDestLen > sizeof(szLibName)/sizeof(WCHAR))
2331     dwDestLen = sizeof(szLibName)/sizeof(WCHAR);
2332
2333   hRet = MLBuildResURLW(lpszLibName ? szLibName : NULL, hMod, dwFlags,
2334                         lpszRes ? szRes : NULL, lpszDest ? szDest : NULL, dwDestLen);
2335   if (SUCCEEDED(hRet) && lpszDest)
2336     WideCharToMultiByte(CP_ACP, 0, szDest, -1, lpszDest, dwDestLen, 0, 0);
2337
2338   return hRet;
2339 }
2340
2341 /*************************************************************************
2342  *  MLBuildResURLA      [SHLWAPI.406]
2343  *
2344  * See MLBuildResURLA.
2345  */
2346 HRESULT WINAPI MLBuildResURLW(LPCWSTR lpszLibName, HMODULE hMod, DWORD dwFlags,
2347                               LPCWSTR lpszRes, LPWSTR lpszDest, DWORD dwDestLen)
2348 {
2349   static const WCHAR szRes[] = { 'r','e','s',':','/','/','\0' };
2350 #define szResLen ((sizeof(szRes) - sizeof(WCHAR))/sizeof(WCHAR))
2351   HRESULT hRet = E_FAIL;
2352
2353   TRACE("(%s,%p,0x%08x,%s,%p,%d)\n", debugstr_w(lpszLibName), hMod, dwFlags,
2354         debugstr_w(lpszRes), lpszDest, dwDestLen);
2355
2356   if (!lpszLibName || !hMod || hMod == INVALID_HANDLE_VALUE || !lpszRes ||
2357       !lpszDest || (dwFlags && dwFlags != 2))
2358     return E_INVALIDARG;
2359
2360   if (dwDestLen >= szResLen + 1)
2361   {
2362     dwDestLen -= (szResLen + 1);
2363     memcpy(lpszDest, szRes, sizeof(szRes));
2364
2365     hMod = MLLoadLibraryW(lpszLibName, hMod, dwFlags);
2366
2367     if (hMod)
2368     {
2369       WCHAR szBuff[MAX_PATH];
2370       DWORD len;
2371
2372       len = GetModuleFileNameW(hMod, szBuff, sizeof(szBuff)/sizeof(WCHAR));
2373       if (len && len < sizeof(szBuff)/sizeof(WCHAR))
2374       {
2375         DWORD dwPathLen = strlenW(szBuff) + 1;
2376
2377         if (dwDestLen >= dwPathLen)
2378         {
2379           DWORD dwResLen;
2380
2381           dwDestLen -= dwPathLen;
2382           memcpy(lpszDest + szResLen, szBuff, dwPathLen * sizeof(WCHAR));
2383
2384           dwResLen = strlenW(lpszRes) + 1;
2385           if (dwDestLen >= dwResLen + 1)
2386           {
2387             lpszDest[szResLen + dwPathLen + dwResLen] = '/';
2388             memcpy(lpszDest + szResLen + dwPathLen, lpszRes, dwResLen * sizeof(WCHAR));
2389             hRet = S_OK;
2390           }
2391         }
2392       }
2393       MLFreeLibrary(hMod);
2394     }
2395   }
2396   return hRet;
2397 }
2398
2399 /***********************************************************************
2400  *             UrlFixupW [SHLWAPI.462]
2401  *
2402  * Checks the scheme part of a URL and attempts to correct misspellings.
2403  *
2404  * PARAMS
2405  *  lpszUrl           [I] Pointer to the URL to be corrected
2406  *  lpszTranslatedUrl [O] Pointer to a buffer to store corrected URL
2407  *  dwMaxChars        [I] Maximum size of corrected URL
2408  *
2409  * RETURNS
2410  *  success: S_OK if URL corrected or already correct
2411  *  failure: S_FALSE if unable to correct / COM error code if other error
2412  *
2413  */
2414 HRESULT WINAPI UrlFixupW(LPCWSTR url, LPWSTR translatedUrl, DWORD maxChars)
2415 {
2416     DWORD srcLen;
2417
2418     FIXME("(%s,%p,%d) STUB\n", debugstr_w(url), translatedUrl, maxChars);
2419
2420     if (!url)
2421         return E_FAIL;
2422
2423     srcLen = lstrlenW(url);
2424
2425     /* For now just copy the URL directly */
2426     lstrcpynW(translatedUrl, url, (maxChars < srcLen) ? maxChars : srcLen);
2427
2428     return S_OK;
2429 }