2 * Copyright 2018 Nikolay Sivov
3 * Copyright 2018 Zhiyi Zhang
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
32 #include "kernelbase.h"
33 #include "wine/exception.h"
34 #include "wine/debug.h"
35 #include "wine/heap.h"
37 WINE_DEFAULT_DEBUG_CHANNEL(path
);
39 #define isalnum(ch) (((ch) >= '0' && (ch) <= '9') || \
40 ((ch) >= 'A' && (ch) <= 'Z') || \
41 ((ch) >= 'a' && (ch) <= 'z'))
42 #define isxdigit(ch) (((ch) >= '0' && (ch) <= '9') || \
43 ((ch) >= 'A' && (ch) <= 'F') || \
44 ((ch) >= 'a' && (ch) <= 'f'))
46 static const char hexDigits
[] = "0123456789ABCDEF";
48 static const unsigned char hashdata_lookup
[256] =
50 0x01, 0x0e, 0x6e, 0x19, 0x61, 0xae, 0x84, 0x77, 0x8a, 0xaa, 0x7d, 0x76, 0x1b, 0xe9, 0x8c, 0x33,
51 0x57, 0xc5, 0xb1, 0x6b, 0xea, 0xa9, 0x38, 0x44, 0x1e, 0x07, 0xad, 0x49, 0xbc, 0x28, 0x24, 0x41,
52 0x31, 0xd5, 0x68, 0xbe, 0x39, 0xd3, 0x94, 0xdf, 0x30, 0x73, 0x0f, 0x02, 0x43, 0xba, 0xd2, 0x1c,
53 0x0c, 0xb5, 0x67, 0x46, 0x16, 0x3a, 0x4b, 0x4e, 0xb7, 0xa7, 0xee, 0x9d, 0x7c, 0x93, 0xac, 0x90,
54 0xb0, 0xa1, 0x8d, 0x56, 0x3c, 0x42, 0x80, 0x53, 0x9c, 0xf1, 0x4f, 0x2e, 0xa8, 0xc6, 0x29, 0xfe,
55 0xb2, 0x55, 0xfd, 0xed, 0xfa, 0x9a, 0x85, 0x58, 0x23, 0xce, 0x5f, 0x74, 0xfc, 0xc0, 0x36, 0xdd,
56 0x66, 0xda, 0xff, 0xf0, 0x52, 0x6a, 0x9e, 0xc9, 0x3d, 0x03, 0x59, 0x09, 0x2a, 0x9b, 0x9f, 0x5d,
57 0xa6, 0x50, 0x32, 0x22, 0xaf, 0xc3, 0x64, 0x63, 0x1a, 0x96, 0x10, 0x91, 0x04, 0x21, 0x08, 0xbd,
58 0x79, 0x40, 0x4d, 0x48, 0xd0, 0xf5, 0x82, 0x7a, 0x8f, 0x37, 0x69, 0x86, 0x1d, 0xa4, 0xb9, 0xc2,
59 0xc1, 0xef, 0x65, 0xf2, 0x05, 0xab, 0x7e, 0x0b, 0x4a, 0x3b, 0x89, 0xe4, 0x6c, 0xbf, 0xe8, 0x8b,
60 0x06, 0x18, 0x51, 0x14, 0x7f, 0x11, 0x5b, 0x5c, 0xfb, 0x97, 0xe1, 0xcf, 0x15, 0x62, 0x71, 0x70,
61 0x54, 0xe2, 0x12, 0xd6, 0xc7, 0xbb, 0x0d, 0x20, 0x5e, 0xdc, 0xe0, 0xd4, 0xf7, 0xcc, 0xc4, 0x2b,
62 0xf9, 0xec, 0x2d, 0xf4, 0x6f, 0xb6, 0x99, 0x88, 0x81, 0x5a, 0xd9, 0xca, 0x13, 0xa5, 0xe7, 0x47,
63 0xe6, 0x8e, 0x60, 0xe3, 0x3e, 0xb3, 0xf6, 0x72, 0xa2, 0x35, 0xa0, 0xd7, 0xcd, 0xb4, 0x2f, 0x6d,
64 0x2c, 0x26, 0x1f, 0x95, 0x87, 0x00, 0xd8, 0x34, 0x3f, 0x17, 0x25, 0x45, 0x27, 0x75, 0x92, 0xb8,
65 0xa3, 0xc8, 0xde, 0xeb, 0xf8, 0xf3, 0xdb, 0x0a, 0x98, 0x83, 0x7b, 0xe5, 0xcb, 0x4c, 0x78, 0xd1,
70 const WCHAR
*scheme
; /* [out] start of scheme */
71 DWORD scheme_len
; /* [out] size of scheme (until colon) */
72 const WCHAR
*username
; /* [out] start of Username */
73 DWORD username_len
; /* [out] size of Username (until ":" or "@") */
74 const WCHAR
*password
; /* [out] start of Password */
75 DWORD password_len
; /* [out] size of Password (until "@") */
76 const WCHAR
*hostname
; /* [out] start of Hostname */
77 DWORD hostname_len
; /* [out] size of Hostname (until ":" or "/") */
78 const WCHAR
*port
; /* [out] start of Port */
79 DWORD port_len
; /* [out] size of Port (until "/" or eos) */
80 const WCHAR
*query
; /* [out] start of Query */
81 DWORD query_len
; /* [out] size of Query (until eos) */
92 static WCHAR
*heap_strdupAtoW(const char *str
)
100 len
= MultiByteToWideChar(CP_ACP
, 0, str
, -1, NULL
, 0);
101 ret
= heap_alloc(len
* sizeof(WCHAR
));
102 MultiByteToWideChar(CP_ACP
, 0, str
, -1, ret
, len
);
108 static SIZE_T
strnlenW(const WCHAR
*string
, SIZE_T maxlen
)
112 for (i
= 0; i
< maxlen
; i
++)
113 if (!string
[i
]) break;
117 static BOOL
is_drive_spec( const WCHAR
*str
)
119 return ((str
[0] >= 'A' && str
[0] <= 'Z') || (str
[0] >= 'a' && str
[0] <= 'z')) && str
[1] == ':';
122 static BOOL
is_escaped_drive_spec( const WCHAR
*str
)
124 return ((str
[0] >= 'A' && str
[0] <= 'Z') || (str
[0] >= 'a' && str
[0] <= 'z')) &&
125 (str
[1] == ':' || str
[1] == '|');
128 static BOOL
is_prefixed_unc(const WCHAR
*string
)
130 return !wcsnicmp(string
, L
"\\\\?\\UNC\\", 8 );
133 static BOOL
is_prefixed_disk(const WCHAR
*string
)
135 return !wcsncmp(string
, L
"\\\\?\\", 4) && is_drive_spec( string
+ 4 );
138 static BOOL
is_prefixed_volume(const WCHAR
*string
)
143 if (wcsnicmp( string
, L
"\\\\?\\Volume", 10 )) return FALSE
;
152 if (guid
[i
] != '{') return FALSE
;
158 if (guid
[i
] != '-') return FALSE
;
161 if (guid
[i
] != '}') return FALSE
;
164 if (!isxdigit(guid
[i
])) return FALSE
;
173 /* Get the next character beyond end of the segment.
174 Return TRUE if the last segment ends with a backslash */
175 static BOOL
get_next_segment(const WCHAR
*next
, const WCHAR
**next_segment
)
177 while (*next
&& *next
!= '\\') next
++;
180 *next_segment
= next
+ 1;
185 *next_segment
= next
;
190 /* Find the last character of the root in a path, if there is one, without any segments */
191 static const WCHAR
*get_root_end(const WCHAR
*path
)
194 if (is_prefixed_volume(path
))
195 return path
[48] == '\\' ? path
+ 48 : path
+ 47;
196 else if (is_prefixed_unc(path
))
198 else if (is_prefixed_disk(path
))
199 return path
[6] == '\\' ? path
+ 6 : path
+ 5;
201 else if (path
[0] == '\\' && path
[1] == '\\')
204 else if (path
[0] == '\\')
207 else if (is_drive_spec( path
))
208 return path
[2] == '\\' ? path
+ 2 : path
+ 1;
213 HRESULT WINAPI
PathAllocCanonicalize(const WCHAR
*path_in
, DWORD flags
, WCHAR
**path_out
)
217 const WCHAR
*root_end
;
218 SIZE_T buffer_size
, length
;
220 TRACE("%s %#x %p\n", debugstr_w(path_in
), flags
, path_out
);
222 if (!path_in
|| !path_out
223 || ((flags
& PATHCCH_FORCE_ENABLE_LONG_NAME_PROCESS
) && (flags
& PATHCCH_FORCE_DISABLE_LONG_NAME_PROCESS
))
224 || (flags
& (PATHCCH_FORCE_ENABLE_LONG_NAME_PROCESS
| PATHCCH_FORCE_DISABLE_LONG_NAME_PROCESS
)
225 && !(flags
& PATHCCH_ALLOW_LONG_PATHS
))
226 || ((flags
& PATHCCH_ENSURE_IS_EXTENDED_LENGTH_PATH
) && (flags
& PATHCCH_ALLOW_LONG_PATHS
)))
228 if (path_out
) *path_out
= NULL
;
232 length
= lstrlenW(path_in
);
233 if ((length
+ 1 > MAX_PATH
&& !(flags
& (PATHCCH_ALLOW_LONG_PATHS
| PATHCCH_ENSURE_IS_EXTENDED_LENGTH_PATH
)))
234 || (length
+ 1 > PATHCCH_MAX_CCH
))
237 return HRESULT_FROM_WIN32(ERROR_FILENAME_EXCED_RANGE
);
240 /* PATHCCH_ENSURE_IS_EXTENDED_LENGTH_PATH implies PATHCCH_DO_NOT_NORMALIZE_SEGMENTS */
241 if (flags
& PATHCCH_ENSURE_IS_EXTENDED_LENGTH_PATH
) flags
|= PATHCCH_DO_NOT_NORMALIZE_SEGMENTS
;
243 /* path length + possible \\?\ addition + possible \ addition + NUL */
244 buffer_size
= (length
+ 6) * sizeof(WCHAR
);
245 buffer
= LocalAlloc(LMEM_ZEROINIT
, buffer_size
);
249 return E_OUTOFMEMORY
;
255 root_end
= get_root_end(path_in
);
256 if (root_end
) root_end
= buffer
+ (root_end
- path_in
);
261 memcpy(dst
, src
, (root_end
- buffer
+ 1) * sizeof(WCHAR
));
262 src
+= root_end
- buffer
+ 1;
263 if(PathCchStripPrefix(dst
, length
+ 6) == S_OK
)
265 /* Fill in \ in X:\ if the \ is missing */
266 if (is_drive_spec( dst
) && dst
[2]!= '\\')
271 dst
= buffer
+ lstrlenW(buffer
);
275 dst
+= root_end
- buffer
+ 1;
284 /* Keep one . after * */
285 if (dst
> buffer
&& dst
[-1] == '*')
291 /* Keep the .. if not surrounded by \ */
292 if ((src
[2] != '\\' && src
[2]) || (dst
> buffer
&& dst
[-1] != '\\'))
299 /* Remove the \ before .. if the \ is not part of root */
300 if (dst
> buffer
&& dst
[-1] == '\\' && (!root_end
|| dst
- 1 > root_end
))
303 /* Remove characters until a \ is encountered */
315 /* Remove the extra \ after .. if the \ before .. wasn't deleted */
316 else if (src
[2] == '\\')
323 /* Keep the . if not surrounded by \ */
324 if ((src
[1] != '\\' && src
[1]) || (dst
> buffer
&& dst
[-1] != '\\'))
330 /* Remove the \ before . if the \ is not part of root */
331 if (dst
> buffer
&& dst
[-1] == '\\' && (!root_end
|| dst
- 1 > root_end
)) dst
--;
332 /* Remove the extra \ after . if the \ before . wasn't deleted */
333 else if (src
[1] == '\\')
339 /* If X:\ is not complete, then complete it */
340 if (is_drive_spec( buffer
) && buffer
[2] != '\\')
342 root_end
= buffer
+ 2;
345 /* If next character is \, use the \ to fill in */
346 if (src
[0] == '\\') src
++;
356 /* Strip multiple trailing . */
357 if (!(flags
& PATHCCH_DO_NOT_NORMALIZE_SEGMENTS
))
359 while (dst
> buffer
&& dst
[-1] == '.')
361 /* Keep a . after * */
362 if (dst
- 1 > buffer
&& dst
[-2] == '*')
364 /* If . follow a : at the second character, remove the . and add a \ */
365 else if (dst
- 1 > buffer
&& dst
[-2] == ':' && dst
- 2 == buffer
+ 1)
372 /* If result path is empty, fill in \ */
379 /* Extend the path if needed */
380 length
= lstrlenW(buffer
);
381 if (((length
+ 1 > MAX_PATH
&& is_drive_spec( buffer
))
382 || (is_drive_spec( buffer
) && flags
& PATHCCH_ENSURE_IS_EXTENDED_LENGTH_PATH
))
383 && !(flags
& PATHCCH_FORCE_ENABLE_LONG_NAME_PROCESS
))
385 memmove(buffer
+ 4, buffer
, (length
+ 1) * sizeof(WCHAR
));
392 /* Add a trailing backslash to the path if needed */
393 if (flags
& PATHCCH_ENSURE_TRAILING_SLASH
)
394 PathCchAddBackslash(buffer
, buffer_size
);
400 HRESULT WINAPI
PathAllocCombine(const WCHAR
*path1
, const WCHAR
*path2
, DWORD flags
, WCHAR
**out
)
402 SIZE_T combined_length
, length2
;
403 WCHAR
*combined_path
;
404 BOOL from_path2
= FALSE
;
407 TRACE("%s %s %#x %p\n", wine_dbgstr_w(path1
), wine_dbgstr_w(path2
), flags
, out
);
409 if ((!path1
&& !path2
) || !out
)
411 if (out
) *out
= NULL
;
415 if (!path1
|| !path2
) return PathAllocCanonicalize(path1
? path1
: path2
, flags
, out
);
417 /* If path2 is fully qualified, use path2 only */
418 if (is_drive_spec( path2
) || (path2
[0] == '\\' && path2
[1] == '\\'))
425 length2
= path2
? lstrlenW(path2
) : 0;
426 /* path1 length + path2 length + possible backslash + NULL */
427 combined_length
= lstrlenW(path1
) + length2
+ 2;
429 combined_path
= HeapAlloc(GetProcessHeap(), 0, combined_length
* sizeof(WCHAR
));
433 return E_OUTOFMEMORY
;
436 lstrcpyW(combined_path
, path1
);
437 PathCchStripPrefix(combined_path
, combined_length
);
438 if (from_path2
) PathCchAddBackslashEx(combined_path
, combined_length
, NULL
, NULL
);
440 if (path2
&& path2
[0])
442 if (path2
[0] == '\\' && path2
[1] != '\\')
444 PathCchStripToRoot(combined_path
, combined_length
);
448 PathCchAddBackslashEx(combined_path
, combined_length
, NULL
, NULL
);
449 lstrcatW(combined_path
, path2
);
452 hr
= PathAllocCanonicalize(combined_path
, flags
, out
);
453 HeapFree(GetProcessHeap(), 0, combined_path
);
457 HRESULT WINAPI
PathCchAddBackslash(WCHAR
*path
, SIZE_T size
)
459 return PathCchAddBackslashEx(path
, size
, NULL
, NULL
);
462 HRESULT WINAPI
PathCchAddBackslashEx(WCHAR
*path
, SIZE_T size
, WCHAR
**endptr
, SIZE_T
*remaining
)
464 BOOL needs_termination
;
467 TRACE("%s, %lu, %p, %p\n", debugstr_w(path
), size
, endptr
, remaining
);
469 length
= lstrlenW(path
);
470 needs_termination
= size
&& length
&& path
[length
- 1] != '\\';
472 if (length
>= (needs_termination
? size
- 1 : size
))
474 if (endptr
) *endptr
= NULL
;
475 if (remaining
) *remaining
= 0;
476 return STRSAFE_E_INSUFFICIENT_BUFFER
;
479 if (!needs_termination
)
481 if (endptr
) *endptr
= path
+ length
;
482 if (remaining
) *remaining
= size
- length
;
486 path
[length
++] = '\\';
489 if (endptr
) *endptr
= path
+ length
;
490 if (remaining
) *remaining
= size
- length
;
495 HRESULT WINAPI
PathCchAddExtension(WCHAR
*path
, SIZE_T size
, const WCHAR
*extension
)
497 const WCHAR
*existing_extension
, *next
;
498 SIZE_T path_length
, extension_length
, dot_length
;
502 TRACE("%s %lu %s\n", wine_dbgstr_w(path
), size
, wine_dbgstr_w(extension
));
504 if (!path
|| !size
|| size
> PATHCCH_MAX_CCH
|| !extension
) return E_INVALIDARG
;
509 if ((*next
== '.' && next
> extension
) || *next
== ' ' || *next
== '\\') return E_INVALIDARG
;
513 has_dot
= extension
[0] == '.';
515 hr
= PathCchFindExtension(path
, size
, &existing_extension
);
516 if (FAILED(hr
)) return hr
;
517 if (*existing_extension
) return S_FALSE
;
519 path_length
= strnlenW(path
, size
);
520 dot_length
= has_dot
? 0 : 1;
521 extension_length
= lstrlenW(extension
);
523 if (path_length
+ dot_length
+ extension_length
+ 1 > size
) return STRSAFE_E_INSUFFICIENT_BUFFER
;
525 /* If extension is empty or only dot, return S_OK with path unchanged */
526 if (!extension
[0] || (extension
[0] == '.' && !extension
[1])) return S_OK
;
530 path
[path_length
] = '.';
534 lstrcpyW(path
+ path_length
, extension
);
538 HRESULT WINAPI
PathCchAppend(WCHAR
*path1
, SIZE_T size
, const WCHAR
*path2
)
540 TRACE("%s %lu %s\n", wine_dbgstr_w(path1
), size
, wine_dbgstr_w(path2
));
542 return PathCchAppendEx(path1
, size
, path2
, PATHCCH_NONE
);
545 HRESULT WINAPI
PathCchAppendEx(WCHAR
*path1
, SIZE_T size
, const WCHAR
*path2
, DWORD flags
)
550 TRACE("%s %lu %s %#x\n", wine_dbgstr_w(path1
), size
, wine_dbgstr_w(path2
), flags
);
552 if (!path1
|| !size
) return E_INVALIDARG
;
554 /* Create a temporary buffer for result because we need to keep path1 unchanged if error occurs.
555 * And PathCchCombineEx writes empty result if there is error so we can't just use path1 as output
556 * buffer for PathCchCombineEx */
557 result
= HeapAlloc(GetProcessHeap(), 0, size
* sizeof(WCHAR
));
558 if (!result
) return E_OUTOFMEMORY
;
560 /* Avoid the single backslash behavior with PathCchCombineEx when appending */
561 if (path2
&& path2
[0] == '\\' && path2
[1] != '\\') path2
++;
563 hr
= PathCchCombineEx(result
, size
, path1
, path2
, flags
);
564 if (SUCCEEDED(hr
)) memcpy(path1
, result
, size
* sizeof(WCHAR
));
566 HeapFree(GetProcessHeap(), 0, result
);
570 HRESULT WINAPI
PathCchCanonicalize(WCHAR
*out
, SIZE_T size
, const WCHAR
*in
)
572 TRACE("%p %lu %s\n", out
, size
, wine_dbgstr_w(in
));
574 /* Not X:\ and path > MAX_PATH - 4, return HRESULT_FROM_WIN32(ERROR_FILENAME_EXCED_RANGE) */
575 if (lstrlenW(in
) > MAX_PATH
- 4 && !(is_drive_spec( in
) && in
[2] == '\\'))
576 return HRESULT_FROM_WIN32(ERROR_FILENAME_EXCED_RANGE
);
578 return PathCchCanonicalizeEx(out
, size
, in
, PATHCCH_NONE
);
581 HRESULT WINAPI
PathCchCanonicalizeEx(WCHAR
*out
, SIZE_T size
, const WCHAR
*in
, DWORD flags
)
587 TRACE("%p %lu %s %#x\n", out
, size
, wine_dbgstr_w(in
), flags
);
589 if (!size
) return E_INVALIDARG
;
591 hr
= PathAllocCanonicalize(in
, flags
, &buffer
);
592 if (FAILED(hr
)) return hr
;
594 length
= lstrlenW(buffer
);
595 if (size
< length
+ 1)
597 /* No root and path > MAX_PATH - 4, return HRESULT_FROM_WIN32(ERROR_FILENAME_EXCED_RANGE) */
598 if (length
> MAX_PATH
- 4 && !(in
[0] == '\\' || (is_drive_spec( in
) && in
[2] == '\\')))
599 hr
= HRESULT_FROM_WIN32(ERROR_FILENAME_EXCED_RANGE
);
601 hr
= STRSAFE_E_INSUFFICIENT_BUFFER
;
606 memcpy(out
, buffer
, (length
+ 1) * sizeof(WCHAR
));
608 /* Fill a backslash at the end of X: */
609 if (is_drive_spec( out
) && !out
[2] && size
> 3)
620 HRESULT WINAPI
PathCchCombine(WCHAR
*out
, SIZE_T size
, const WCHAR
*path1
, const WCHAR
*path2
)
622 TRACE("%p %s %s\n", out
, wine_dbgstr_w(path1
), wine_dbgstr_w(path2
));
624 return PathCchCombineEx(out
, size
, path1
, path2
, PATHCCH_NONE
);
627 HRESULT WINAPI
PathCchCombineEx(WCHAR
*out
, SIZE_T size
, const WCHAR
*path1
, const WCHAR
*path2
, DWORD flags
)
633 TRACE("%p %s %s %#x\n", out
, wine_dbgstr_w(path1
), wine_dbgstr_w(path2
), flags
);
635 if (!out
|| !size
|| size
> PATHCCH_MAX_CCH
) return E_INVALIDARG
;
637 hr
= PathAllocCombine(path1
, path2
, flags
, &buffer
);
644 length
= lstrlenW(buffer
);
645 if (length
+ 1 > size
)
649 return STRSAFE_E_INSUFFICIENT_BUFFER
;
653 memcpy(out
, buffer
, (length
+ 1) * sizeof(WCHAR
));
659 HRESULT WINAPI
PathCchFindExtension(const WCHAR
*path
, SIZE_T size
, const WCHAR
**extension
)
661 const WCHAR
*lastpoint
= NULL
;
664 TRACE("%s %lu %p\n", wine_dbgstr_w(path
), size
, extension
);
666 if (!path
|| !size
|| size
> PATHCCH_MAX_CCH
)
674 if (*path
== '\\' || *path
== ' ')
676 else if (*path
== '.')
681 if (counter
== size
|| counter
== PATHCCH_MAX_CCH
)
688 *extension
= lastpoint
? lastpoint
: path
;
692 BOOL WINAPI
PathCchIsRoot(const WCHAR
*path
)
694 const WCHAR
*root_end
;
698 TRACE("%s\n", wine_dbgstr_w(path
));
700 if (!path
|| !*path
) return FALSE
;
702 root_end
= get_root_end(path
);
703 if (!root_end
) return FALSE
;
705 if ((is_unc
= is_prefixed_unc(path
)) || (path
[0] == '\\' && path
[1] == '\\' && path
[2] != '?'))
708 /* No extra segments */
709 if ((is_unc
&& !*next
) || (!is_unc
&& !*next
)) return TRUE
;
711 /* Has first segment with an ending backslash but no remaining characters */
712 if (get_next_segment(next
, &next
) && !*next
) return FALSE
;
713 /* Has first segment with no ending backslash */
716 /* Has first segment with an ending backslash and has remaining characters*/
720 /* Second segment must have no backslash and no remaining characters */
721 return !get_next_segment(next
, &next
) && !*next
;
724 else if (*root_end
== '\\' && !root_end
[1])
730 HRESULT WINAPI
PathCchRemoveBackslash(WCHAR
*path
, SIZE_T path_size
)
735 TRACE("%s %lu\n", debugstr_w(path
), path_size
);
737 return PathCchRemoveBackslashEx(path
, path_size
, &path_end
, &free_size
);
740 HRESULT WINAPI
PathCchRemoveBackslashEx(WCHAR
*path
, SIZE_T path_size
, WCHAR
**path_end
, SIZE_T
*free_size
)
742 const WCHAR
*root_end
;
745 TRACE("%s %lu %p %p\n", debugstr_w(path
), path_size
, path_end
, free_size
);
747 if (!path_size
|| !path_end
|| !free_size
)
749 if (path_end
) *path_end
= NULL
;
750 if (free_size
) *free_size
= 0;
754 path_length
= strnlenW(path
, path_size
);
755 if (path_length
== path_size
&& !path
[path_length
]) return E_INVALIDARG
;
757 root_end
= get_root_end(path
);
758 if (path_length
> 0 && path
[path_length
- 1] == '\\')
760 *path_end
= path
+ path_length
- 1;
761 *free_size
= path_size
- path_length
+ 1;
762 /* If the last character is beyond end of root */
763 if (!root_end
|| path
+ path_length
- 1 > root_end
)
765 path
[path_length
- 1] = 0;
773 *path_end
= path
+ path_length
;
774 *free_size
= path_size
- path_length
;
779 HRESULT WINAPI
PathCchRemoveExtension(WCHAR
*path
, SIZE_T size
)
781 const WCHAR
*extension
;
785 TRACE("%s %lu\n", wine_dbgstr_w(path
), size
);
787 if (!path
|| !size
|| size
> PATHCCH_MAX_CCH
) return E_INVALIDARG
;
789 hr
= PathCchFindExtension(path
, size
, &extension
);
790 if (FAILED(hr
)) return hr
;
792 next
= path
+ (extension
- path
);
793 while (next
- path
< size
&& *next
) *next
++ = 0;
795 return next
== extension
? S_FALSE
: S_OK
;
798 HRESULT WINAPI
PathCchRemoveFileSpec(WCHAR
*path
, SIZE_T size
)
800 const WCHAR
*root_end
= NULL
;
804 TRACE("%s %lu\n", wine_dbgstr_w(path
), size
);
806 if (!path
|| !size
|| size
> PATHCCH_MAX_CCH
) return E_INVALIDARG
;
808 if (PathCchIsRoot(path
)) return S_FALSE
;
810 PathCchSkipRoot(path
, &root_end
);
812 /* The backslash at the end of UNC and \\* are not considered part of root in this case */
813 if (root_end
&& root_end
> path
&& root_end
[-1] == '\\'
814 && (is_prefixed_unc(path
) || (path
[0] == '\\' && path
[1] == '\\' && path
[2] != '?')))
817 length
= lstrlenW(path
);
818 last
= path
+ length
- 1;
819 while (last
>= path
&& (!root_end
|| last
>= root_end
))
821 if (last
- path
>= size
) return E_INVALIDARG
;
832 return last
!= path
+ length
- 1 ? S_OK
: S_FALSE
;
835 HRESULT WINAPI
PathCchRenameExtension(WCHAR
*path
, SIZE_T size
, const WCHAR
*extension
)
839 TRACE("%s %lu %s\n", wine_dbgstr_w(path
), size
, wine_dbgstr_w(extension
));
841 hr
= PathCchRemoveExtension(path
, size
);
842 if (FAILED(hr
)) return hr
;
844 hr
= PathCchAddExtension(path
, size
, extension
);
845 return FAILED(hr
) ? hr
: S_OK
;
848 HRESULT WINAPI
PathCchSkipRoot(const WCHAR
*path
, const WCHAR
**root_end
)
850 TRACE("%s %p\n", debugstr_w(path
), root_end
);
852 if (!path
|| !path
[0] || !root_end
853 || (!wcsnicmp(path
, L
"\\\\?", 3) && !is_prefixed_volume(path
) && !is_prefixed_unc(path
)
854 && !is_prefixed_disk(path
)))
857 *root_end
= get_root_end(path
);
861 if (is_prefixed_unc(path
))
863 get_next_segment(*root_end
, root_end
);
864 get_next_segment(*root_end
, root_end
);
866 else if (path
[0] == '\\' && path
[1] == '\\' && path
[2] != '?')
868 /* Skip share server */
869 get_next_segment(*root_end
, root_end
);
870 /* If mount point is empty, don't skip over mount point */
871 if (**root_end
!= '\\') get_next_segment(*root_end
, root_end
);
875 return *root_end
? S_OK
: E_INVALIDARG
;
878 HRESULT WINAPI
PathCchStripPrefix(WCHAR
*path
, SIZE_T size
)
880 TRACE("%s %lu\n", wine_dbgstr_w(path
), size
);
882 if (!path
|| !size
|| size
> PATHCCH_MAX_CCH
) return E_INVALIDARG
;
884 if (is_prefixed_unc(path
))
886 /* \\?\UNC\a -> \\a */
887 if (size
< lstrlenW(path
+ 8) + 3) return E_INVALIDARG
;
888 lstrcpyW(path
+ 2, path
+ 8);
891 else if (is_prefixed_disk(path
))
894 if (size
< lstrlenW(path
+ 4) + 1) return E_INVALIDARG
;
895 lstrcpyW(path
, path
+ 4);
902 HRESULT WINAPI
PathCchStripToRoot(WCHAR
*path
, SIZE_T size
)
904 const WCHAR
*root_end
;
908 TRACE("%s %lu\n", wine_dbgstr_w(path
), size
);
910 if (!path
|| !*path
|| !size
|| size
> PATHCCH_MAX_CCH
) return E_INVALIDARG
;
912 /* \\\\?\\UNC\\* and \\\\* have to have at least two extra segments to be striped,
913 * e.g. \\\\?\\UNC\\a\\b\\c -> \\\\?\\UNC\\a\\b
914 * \\\\a\\b\\c -> \\\\a\\b */
915 if ((is_unc
= is_prefixed_unc(path
)) || (path
[0] == '\\' && path
[1] == '\\' && path
[2] != '?'))
917 root_end
= is_unc
? path
+ 8 : path
+ 3;
918 if (!get_next_segment(root_end
, &root_end
)) return S_FALSE
;
919 if (!get_next_segment(root_end
, &root_end
)) return S_FALSE
;
921 if (root_end
- path
>= size
) return E_INVALIDARG
;
923 segment_end
= path
+ (root_end
- path
) - 1;
927 else if (PathCchSkipRoot(path
, &root_end
) == S_OK
)
929 if (root_end
- path
>= size
) return E_INVALIDARG
;
931 segment_end
= path
+ (root_end
- path
);
932 if (!*segment_end
) return S_FALSE
;
941 BOOL WINAPI
PathIsUNCEx(const WCHAR
*path
, const WCHAR
**server
)
943 const WCHAR
*result
= NULL
;
945 TRACE("%s %p\n", wine_dbgstr_w(path
), server
);
947 if (is_prefixed_unc(path
))
949 else if (path
[0] == '\\' && path
[1] == '\\' && path
[2] != '?')
952 if (server
) *server
= result
;
956 BOOL WINAPI
PathIsUNCA(const char *path
)
958 TRACE("%s\n", wine_dbgstr_a(path
));
960 return path
&& (path
[0] == '\\') && (path
[1] == '\\');
963 BOOL WINAPI
PathIsUNCW(const WCHAR
*path
)
965 TRACE("%s\n", wine_dbgstr_w(path
));
967 return path
&& (path
[0] == '\\') && (path
[1] == '\\');
970 BOOL WINAPI
PathIsRelativeA(const char *path
)
972 TRACE("%s\n", wine_dbgstr_a(path
));
974 if (!path
|| !*path
|| IsDBCSLeadByte(*path
))
977 return !(*path
== '\\' || (*path
&& path
[1] == ':'));
980 BOOL WINAPI
PathIsRelativeW(const WCHAR
*path
)
982 TRACE("%s\n", wine_dbgstr_w(path
));
987 return !(*path
== '\\' || (*path
&& path
[1] == ':'));
990 BOOL WINAPI
PathIsUNCServerShareA(const char *path
)
992 BOOL seen_slash
= FALSE
;
994 TRACE("%s\n", wine_dbgstr_a(path
));
996 if (path
&& *path
++ == '\\' && *path
++ == '\\')
1007 path
= CharNextA(path
);
1014 BOOL WINAPI
PathIsUNCServerShareW(const WCHAR
*path
)
1016 BOOL seen_slash
= FALSE
;
1018 TRACE("%s\n", wine_dbgstr_w(path
));
1020 if (path
&& *path
++ == '\\' && *path
++ == '\\')
1038 BOOL WINAPI
PathIsRootA(const char *path
)
1040 TRACE("%s\n", wine_dbgstr_a(path
));
1042 if (!path
|| !*path
)
1048 return TRUE
; /* \ */
1049 else if (path
[1] == '\\')
1051 BOOL seen_slash
= FALSE
;
1054 /* Check for UNC root path */
1064 path
= CharNextA(path
);
1070 else if (path
[1] == ':' && path
[2] == '\\' && path
[3] == '\0')
1071 return TRUE
; /* X:\ */
1076 BOOL WINAPI
PathIsRootW(const WCHAR
*path
)
1078 TRACE("%s\n", wine_dbgstr_w(path
));
1080 if (!path
|| !*path
)
1086 return TRUE
; /* \ */
1087 else if (path
[1] == '\\')
1089 BOOL seen_slash
= FALSE
;
1092 /* Check for UNC root path */
1107 else if (path
[1] == ':' && path
[2] == '\\' && path
[3] == '\0')
1108 return TRUE
; /* X:\ */
1113 BOOL WINAPI
PathRemoveFileSpecA(char *path
)
1115 char *filespec
= path
;
1116 BOOL modified
= FALSE
;
1118 TRACE("%s\n", wine_dbgstr_a(path
));
1123 /* Skip directory or UNC path */
1132 filespec
= path
; /* Skip dir */
1133 else if (*path
== ':')
1135 filespec
= ++path
; /* Skip drive */
1139 if (!(path
= CharNextA(path
)))
1152 BOOL WINAPI
PathRemoveFileSpecW(WCHAR
*path
)
1154 WCHAR
*filespec
= path
;
1155 BOOL modified
= FALSE
;
1157 TRACE("%s\n", wine_dbgstr_w(path
));
1162 /* Skip directory or UNC path */
1171 filespec
= path
; /* Skip dir */
1172 else if (*path
== ':')
1174 filespec
= ++path
; /* Skip drive */
1191 BOOL WINAPI
PathStripToRootA(char *path
)
1193 TRACE("%s\n", wine_dbgstr_a(path
));
1198 while (!PathIsRootA(path
))
1199 if (!PathRemoveFileSpecA(path
))
1205 BOOL WINAPI
PathStripToRootW(WCHAR
*path
)
1207 TRACE("%s\n", wine_dbgstr_w(path
));
1212 while (!PathIsRootW(path
))
1213 if (!PathRemoveFileSpecW(path
))
1219 LPSTR WINAPI
PathAddBackslashA(char *path
)
1224 TRACE("%s\n", wine_dbgstr_a(path
));
1226 if (!path
|| (len
= strlen(path
)) >= MAX_PATH
)
1233 path
= CharNextA(prev
);
1248 LPWSTR WINAPI
PathAddBackslashW(WCHAR
*path
)
1252 TRACE("%s\n", wine_dbgstr_w(path
));
1254 if (!path
|| (len
= lstrlenW(path
)) >= MAX_PATH
)
1260 if (path
[-1] != '\\')
1270 LPSTR WINAPI
PathFindExtensionA(const char *path
)
1272 const char *lastpoint
= NULL
;
1274 TRACE("%s\n", wine_dbgstr_a(path
));
1280 if (*path
== '\\' || *path
== ' ')
1282 else if (*path
== '.')
1284 path
= CharNextA(path
);
1288 return (LPSTR
)(lastpoint
? lastpoint
: path
);
1291 LPWSTR WINAPI
PathFindExtensionW(const WCHAR
*path
)
1293 const WCHAR
*lastpoint
= NULL
;
1295 TRACE("%s\n", wine_dbgstr_w(path
));
1301 if (*path
== '\\' || *path
== ' ')
1303 else if (*path
== '.')
1309 return (LPWSTR
)(lastpoint
? lastpoint
: path
);
1312 BOOL WINAPI
PathAddExtensionA(char *path
, const char *ext
)
1316 TRACE("%s, %s\n", wine_dbgstr_a(path
), wine_dbgstr_a(ext
));
1318 if (!path
|| !ext
|| *(PathFindExtensionA(path
)))
1322 if (len
+ strlen(ext
) >= MAX_PATH
)
1325 strcpy(path
+ len
, ext
);
1329 BOOL WINAPI
PathAddExtensionW(WCHAR
*path
, const WCHAR
*ext
)
1333 TRACE("%s, %s\n", wine_dbgstr_w(path
), wine_dbgstr_w(ext
));
1335 if (!path
|| !ext
|| *(PathFindExtensionW(path
)))
1338 len
= lstrlenW(path
);
1339 if (len
+ lstrlenW(ext
) >= MAX_PATH
)
1342 lstrcpyW(path
+ len
, ext
);
1346 BOOL WINAPI
PathCanonicalizeW(WCHAR
*buffer
, const WCHAR
*path
)
1348 const WCHAR
*src
= path
;
1349 WCHAR
*dst
= buffer
;
1351 TRACE("%p, %s\n", buffer
, wine_dbgstr_w(path
));
1358 SetLastError(ERROR_INVALID_PARAMETER
);
1369 /* Copy path root */
1374 else if (*src
&& src
[1] == ':')
1383 /* Canonicalize the rest of the path */
1388 if (src
[1] == '\\' && (src
== path
|| src
[-1] == '\\' || src
[-1] == ':'))
1390 src
+= 2; /* Skip .\ */
1392 else if (src
[1] == '.' && (dst
== buffer
|| dst
[-1] == '\\'))
1394 /* \.. backs up a directory, over the root if it has no \ following X:.
1395 * .. is ignored if it would remove a UNC server name or initial \\
1399 *dst
= '\0'; /* Allow PathIsUNCServerShareA test on lpszBuf */
1400 if (dst
> buffer
+ 1 && dst
[-1] == '\\' && (dst
[-2] != '\\' || dst
> buffer
+ 2))
1402 if (dst
[-2] == ':' && (dst
> buffer
+ 3 || dst
[-3] == ':'))
1405 while (dst
> buffer
&& *dst
!= '\\')
1408 dst
++; /* Reset to last '\' */
1410 dst
= buffer
; /* Start path again from new root */
1412 else if (dst
[-2] != ':' && !PathIsUNCServerShareW(buffer
))
1415 while (dst
> buffer
&& *dst
!= '\\')
1423 src
+= 2; /* Skip .. in src path */
1432 /* Append \ to naked drive specs */
1433 if (dst
- buffer
== 2 && dst
[-1] == ':')
1439 BOOL WINAPI
PathCanonicalizeA(char *buffer
, const char *path
)
1441 WCHAR pathW
[MAX_PATH
], bufferW
[MAX_PATH
];
1445 TRACE("%p, %s\n", buffer
, wine_dbgstr_a(path
));
1450 if (!buffer
|| !path
)
1452 SetLastError(ERROR_INVALID_PARAMETER
);
1456 len
= MultiByteToWideChar(CP_ACP
, 0, path
, -1, pathW
, ARRAY_SIZE(pathW
));
1460 ret
= PathCanonicalizeW(bufferW
, pathW
);
1461 WideCharToMultiByte(CP_ACP
, 0, bufferW
, -1, buffer
, MAX_PATH
, 0, 0);
1466 WCHAR
* WINAPI
PathCombineW(WCHAR
*dst
, const WCHAR
*dir
, const WCHAR
*file
)
1468 BOOL use_both
= FALSE
, strip
= FALSE
;
1469 WCHAR tmp
[MAX_PATH
];
1471 TRACE("%p, %s, %s\n", dst
, wine_dbgstr_w(dir
), wine_dbgstr_w(file
));
1473 /* Invalid parameters */
1483 if ((!file
|| !*file
) && dir
)
1486 lstrcpynW(tmp
, dir
, ARRAY_SIZE(tmp
));
1488 else if (!dir
|| !*dir
|| !PathIsRelativeW(file
))
1490 if (!dir
|| !*dir
|| *file
!= '\\' || PathIsUNCW(file
))
1493 lstrcpynW(tmp
, file
, ARRAY_SIZE(tmp
));
1506 lstrcpynW(tmp
, dir
, ARRAY_SIZE(tmp
));
1509 PathStripToRootW(tmp
);
1510 file
++; /* Skip '\' */
1513 if (!PathAddBackslashW(tmp
) || lstrlenW(tmp
) + lstrlenW(file
) >= MAX_PATH
)
1519 lstrcatW(tmp
, file
);
1522 PathCanonicalizeW(dst
, tmp
);
1526 LPSTR WINAPI
PathCombineA(char *dst
, const char *dir
, const char *file
)
1528 WCHAR dstW
[MAX_PATH
], dirW
[MAX_PATH
], fileW
[MAX_PATH
];
1530 TRACE("%p, %s, %s\n", dst
, wine_dbgstr_a(dir
), wine_dbgstr_a(file
));
1532 /* Invalid parameters */
1539 if (dir
&& !MultiByteToWideChar(CP_ACP
, 0, dir
, -1, dirW
, ARRAY_SIZE(dirW
)))
1542 if (file
&& !MultiByteToWideChar(CP_ACP
, 0, file
, -1, fileW
, ARRAY_SIZE(fileW
)))
1545 if (PathCombineW(dstW
, dir
? dirW
: NULL
, file
? fileW
: NULL
))
1546 if (WideCharToMultiByte(CP_ACP
, 0, dstW
, -1, dst
, MAX_PATH
, 0, 0))
1553 BOOL WINAPI
PathAppendA(char *path
, const char *append
)
1555 TRACE("%s, %s\n", wine_dbgstr_a(path
), wine_dbgstr_a(append
));
1559 if (!PathIsUNCA(append
))
1560 while (*append
== '\\')
1563 if (PathCombineA(path
, path
, append
))
1570 BOOL WINAPI
PathAppendW(WCHAR
*path
, const WCHAR
*append
)
1572 TRACE("%s, %s\n", wine_dbgstr_w(path
), wine_dbgstr_w(append
));
1576 if (!PathIsUNCW(append
))
1577 while (*append
== '\\')
1580 if (PathCombineW(path
, path
, append
))
1587 int WINAPI
PathCommonPrefixA(const char *file1
, const char *file2
, char *path
)
1589 const char *iter1
= file1
;
1590 const char *iter2
= file2
;
1591 unsigned int len
= 0;
1593 TRACE("%s, %s, %p.\n", wine_dbgstr_a(file1
), wine_dbgstr_a(file2
), path
);
1598 if (!file1
|| !file2
)
1601 /* Handle roots first */
1602 if (PathIsUNCA(file1
))
1604 if (!PathIsUNCA(file2
))
1609 else if (PathIsUNCA(file2
))
1615 if ((!*iter1
|| *iter1
== '\\') && (!*iter2
|| *iter2
== '\\'))
1616 len
= iter1
- file1
; /* Common to this point */
1618 if (!*iter1
|| (tolower(*iter1
) != tolower(*iter2
)))
1619 break; /* Strings differ at this point */
1626 len
++; /* Feature/Bug compatible with Win32 */
1630 memcpy(path
, file1
, len
);
1637 int WINAPI
PathCommonPrefixW(const WCHAR
*file1
, const WCHAR
*file2
, WCHAR
*path
)
1639 const WCHAR
*iter1
= file1
;
1640 const WCHAR
*iter2
= file2
;
1641 unsigned int len
= 0;
1643 TRACE("%s, %s, %p\n", wine_dbgstr_w(file1
), wine_dbgstr_w(file2
), path
);
1648 if (!file1
|| !file2
)
1651 /* Handle roots first */
1652 if (PathIsUNCW(file1
))
1654 if (!PathIsUNCW(file2
))
1659 else if (PathIsUNCW(file2
))
1665 if ((!*iter1
|| *iter1
== '\\') && (!*iter2
|| *iter2
== '\\'))
1666 len
= iter1
- file1
; /* Common to this point */
1668 if (!*iter1
|| (towupper(*iter1
) != towupper(*iter2
)))
1669 break; /* Strings differ at this point */
1676 len
++; /* Feature/Bug compatible with Win32 */
1680 memcpy(path
, file1
, len
* sizeof(WCHAR
));
1687 BOOL WINAPI
PathIsPrefixA(const char *prefix
, const char *path
)
1689 TRACE("%s, %s\n", wine_dbgstr_a(prefix
), wine_dbgstr_a(path
));
1691 return prefix
&& path
&& PathCommonPrefixA(path
, prefix
, NULL
) == (int)strlen(prefix
);
1694 BOOL WINAPI
PathIsPrefixW(const WCHAR
*prefix
, const WCHAR
*path
)
1696 TRACE("%s, %s\n", wine_dbgstr_w(prefix
), wine_dbgstr_w(path
));
1698 return prefix
&& path
&& PathCommonPrefixW(path
, prefix
, NULL
) == (int)lstrlenW(prefix
);
1701 char * WINAPI
PathFindFileNameA(const char *path
)
1703 const char *last_slash
= path
;
1705 TRACE("%s\n", wine_dbgstr_a(path
));
1707 while (path
&& *path
)
1709 if ((*path
== '\\' || *path
== '/' || *path
== ':') &&
1710 path
[1] && path
[1] != '\\' && path
[1] != '/')
1711 last_slash
= path
+ 1;
1712 path
= CharNextA(path
);
1715 return (char *)last_slash
;
1718 WCHAR
* WINAPI
PathFindFileNameW(const WCHAR
*path
)
1720 const WCHAR
*last_slash
= path
;
1722 TRACE("%s\n", wine_dbgstr_w(path
));
1724 while (path
&& *path
)
1726 if ((*path
== '\\' || *path
== '/' || *path
== ':') &&
1727 path
[1] && path
[1] != '\\' && path
[1] != '/')
1728 last_slash
= path
+ 1;
1732 return (WCHAR
*)last_slash
;
1735 char * WINAPI
PathGetArgsA(const char *path
)
1737 BOOL seen_quote
= FALSE
;
1739 TRACE("%s\n", wine_dbgstr_a(path
));
1746 if (*path
== ' ' && !seen_quote
)
1747 return (char *)path
+ 1;
1750 seen_quote
= !seen_quote
;
1751 path
= CharNextA(path
);
1754 return (char *)path
;
1757 WCHAR
* WINAPI
PathGetArgsW(const WCHAR
*path
)
1759 BOOL seen_quote
= FALSE
;
1761 TRACE("%s\n", wine_dbgstr_w(path
));
1768 if (*path
== ' ' && !seen_quote
)
1769 return (WCHAR
*)path
+ 1;
1772 seen_quote
= !seen_quote
;
1776 return (WCHAR
*)path
;
1779 UINT WINAPI
PathGetCharTypeW(WCHAR ch
)
1785 if (!ch
|| ch
< ' ' || ch
== '<' || ch
== '>' || ch
== '"' || ch
== '|' || ch
== '/')
1786 flags
= GCT_INVALID
; /* Invalid */
1787 else if (ch
== '*' || ch
== '?')
1788 flags
= GCT_WILD
; /* Wildchars */
1789 else if (ch
== '\\' || ch
== ':')
1790 return GCT_SEPARATOR
; /* Path separators */
1795 if (((ch
& 0x1) && ch
!= ';') || !ch
|| isalnum(ch
) || ch
== '$' || ch
== '&' || ch
== '(' ||
1796 ch
== '.' || ch
== '@' || ch
== '^' || ch
== '\'' || ch
== '`')
1798 flags
|= GCT_SHORTCHAR
; /* All these are valid for DOS */
1802 flags
|= GCT_SHORTCHAR
; /* Bug compatible with win32 */
1804 flags
|= GCT_LFNCHAR
; /* Valid for long file names */
1810 UINT WINAPI
PathGetCharTypeA(UCHAR ch
)
1812 return PathGetCharTypeW(ch
);
1815 int WINAPI
PathGetDriveNumberA(const char *path
)
1817 TRACE("%s\n", wine_dbgstr_a(path
));
1819 if (path
&& *path
&& path
[1] == ':')
1821 if (*path
>= 'a' && *path
<= 'z') return *path
- 'a';
1822 if (*path
>= 'A' && *path
<= 'Z') return *path
- 'A';
1827 int WINAPI
PathGetDriveNumberW(const WCHAR
*path
)
1829 TRACE("%s\n", wine_dbgstr_w(path
));
1834 if (!wcsncmp(path
, L
"\\\\?\\", 4)) path
+= 4;
1836 if (!path
[0] || path
[1] != ':') return -1;
1837 if (path
[0] >= 'A' && path
[0] <= 'Z') return path
[0] - 'A';
1838 if (path
[0] >= 'a' && path
[0] <= 'z') return path
[0] - 'a';
1842 BOOL WINAPI
PathIsFileSpecA(const char *path
)
1844 TRACE("%s\n", wine_dbgstr_a(path
));
1851 if (*path
== '\\' || *path
== ':')
1853 path
= CharNextA(path
);
1859 BOOL WINAPI
PathIsFileSpecW(const WCHAR
*path
)
1861 TRACE("%s\n", wine_dbgstr_w(path
));
1868 if (*path
== '\\' || *path
== ':')
1876 BOOL WINAPI
PathIsUNCServerA(const char *path
)
1878 TRACE("%s\n", wine_dbgstr_a(path
));
1880 if (!(path
&& path
[0] == '\\' && path
[1] == '\\'))
1887 path
= CharNextA(path
);
1893 BOOL WINAPI
PathIsUNCServerW(const WCHAR
*path
)
1895 TRACE("%s\n", wine_dbgstr_w(path
));
1897 if (!(path
&& path
[0] == '\\' && path
[1] == '\\'))
1900 return !wcschr(path
+ 2, '\\');
1903 void WINAPI
PathRemoveBlanksA(char *path
)
1907 TRACE("%s\n", wine_dbgstr_a(path
));
1909 if (!path
|| !*path
)
1914 while (*path
== ' ')
1915 path
= CharNextA(path
);
1921 while (start
[-1] == ' ')
1927 void WINAPI
PathRemoveBlanksW(WCHAR
*path
)
1929 WCHAR
*start
= path
;
1931 TRACE("%s\n", wine_dbgstr_w(path
));
1933 if (!path
|| !*path
)
1936 while (*path
== ' ')
1943 while (start
[-1] == ' ')
1949 void WINAPI
PathRemoveExtensionA(char *path
)
1951 TRACE("%s\n", wine_dbgstr_a(path
));
1956 path
= PathFindExtensionA(path
);
1961 void WINAPI
PathRemoveExtensionW(WCHAR
*path
)
1963 TRACE("%s\n", wine_dbgstr_w(path
));
1968 path
= PathFindExtensionW(path
);
1973 BOOL WINAPI
PathRenameExtensionA(char *path
, const char *ext
)
1977 TRACE("%s, %s\n", wine_dbgstr_a(path
), wine_dbgstr_a(ext
));
1979 extension
= PathFindExtensionA(path
);
1981 if (!extension
|| (extension
- path
+ strlen(ext
) >= MAX_PATH
))
1984 strcpy(extension
, ext
);
1988 BOOL WINAPI
PathRenameExtensionW(WCHAR
*path
, const WCHAR
*ext
)
1992 TRACE("%s, %s\n", wine_dbgstr_w(path
), wine_dbgstr_w(ext
));
1994 extension
= PathFindExtensionW(path
);
1996 if (!extension
|| (extension
- path
+ lstrlenW(ext
) >= MAX_PATH
))
1999 lstrcpyW(extension
, ext
);
2003 void WINAPI
PathUnquoteSpacesA(char *path
)
2007 TRACE("%s\n", wine_dbgstr_a(path
));
2009 if (!path
|| *path
!= '"')
2012 len
= strlen(path
) - 1;
2013 if (path
[len
] == '"')
2016 for (; *path
; path
++)
2021 void WINAPI
PathUnquoteSpacesW(WCHAR
*path
)
2025 TRACE("%s\n", wine_dbgstr_w(path
));
2027 if (!path
|| *path
!= '"')
2030 len
= lstrlenW(path
) - 1;
2031 if (path
[len
] == '"')
2034 for (; *path
; path
++)
2039 char * WINAPI
PathRemoveBackslashA(char *path
)
2043 TRACE("%s\n", wine_dbgstr_a(path
));
2048 ptr
= CharPrevA(path
, path
+ strlen(path
));
2049 if (!PathIsRootA(path
) && *ptr
== '\\')
2055 WCHAR
* WINAPI
PathRemoveBackslashW(WCHAR
*path
)
2059 TRACE("%s\n", wine_dbgstr_w(path
));
2064 ptr
= path
+ lstrlenW(path
);
2065 if (ptr
> path
) ptr
--;
2066 if (!PathIsRootW(path
) && *ptr
== '\\')
2072 BOOL WINAPI
PathIsLFNFileSpecA(const char *path
)
2074 unsigned int name_len
= 0, ext_len
= 0;
2076 TRACE("%s\n", wine_dbgstr_a(path
));
2084 return TRUE
; /* DOS names cannot have spaces */
2088 return TRUE
; /* DOS names have only one dot */
2095 return TRUE
; /* DOS extensions are <= 3 chars*/
2101 return TRUE
; /* DOS names are <= 8 chars */
2103 path
= CharNextA(path
);
2106 return FALSE
; /* Valid DOS path */
2109 BOOL WINAPI
PathIsLFNFileSpecW(const WCHAR
*path
)
2111 unsigned int name_len
= 0, ext_len
= 0;
2113 TRACE("%s\n", wine_dbgstr_w(path
));
2121 return TRUE
; /* DOS names cannot have spaces */
2125 return TRUE
; /* DOS names have only one dot */
2132 return TRUE
; /* DOS extensions are <= 3 chars*/
2138 return TRUE
; /* DOS names are <= 8 chars */
2143 return FALSE
; /* Valid DOS path */
2146 #define PATH_CHAR_CLASS_LETTER 0x00000001
2147 #define PATH_CHAR_CLASS_ASTERIX 0x00000002
2148 #define PATH_CHAR_CLASS_DOT 0x00000004
2149 #define PATH_CHAR_CLASS_BACKSLASH 0x00000008
2150 #define PATH_CHAR_CLASS_COLON 0x00000010
2151 #define PATH_CHAR_CLASS_SEMICOLON 0x00000020
2152 #define PATH_CHAR_CLASS_COMMA 0x00000040
2153 #define PATH_CHAR_CLASS_SPACE 0x00000080
2154 #define PATH_CHAR_CLASS_OTHER_VALID 0x00000100
2155 #define PATH_CHAR_CLASS_DOUBLEQUOTE 0x00000200
2157 #define PATH_CHAR_CLASS_INVALID 0x00000000
2158 #define PATH_CHAR_CLASS_ANY 0xffffffff
2160 static const DWORD path_charclass
[] =
2162 /* 0x00 */ PATH_CHAR_CLASS_INVALID
, /* 0x01 */ PATH_CHAR_CLASS_INVALID
,
2163 /* 0x02 */ PATH_CHAR_CLASS_INVALID
, /* 0x03 */ PATH_CHAR_CLASS_INVALID
,
2164 /* 0x04 */ PATH_CHAR_CLASS_INVALID
, /* 0x05 */ PATH_CHAR_CLASS_INVALID
,
2165 /* 0x06 */ PATH_CHAR_CLASS_INVALID
, /* 0x07 */ PATH_CHAR_CLASS_INVALID
,
2166 /* 0x08 */ PATH_CHAR_CLASS_INVALID
, /* 0x09 */ PATH_CHAR_CLASS_INVALID
,
2167 /* 0x0a */ PATH_CHAR_CLASS_INVALID
, /* 0x0b */ PATH_CHAR_CLASS_INVALID
,
2168 /* 0x0c */ PATH_CHAR_CLASS_INVALID
, /* 0x0d */ PATH_CHAR_CLASS_INVALID
,
2169 /* 0x0e */ PATH_CHAR_CLASS_INVALID
, /* 0x0f */ PATH_CHAR_CLASS_INVALID
,
2170 /* 0x10 */ PATH_CHAR_CLASS_INVALID
, /* 0x11 */ PATH_CHAR_CLASS_INVALID
,
2171 /* 0x12 */ PATH_CHAR_CLASS_INVALID
, /* 0x13 */ PATH_CHAR_CLASS_INVALID
,
2172 /* 0x14 */ PATH_CHAR_CLASS_INVALID
, /* 0x15 */ PATH_CHAR_CLASS_INVALID
,
2173 /* 0x16 */ PATH_CHAR_CLASS_INVALID
, /* 0x17 */ PATH_CHAR_CLASS_INVALID
,
2174 /* 0x18 */ PATH_CHAR_CLASS_INVALID
, /* 0x19 */ PATH_CHAR_CLASS_INVALID
,
2175 /* 0x1a */ PATH_CHAR_CLASS_INVALID
, /* 0x1b */ PATH_CHAR_CLASS_INVALID
,
2176 /* 0x1c */ PATH_CHAR_CLASS_INVALID
, /* 0x1d */ PATH_CHAR_CLASS_INVALID
,
2177 /* 0x1e */ PATH_CHAR_CLASS_INVALID
, /* 0x1f */ PATH_CHAR_CLASS_INVALID
,
2178 /* ' ' */ PATH_CHAR_CLASS_SPACE
, /* '!' */ PATH_CHAR_CLASS_OTHER_VALID
,
2179 /* '"' */ PATH_CHAR_CLASS_DOUBLEQUOTE
, /* '#' */ PATH_CHAR_CLASS_OTHER_VALID
,
2180 /* '$' */ PATH_CHAR_CLASS_OTHER_VALID
, /* '%' */ PATH_CHAR_CLASS_OTHER_VALID
,
2181 /* '&' */ PATH_CHAR_CLASS_OTHER_VALID
, /* '\'' */ PATH_CHAR_CLASS_OTHER_VALID
,
2182 /* '(' */ PATH_CHAR_CLASS_OTHER_VALID
, /* ')' */ PATH_CHAR_CLASS_OTHER_VALID
,
2183 /* '*' */ PATH_CHAR_CLASS_ASTERIX
, /* '+' */ PATH_CHAR_CLASS_OTHER_VALID
,
2184 /* ',' */ PATH_CHAR_CLASS_COMMA
, /* '-' */ PATH_CHAR_CLASS_OTHER_VALID
,
2185 /* '.' */ PATH_CHAR_CLASS_DOT
, /* '/' */ PATH_CHAR_CLASS_INVALID
,
2186 /* '0' */ PATH_CHAR_CLASS_OTHER_VALID
, /* '1' */ PATH_CHAR_CLASS_OTHER_VALID
,
2187 /* '2' */ PATH_CHAR_CLASS_OTHER_VALID
, /* '3' */ PATH_CHAR_CLASS_OTHER_VALID
,
2188 /* '4' */ PATH_CHAR_CLASS_OTHER_VALID
, /* '5' */ PATH_CHAR_CLASS_OTHER_VALID
,
2189 /* '6' */ PATH_CHAR_CLASS_OTHER_VALID
, /* '7' */ PATH_CHAR_CLASS_OTHER_VALID
,
2190 /* '8' */ PATH_CHAR_CLASS_OTHER_VALID
, /* '9' */ PATH_CHAR_CLASS_OTHER_VALID
,
2191 /* ':' */ PATH_CHAR_CLASS_COLON
, /* ';' */ PATH_CHAR_CLASS_SEMICOLON
,
2192 /* '<' */ PATH_CHAR_CLASS_INVALID
, /* '=' */ PATH_CHAR_CLASS_OTHER_VALID
,
2193 /* '>' */ PATH_CHAR_CLASS_INVALID
, /* '?' */ PATH_CHAR_CLASS_LETTER
,
2194 /* '@' */ PATH_CHAR_CLASS_OTHER_VALID
, /* 'A' */ PATH_CHAR_CLASS_ANY
,
2195 /* 'B' */ PATH_CHAR_CLASS_ANY
, /* 'C' */ PATH_CHAR_CLASS_ANY
,
2196 /* 'D' */ PATH_CHAR_CLASS_ANY
, /* 'E' */ PATH_CHAR_CLASS_ANY
,
2197 /* 'F' */ PATH_CHAR_CLASS_ANY
, /* 'G' */ PATH_CHAR_CLASS_ANY
,
2198 /* 'H' */ PATH_CHAR_CLASS_ANY
, /* 'I' */ PATH_CHAR_CLASS_ANY
,
2199 /* 'J' */ PATH_CHAR_CLASS_ANY
, /* 'K' */ PATH_CHAR_CLASS_ANY
,
2200 /* 'L' */ PATH_CHAR_CLASS_ANY
, /* 'M' */ PATH_CHAR_CLASS_ANY
,
2201 /* 'N' */ PATH_CHAR_CLASS_ANY
, /* 'O' */ PATH_CHAR_CLASS_ANY
,
2202 /* 'P' */ PATH_CHAR_CLASS_ANY
, /* 'Q' */ PATH_CHAR_CLASS_ANY
,
2203 /* 'R' */ PATH_CHAR_CLASS_ANY
, /* 'S' */ PATH_CHAR_CLASS_ANY
,
2204 /* 'T' */ PATH_CHAR_CLASS_ANY
, /* 'U' */ PATH_CHAR_CLASS_ANY
,
2205 /* 'V' */ PATH_CHAR_CLASS_ANY
, /* 'W' */ PATH_CHAR_CLASS_ANY
,
2206 /* 'X' */ PATH_CHAR_CLASS_ANY
, /* 'Y' */ PATH_CHAR_CLASS_ANY
,
2207 /* 'Z' */ PATH_CHAR_CLASS_ANY
, /* '[' */ PATH_CHAR_CLASS_OTHER_VALID
,
2208 /* '\\' */ PATH_CHAR_CLASS_BACKSLASH
, /* ']' */ PATH_CHAR_CLASS_OTHER_VALID
,
2209 /* '^' */ PATH_CHAR_CLASS_OTHER_VALID
, /* '_' */ PATH_CHAR_CLASS_OTHER_VALID
,
2210 /* '`' */ PATH_CHAR_CLASS_OTHER_VALID
, /* 'a' */ PATH_CHAR_CLASS_ANY
,
2211 /* 'b' */ PATH_CHAR_CLASS_ANY
, /* 'c' */ PATH_CHAR_CLASS_ANY
,
2212 /* 'd' */ PATH_CHAR_CLASS_ANY
, /* 'e' */ PATH_CHAR_CLASS_ANY
,
2213 /* 'f' */ PATH_CHAR_CLASS_ANY
, /* 'g' */ PATH_CHAR_CLASS_ANY
,
2214 /* 'h' */ PATH_CHAR_CLASS_ANY
, /* 'i' */ PATH_CHAR_CLASS_ANY
,
2215 /* 'j' */ PATH_CHAR_CLASS_ANY
, /* 'k' */ PATH_CHAR_CLASS_ANY
,
2216 /* 'l' */ PATH_CHAR_CLASS_ANY
, /* 'm' */ PATH_CHAR_CLASS_ANY
,
2217 /* 'n' */ PATH_CHAR_CLASS_ANY
, /* 'o' */ PATH_CHAR_CLASS_ANY
,
2218 /* 'p' */ PATH_CHAR_CLASS_ANY
, /* 'q' */ PATH_CHAR_CLASS_ANY
,
2219 /* 'r' */ PATH_CHAR_CLASS_ANY
, /* 's' */ PATH_CHAR_CLASS_ANY
,
2220 /* 't' */ PATH_CHAR_CLASS_ANY
, /* 'u' */ PATH_CHAR_CLASS_ANY
,
2221 /* 'v' */ PATH_CHAR_CLASS_ANY
, /* 'w' */ PATH_CHAR_CLASS_ANY
,
2222 /* 'x' */ PATH_CHAR_CLASS_ANY
, /* 'y' */ PATH_CHAR_CLASS_ANY
,
2223 /* 'z' */ PATH_CHAR_CLASS_ANY
, /* '{' */ PATH_CHAR_CLASS_OTHER_VALID
,
2224 /* '|' */ PATH_CHAR_CLASS_INVALID
, /* '}' */ PATH_CHAR_CLASS_OTHER_VALID
,
2225 /* '~' */ PATH_CHAR_CLASS_OTHER_VALID
2228 BOOL WINAPI
PathIsValidCharA(char c
, DWORD
class)
2230 if ((unsigned)c
> 0x7e)
2231 return class & PATH_CHAR_CLASS_OTHER_VALID
;
2233 return class & path_charclass
[(unsigned)c
];
2236 BOOL WINAPI
PathIsValidCharW(WCHAR c
, DWORD
class)
2239 return class & PATH_CHAR_CLASS_OTHER_VALID
;
2241 return class & path_charclass
[c
];
2244 char * WINAPI
PathFindNextComponentA(const char *path
)
2248 TRACE("%s\n", wine_dbgstr_a(path
));
2250 if (!path
|| !*path
)
2253 if ((slash
= StrChrA(path
, '\\')))
2255 if (slash
[1] == '\\')
2260 return (char *)path
+ strlen(path
);
2263 WCHAR
* WINAPI
PathFindNextComponentW(const WCHAR
*path
)
2267 TRACE("%s\n", wine_dbgstr_w(path
));
2269 if (!path
|| !*path
)
2272 if ((slash
= StrChrW(path
, '\\')))
2274 if (slash
[1] == '\\')
2279 return (WCHAR
*)path
+ lstrlenW(path
);
2282 char * WINAPI
PathSkipRootA(const char *path
)
2284 TRACE("%s\n", wine_dbgstr_a(path
));
2286 if (!path
|| !*path
)
2289 if (*path
== '\\' && path
[1] == '\\')
2291 /* Network share: skip share server and mount point */
2293 if ((path
= StrChrA(path
, '\\')) && (path
= StrChrA(path
+ 1, '\\')))
2295 return (char *)path
;
2298 if (IsDBCSLeadByte(*path
))
2302 if (path
[0] && path
[1] == ':' && path
[2] == '\\')
2303 return (char *)path
+ 3;
2308 WCHAR
* WINAPI
PathSkipRootW(const WCHAR
*path
)
2310 TRACE("%s\n", wine_dbgstr_w(path
));
2312 if (!path
|| !*path
)
2315 if (*path
== '\\' && path
[1] == '\\')
2317 /* Network share: skip share server and mount point */
2319 if ((path
= StrChrW(path
, '\\')) && (path
= StrChrW(path
+ 1, '\\')))
2321 return (WCHAR
*)path
;
2325 if (path
[0] && path
[1] == ':' && path
[2] == '\\')
2326 return (WCHAR
*)path
+ 3;
2331 void WINAPI
PathStripPathA(char *path
)
2333 TRACE("%s\n", wine_dbgstr_a(path
));
2337 char *filename
= PathFindFileNameA(path
);
2338 if (filename
!= path
)
2339 RtlMoveMemory(path
, filename
, strlen(filename
) + 1);
2343 void WINAPI
PathStripPathW(WCHAR
*path
)
2347 TRACE("%s\n", wine_dbgstr_w(path
));
2348 filename
= PathFindFileNameW(path
);
2349 if (filename
!= path
)
2350 RtlMoveMemory(path
, filename
, (lstrlenW(filename
) + 1) * sizeof(WCHAR
));
2353 BOOL WINAPI
PathSearchAndQualifyA(const char *path
, char *buffer
, UINT length
)
2355 TRACE("%s, %p, %u\n", wine_dbgstr_a(path
), buffer
, length
);
2357 if (SearchPathA(NULL
, path
, NULL
, length
, buffer
, NULL
))
2360 return !!GetFullPathNameA(path
, length
, buffer
, NULL
);
2363 BOOL WINAPI
PathSearchAndQualifyW(const WCHAR
*path
, WCHAR
*buffer
, UINT length
)
2365 TRACE("%s, %p, %u\n", wine_dbgstr_w(path
), buffer
, length
);
2367 if (SearchPathW(NULL
, path
, NULL
, length
, buffer
, NULL
))
2369 return !!GetFullPathNameW(path
, length
, buffer
, NULL
);
2372 BOOL WINAPI
PathRelativePathToA(char *path
, const char *from
, DWORD attributes_from
, const char *to
,
2373 DWORD attributes_to
)
2375 WCHAR pathW
[MAX_PATH
], fromW
[MAX_PATH
], toW
[MAX_PATH
];
2378 TRACE("%p, %s, %#x, %s, %#x\n", path
, wine_dbgstr_a(from
), attributes_from
, wine_dbgstr_a(to
), attributes_to
);
2380 if (!path
|| !from
|| !to
)
2383 MultiByteToWideChar(CP_ACP
, 0, from
, -1, fromW
, ARRAY_SIZE(fromW
));
2384 MultiByteToWideChar(CP_ACP
, 0, to
, -1, toW
, ARRAY_SIZE(toW
));
2385 ret
= PathRelativePathToW(pathW
, fromW
, attributes_from
, toW
, attributes_to
);
2386 WideCharToMultiByte(CP_ACP
, 0, pathW
, -1, path
, MAX_PATH
, 0, 0);
2391 BOOL WINAPI
PathRelativePathToW(WCHAR
*path
, const WCHAR
*from
, DWORD attributes_from
, const WCHAR
*to
,
2392 DWORD attributes_to
)
2394 WCHAR fromW
[MAX_PATH
], toW
[MAX_PATH
];
2397 TRACE("%p, %s, %#x, %s, %#x\n", path
, wine_dbgstr_w(from
), attributes_from
, wine_dbgstr_w(to
), attributes_to
);
2399 if (!path
|| !from
|| !to
)
2403 lstrcpynW(fromW
, from
, ARRAY_SIZE(fromW
));
2404 lstrcpynW(toW
, to
, ARRAY_SIZE(toW
));
2406 if (!(attributes_from
& FILE_ATTRIBUTE_DIRECTORY
))
2407 PathRemoveFileSpecW(fromW
);
2408 if (!(attributes_to
& FILE_ATTRIBUTE_DIRECTORY
))
2409 PathRemoveFileSpecW(toW
);
2411 /* Paths can only be relative if they have a common root */
2412 if (!(len
= PathCommonPrefixW(fromW
, toW
, 0)))
2415 /* Strip off 'from' components to the root, by adding "..\" */
2427 from
= PathFindNextComponentW(from
);
2428 lstrcatW(path
, *from
? L
"..\\" : L
"..");
2431 /* From the root add the components of 'to' */
2433 /* We check to[-1] to avoid skipping end of string. See the notes for this function. */
2438 len
= lstrlenW(path
);
2439 if (len
+ lstrlenW(to
) >= MAX_PATH
)
2444 lstrcpyW(path
+ len
, to
);
2450 BOOL WINAPI
PathMatchSpecA(const char *path
, const char *mask
)
2452 WCHAR
*pathW
, *maskW
;
2455 TRACE("%s, %s\n", wine_dbgstr_a(path
), wine_dbgstr_a(mask
));
2457 if (!lstrcmpA(mask
, "*.*"))
2458 return TRUE
; /* Matches every path */
2460 pathW
= heap_strdupAtoW( path
);
2461 maskW
= heap_strdupAtoW( mask
);
2462 ret
= PathMatchSpecW( pathW
, maskW
);
2468 static BOOL
path_match_maskW(const WCHAR
*name
, const WCHAR
*mask
)
2470 while (*name
&& *mask
&& *mask
!= ';')
2476 if (path_match_maskW(name
, mask
+ 1))
2477 return TRUE
; /* try substrings */
2482 if (towupper(*mask
) != towupper(*name
) && *mask
!= '?')
2491 while (*mask
== '*')
2493 if (!*mask
|| *mask
== ';')
2500 BOOL WINAPI
PathMatchSpecW(const WCHAR
*path
, const WCHAR
*mask
)
2502 TRACE("%s, %s\n", wine_dbgstr_w(path
), wine_dbgstr_w(mask
));
2504 if (!lstrcmpW(mask
, L
"*.*"))
2505 return TRUE
; /* Matches every path */
2509 while (*mask
== ' ')
2510 mask
++; /* Eat leading spaces */
2512 if (path_match_maskW(path
, mask
))
2513 return TRUE
; /* Matches the current path */
2515 while (*mask
&& *mask
!= ';')
2516 mask
++; /* masks separated by ';' */
2525 void WINAPI
PathQuoteSpacesA(char *path
)
2527 TRACE("%s\n", wine_dbgstr_a(path
));
2529 if (path
&& StrChrA(path
, ' '))
2531 size_t len
= strlen(path
) + 1;
2533 if (len
+ 2 < MAX_PATH
)
2535 memmove(path
+ 1, path
, len
);
2538 path
[len
+ 1] = '\0';
2543 void WINAPI
PathQuoteSpacesW(WCHAR
*path
)
2545 TRACE("%s\n", wine_dbgstr_w(path
));
2547 if (path
&& StrChrW(path
, ' '))
2549 int len
= lstrlenW(path
) + 1;
2551 if (len
+ 2 < MAX_PATH
)
2553 memmove(path
+ 1, path
, len
* sizeof(WCHAR
));
2556 path
[len
+ 1] = '\0';
2561 BOOL WINAPI
PathIsSameRootA(const char *path1
, const char *path2
)
2566 TRACE("%s, %s\n", wine_dbgstr_a(path1
), wine_dbgstr_a(path2
));
2568 if (!path1
|| !path2
|| !(start
= PathSkipRootA(path1
)))
2571 len
= PathCommonPrefixA(path1
, path2
, NULL
) + 1;
2572 return start
- path1
<= len
;
2575 BOOL WINAPI
PathIsSameRootW(const WCHAR
*path1
, const WCHAR
*path2
)
2580 TRACE("%s, %s\n", wine_dbgstr_w(path1
), wine_dbgstr_w(path2
));
2582 if (!path1
|| !path2
|| !(start
= PathSkipRootW(path1
)))
2585 len
= PathCommonPrefixW(path1
, path2
, NULL
) + 1;
2586 return start
- path1
<= len
;
2589 BOOL WINAPI
PathFileExistsA(const char *path
)
2594 TRACE("%s\n", wine_dbgstr_a(path
));
2599 /* Prevent a dialog box if path is on a disk that has been ejected. */
2600 prev_mode
= SetErrorMode(SEM_FAILCRITICALERRORS
);
2601 attrs
= GetFileAttributesA(path
);
2602 SetErrorMode(prev_mode
);
2603 return attrs
!= INVALID_FILE_ATTRIBUTES
;
2606 BOOL WINAPI
PathFileExistsW(const WCHAR
*path
)
2611 TRACE("%s\n", wine_dbgstr_w(path
));
2616 prev_mode
= SetErrorMode(SEM_FAILCRITICALERRORS
);
2617 attrs
= GetFileAttributesW(path
);
2618 SetErrorMode(prev_mode
);
2619 return attrs
!= INVALID_FILE_ATTRIBUTES
;
2622 int WINAPI
PathParseIconLocationA(char *path
)
2627 TRACE("%s\n", debugstr_a(path
));
2632 if ((comma
= strchr(path
, ',')))
2635 ret
= StrToIntA(comma
);
2637 PathUnquoteSpacesA(path
);
2638 PathRemoveBlanksA(path
);
2643 int WINAPI
PathParseIconLocationW(WCHAR
*path
)
2648 TRACE("%s\n", debugstr_w(path
));
2653 if ((comma
= StrChrW(path
, ',')))
2656 ret
= StrToIntW(comma
);
2658 PathUnquoteSpacesW(path
);
2659 PathRemoveBlanksW(path
);
2664 BOOL WINAPI
PathUnExpandEnvStringsA(const char *path
, char *buffer
, UINT buf_len
)
2666 WCHAR bufferW
[MAX_PATH
], *pathW
;
2670 TRACE("%s, %p, %d\n", debugstr_a(path
), buffer
, buf_len
);
2672 pathW
= heap_strdupAtoW(path
);
2673 if (!pathW
) return FALSE
;
2675 ret
= PathUnExpandEnvStringsW(pathW
, bufferW
, MAX_PATH
);
2676 HeapFree(GetProcessHeap(), 0, pathW
);
2677 if (!ret
) return FALSE
;
2679 len
= WideCharToMultiByte(CP_ACP
, 0, bufferW
, -1, NULL
, 0, NULL
, NULL
);
2680 if (buf_len
< len
+ 1) return FALSE
;
2682 WideCharToMultiByte(CP_ACP
, 0, bufferW
, -1, buffer
, buf_len
, NULL
, NULL
);
2689 WCHAR path
[MAX_PATH
];
2693 static void init_envvars_map(struct envvars_map
*map
)
2697 map
->len
= ExpandEnvironmentStringsW(map
->var
, map
->path
, ARRAY_SIZE(map
->path
));
2698 /* exclude null from length */
2699 if (map
->len
) map
->len
--;
2704 BOOL WINAPI
PathUnExpandEnvStringsW(const WCHAR
*path
, WCHAR
*buffer
, UINT buf_len
)
2706 static struct envvars_map null_var
= {L
"", {0}, 0};
2707 struct envvars_map
*match
= &null_var
, *cur
;
2708 struct envvars_map envvars
[] =
2710 { L
"%ALLUSERSPROFILE%" },
2712 { L
"%ProgramFiles%" },
2713 { L
"%SystemRoot%" },
2714 { L
"%SystemDrive%" },
2715 { L
"%USERPROFILE%" },
2721 TRACE("%s, %p, %d\n", debugstr_w(path
), buffer
, buf_len
);
2723 pathlen
= lstrlenW(path
);
2724 init_envvars_map(envvars
);
2728 /* path can't contain expanded value or value wasn't retrieved */
2729 if (cur
->len
== 0 || cur
->len
> pathlen
||
2730 CompareStringOrdinal( cur
->path
, cur
->len
, path
, cur
->len
, TRUE
) != CSTR_EQUAL
)
2736 if (cur
->len
> match
->len
)
2741 needed
= lstrlenW(match
->var
) + 1 + pathlen
- match
->len
;
2742 if (match
->len
== 0 || needed
> buf_len
) return FALSE
;
2744 lstrcpyW(buffer
, match
->var
);
2745 lstrcatW(buffer
, &path
[match
->len
]);
2746 TRACE("ret %s\n", debugstr_w(buffer
));
2753 URL_SCHEME scheme_number
;
2754 const WCHAR
*scheme_name
;
2758 { URL_SCHEME_FTP
, L
"ftp"},
2759 { URL_SCHEME_HTTP
, L
"http"},
2760 { URL_SCHEME_GOPHER
, L
"gopher"},
2761 { URL_SCHEME_MAILTO
, L
"mailto"},
2762 { URL_SCHEME_NEWS
, L
"news"},
2763 { URL_SCHEME_NNTP
, L
"nntp"},
2764 { URL_SCHEME_TELNET
, L
"telnet"},
2765 { URL_SCHEME_WAIS
, L
"wais"},
2766 { URL_SCHEME_FILE
, L
"file"},
2767 { URL_SCHEME_MK
, L
"mk"},
2768 { URL_SCHEME_HTTPS
, L
"https"},
2769 { URL_SCHEME_SHELL
, L
"shell"},
2770 { URL_SCHEME_SNEWS
, L
"snews"},
2771 { URL_SCHEME_LOCAL
, L
"local"},
2772 { URL_SCHEME_JAVASCRIPT
, L
"javascript"},
2773 { URL_SCHEME_VBSCRIPT
, L
"vbscript"},
2774 { URL_SCHEME_ABOUT
, L
"about"},
2775 { URL_SCHEME_RES
, L
"res"},
2778 static DWORD
get_scheme_code(const WCHAR
*scheme
, DWORD scheme_len
)
2782 for (i
= 0; i
< ARRAY_SIZE(url_schemes
); ++i
)
2784 if (scheme_len
== lstrlenW(url_schemes
[i
].scheme_name
)
2785 && !wcsnicmp(scheme
, url_schemes
[i
].scheme_name
, scheme_len
))
2786 return url_schemes
[i
].scheme_number
;
2789 return URL_SCHEME_UNKNOWN
;
2792 HRESULT WINAPI
ParseURLA(const char *url
, PARSEDURLA
*result
)
2794 WCHAR scheme
[INTERNET_MAX_SCHEME_LENGTH
];
2795 const char *ptr
= url
;
2798 TRACE("%s, %p\n", wine_dbgstr_a(url
), result
);
2800 if (result
->cbSize
!= sizeof(*result
))
2801 return E_INVALIDARG
;
2803 while (*ptr
&& ((*ptr
>= 'a' && *ptr
<= 'z') || (*ptr
>= 'A' && *ptr
<= 'Z') ||
2804 (*ptr
>= '0' && *ptr
<= '9') || *ptr
== '-' || *ptr
== '+' || *ptr
== '.'))
2807 if (*ptr
!= ':' || ptr
<= url
+ 1)
2809 result
->pszProtocol
= NULL
;
2810 return URL_E_INVALID_SYNTAX
;
2813 result
->pszProtocol
= url
;
2814 result
->cchProtocol
= ptr
- url
;
2815 result
->pszSuffix
= ptr
+ 1;
2816 result
->cchSuffix
= strlen(result
->pszSuffix
);
2818 len
= MultiByteToWideChar(CP_ACP
, 0, url
, ptr
- url
, scheme
, ARRAY_SIZE(scheme
));
2819 result
->nScheme
= get_scheme_code(scheme
, len
);
2824 HRESULT WINAPI
ParseURLW(const WCHAR
*url
, PARSEDURLW
*result
)
2826 const WCHAR
*ptr
= url
;
2828 TRACE("%s, %p\n", wine_dbgstr_w(url
), result
);
2830 if (result
->cbSize
!= sizeof(*result
))
2831 return E_INVALIDARG
;
2833 while (*ptr
&& (isalnum(*ptr
) || *ptr
== '-' || *ptr
== '+' || *ptr
== '.'))
2836 if (*ptr
!= ':' || ptr
<= url
+ 1)
2838 result
->pszProtocol
= NULL
;
2839 return URL_E_INVALID_SYNTAX
;
2842 result
->pszProtocol
= url
;
2843 result
->cchProtocol
= ptr
- url
;
2844 result
->pszSuffix
= ptr
+ 1;
2845 result
->cchSuffix
= lstrlenW(result
->pszSuffix
);
2846 result
->nScheme
= get_scheme_code(url
, ptr
- url
);
2851 HRESULT WINAPI
UrlUnescapeA(char *url
, char *unescaped
, DWORD
*unescaped_len
, DWORD flags
)
2853 BOOL stop_unescaping
= FALSE
;
2859 TRACE("%s, %p, %p, %#x\n", wine_dbgstr_a(url
), unescaped
, unescaped_len
, flags
);
2862 return E_INVALIDARG
;
2864 if (flags
& URL_UNESCAPE_INPLACE
)
2868 if (!unescaped
|| !unescaped_len
) return E_INVALIDARG
;
2872 for (src
= url
, needed
= 0; *src
; src
++, needed
++)
2874 if (flags
& URL_DONT_UNESCAPE_EXTRA_INFO
&& (*src
== '#' || *src
== '?'))
2876 stop_unescaping
= TRUE
;
2879 else if (*src
== '%' && isxdigit(*(src
+ 1)) && isxdigit(*(src
+ 2)) && !stop_unescaping
)
2883 memcpy(buf
, src
+ 1, 2);
2885 ih
= strtol(buf
, NULL
, 16);
2887 src
+= 2; /* Advance to end of escape */
2892 if (flags
& URL_UNESCAPE_INPLACE
|| needed
< *unescaped_len
)
2896 if (flags
& URL_UNESCAPE_INPLACE
|| needed
< *unescaped_len
)
2903 needed
++; /* add one for the '\0' */
2907 if (!(flags
& URL_UNESCAPE_INPLACE
))
2908 *unescaped_len
= needed
;
2911 TRACE("result %s\n", flags
& URL_UNESCAPE_INPLACE
? wine_dbgstr_a(url
) : wine_dbgstr_a(unescaped
));
2916 HRESULT WINAPI
UrlUnescapeW(WCHAR
*url
, WCHAR
*unescaped
, DWORD
*unescaped_len
, DWORD flags
)
2918 BOOL stop_unescaping
= FALSE
;
2924 TRACE("%s, %p, %p, %#x\n", wine_dbgstr_w(url
), unescaped
, unescaped_len
, flags
);
2927 return E_INVALIDARG
;
2929 if (flags
& URL_UNESCAPE_INPLACE
)
2933 if (!unescaped
|| !unescaped_len
) return E_INVALIDARG
;
2937 for (src
= url
, needed
= 0; *src
; src
++, needed
++)
2939 if (flags
& URL_DONT_UNESCAPE_EXTRA_INFO
&& (*src
== '#' || *src
== '?'))
2941 stop_unescaping
= TRUE
;
2944 else if (*src
== '%' && isxdigit(*(src
+ 1)) && isxdigit(*(src
+ 2)) && !stop_unescaping
)
2947 WCHAR buf
[5] = L
"0x";
2948 memcpy(buf
+ 2, src
+ 1, 2*sizeof(WCHAR
));
2950 StrToIntExW(buf
, STIF_SUPPORT_HEX
, &ih
);
2952 src
+= 2; /* Advance to end of escape */
2957 if (flags
& URL_UNESCAPE_INPLACE
|| needed
< *unescaped_len
)
2961 if (flags
& URL_UNESCAPE_INPLACE
|| needed
< *unescaped_len
)
2968 needed
++; /* add one for the '\0' */
2972 if (!(flags
& URL_UNESCAPE_INPLACE
))
2973 *unescaped_len
= needed
;
2976 TRACE("result %s\n", flags
& URL_UNESCAPE_INPLACE
? wine_dbgstr_w(url
) : wine_dbgstr_w(unescaped
));
2981 HRESULT WINAPI
PathCreateFromUrlA(const char *pszUrl
, char *pszPath
, DWORD
*pcchPath
, DWORD dwReserved
)
2983 WCHAR bufW
[MAX_PATH
];
2984 WCHAR
*pathW
= bufW
;
2985 UNICODE_STRING urlW
;
2987 DWORD lenW
= ARRAY_SIZE(bufW
), lenA
;
2989 if (!pszUrl
|| !pszPath
|| !pcchPath
|| !*pcchPath
)
2990 return E_INVALIDARG
;
2992 if(!RtlCreateUnicodeStringFromAsciiz(&urlW
, pszUrl
))
2993 return E_INVALIDARG
;
2994 if((ret
= PathCreateFromUrlW(urlW
.Buffer
, pathW
, &lenW
, dwReserved
)) == E_POINTER
) {
2995 pathW
= HeapAlloc(GetProcessHeap(), 0, lenW
* sizeof(WCHAR
));
2996 ret
= PathCreateFromUrlW(urlW
.Buffer
, pathW
, &lenW
, dwReserved
);
2999 RtlUnicodeToMultiByteSize(&lenA
, pathW
, lenW
* sizeof(WCHAR
));
3000 if(*pcchPath
> lenA
) {
3001 RtlUnicodeToMultiByteN(pszPath
, *pcchPath
- 1, &lenA
, pathW
, lenW
* sizeof(WCHAR
));
3005 *pcchPath
= lenA
+ 1;
3009 if(pathW
!= bufW
) HeapFree(GetProcessHeap(), 0, pathW
);
3010 RtlFreeUnicodeString(&urlW
);
3014 HRESULT WINAPI
PathCreateFromUrlW(const WCHAR
*url
, WCHAR
*path
, DWORD
*pcchPath
, DWORD dwReserved
)
3016 DWORD nslashes
, unescape
, len
;
3021 TRACE("%s, %p, %p, %#x\n", wine_dbgstr_w(url
), path
, pcchPath
, dwReserved
);
3023 if (!url
|| !path
|| !pcchPath
|| !*pcchPath
)
3024 return E_INVALIDARG
;
3026 if (wcsnicmp( url
, L
"file:", 5))
3027 return E_INVALIDARG
;
3033 while (*src
== '/' || *src
== '\\')
3039 /* We need a temporary buffer so we can compute what size to ask for.
3040 * We know that the final string won't be longer than the current pszUrl
3041 * plus at most two backslashes. All the other transformations make it
3044 len
= 2 + lstrlenW(url
) + 1;
3045 if (*pcchPath
< len
)
3046 tpath
= heap_alloc(len
* sizeof(WCHAR
));
3056 /* 'file:' + escaped DOS path */
3059 /* 'file:/' + escaped DOS path */
3062 /* 'file:///' (implied localhost) + escaped DOS path */
3063 if (!is_escaped_drive_spec( src
))
3067 if (lstrlenW(src
) >= 10 && !wcsnicmp( src
, L
"localhost", 9) && (src
[9] == '/' || src
[9] == '\\'))
3069 /* 'file://localhost/' + escaped DOS path */
3072 else if (is_escaped_drive_spec( src
))
3074 /* 'file://' + unescaped DOS path */
3079 /* 'file://hostname:port/path' (where path is escaped)
3080 * or 'file:' + escaped UNC path (\\server\share\path)
3081 * The second form is clearly specific to Windows and it might
3082 * even be doing a network lookup to try to figure it out.
3084 while (*src
&& *src
!= '/' && *src
!= '\\')
3087 StrCpyNW(dst
, url
, len
+ 1);
3089 if (*src
&& is_escaped_drive_spec( src
+ 1 ))
3091 /* 'Forget' to add a trailing '/', just like Windows */
3097 /* 'file://' + unescaped UNC path (\\server\share\path) */
3099 if (is_escaped_drive_spec( src
))
3103 /* 'file:/...' + escaped UNC path (\\server\share\path) */
3107 /* Copy the remainder of the path */
3108 len
+= lstrlenW(src
);
3111 /* First do the Windows-specific path conversions */
3112 for (dst
= tpath
; *dst
; dst
++)
3113 if (*dst
== '/') *dst
= '\\';
3114 if (is_escaped_drive_spec( tpath
))
3115 tpath
[1] = ':'; /* c| -> c: */
3117 /* And only then unescape the path (i.e. escaped slashes are left as is) */
3120 hr
= UrlUnescapeW(tpath
, NULL
, &len
, URL_UNESCAPE_INPLACE
);
3123 /* When working in-place UrlUnescapeW() does not set len */
3124 len
= lstrlenW(tpath
);
3128 if (*pcchPath
< len
+ 1)
3131 *pcchPath
= len
+ 1;
3137 lstrcpyW(path
, tpath
);
3142 TRACE("Returning (%u) %s\n", *pcchPath
, wine_dbgstr_w(path
));
3146 HRESULT WINAPI
PathCreateFromUrlAlloc(const WCHAR
*url
, WCHAR
**path
, DWORD reserved
)
3148 WCHAR pathW
[MAX_PATH
];
3153 hr
= PathCreateFromUrlW(url
, pathW
, &size
, reserved
);
3156 /* Yes, this is supposed to crash if 'path' is NULL */
3157 *path
= StrDupW(pathW
);
3163 BOOL WINAPI
PathIsURLA(const char *path
)
3168 TRACE("%s\n", wine_dbgstr_a(path
));
3170 if (!path
|| !*path
)
3174 base
.cbSize
= sizeof(base
);
3175 hr
= ParseURLA(path
, &base
);
3176 return hr
== S_OK
&& (base
.nScheme
!= URL_SCHEME_INVALID
);
3179 BOOL WINAPI
PathIsURLW(const WCHAR
*path
)
3184 TRACE("%s\n", wine_dbgstr_w(path
));
3186 if (!path
|| !*path
)
3190 base
.cbSize
= sizeof(base
);
3191 hr
= ParseURLW(path
, &base
);
3192 return hr
== S_OK
&& (base
.nScheme
!= URL_SCHEME_INVALID
);
3195 #define WINE_URL_BASH_AS_SLASH 0x01
3196 #define WINE_URL_COLLAPSE_SLASHES 0x02
3197 #define WINE_URL_ESCAPE_SLASH 0x04
3198 #define WINE_URL_ESCAPE_HASH 0x08
3199 #define WINE_URL_ESCAPE_QUESTION 0x10
3200 #define WINE_URL_STOP_ON_HASH 0x20
3201 #define WINE_URL_STOP_ON_QUESTION 0x40
3203 static BOOL
url_needs_escape(WCHAR ch
, DWORD flags
, DWORD int_flags
)
3205 if (flags
& URL_ESCAPE_SPACES_ONLY
)
3208 if ((flags
& URL_ESCAPE_PERCENT
) && (ch
== '%'))
3211 if ((flags
& URL_ESCAPE_AS_UTF8
) && (ch
>= 0x80))
3214 if (ch
<= 31 || (ch
>= 127 && ch
<= 255) )
3236 return !!(int_flags
& WINE_URL_ESCAPE_SLASH
);
3238 return !!(int_flags
& WINE_URL_ESCAPE_QUESTION
);
3240 return !!(int_flags
& WINE_URL_ESCAPE_HASH
);
3246 HRESULT WINAPI
UrlEscapeA(const char *url
, char *escaped
, DWORD
*escaped_len
, DWORD flags
)
3248 WCHAR bufW
[INTERNET_MAX_URL_LENGTH
];
3249 WCHAR
*escapedW
= bufW
;
3250 UNICODE_STRING urlW
;
3252 DWORD lenW
= ARRAY_SIZE(bufW
), lenA
;
3254 if (!escaped
|| !escaped_len
|| !*escaped_len
)
3255 return E_INVALIDARG
;
3257 if (!RtlCreateUnicodeStringFromAsciiz(&urlW
, url
))
3258 return E_INVALIDARG
;
3260 if (flags
& URL_ESCAPE_AS_UTF8
)
3262 RtlFreeUnicodeString(&urlW
);
3266 if ((hr
= UrlEscapeW(urlW
.Buffer
, escapedW
, &lenW
, flags
)) == E_POINTER
)
3268 escapedW
= heap_alloc(lenW
* sizeof(WCHAR
));
3269 hr
= UrlEscapeW(urlW
.Buffer
, escapedW
, &lenW
, flags
);
3274 RtlUnicodeToMultiByteSize(&lenA
, escapedW
, lenW
* sizeof(WCHAR
));
3275 if (*escaped_len
> lenA
)
3277 RtlUnicodeToMultiByteN(escaped
, *escaped_len
- 1, &lenA
, escapedW
, lenW
* sizeof(WCHAR
));
3279 *escaped_len
= lenA
;
3283 *escaped_len
= lenA
+ 1;
3287 if (escapedW
!= bufW
)
3288 heap_free(escapedW
);
3289 RtlFreeUnicodeString(&urlW
);
3293 HRESULT WINAPI
UrlEscapeW(const WCHAR
*url
, WCHAR
*escaped
, DWORD
*escaped_len
, DWORD flags
)
3295 DWORD needed
= 0, slashes
= 0, int_flags
;
3296 WCHAR next
[12], *dst
, *dst_ptr
;
3297 BOOL stop_escaping
= FALSE
;
3298 PARSEDURLW parsed_url
;
3303 TRACE("%p, %s, %p, %p, %#x\n", url
, wine_dbgstr_w(url
), escaped
, escaped_len
, flags
);
3305 if (!url
|| !escaped_len
|| !escaped
|| *escaped_len
== 0)
3306 return E_INVALIDARG
;
3308 if (flags
& ~(URL_ESCAPE_SPACES_ONLY
| URL_ESCAPE_SEGMENT_ONLY
| URL_DONT_ESCAPE_EXTRA_INFO
|
3309 URL_ESCAPE_PERCENT
| URL_ESCAPE_AS_UTF8
))
3311 FIXME("Unimplemented flags: %08x\n", flags
);
3314 dst_ptr
= dst
= heap_alloc(*escaped_len
* sizeof(WCHAR
));
3316 return E_OUTOFMEMORY
;
3319 if (flags
& URL_ESCAPE_SPACES_ONLY
)
3320 /* if SPACES_ONLY specified, reset the other controls */
3321 flags
&= ~(URL_DONT_ESCAPE_EXTRA_INFO
| URL_ESCAPE_PERCENT
| URL_ESCAPE_SEGMENT_ONLY
);
3323 /* if SPACES_ONLY *not* specified the assume DONT_ESCAPE_EXTRA_INFO */
3324 flags
|= URL_DONT_ESCAPE_EXTRA_INFO
;
3327 if (flags
& URL_ESCAPE_SEGMENT_ONLY
)
3328 int_flags
= WINE_URL_ESCAPE_QUESTION
| WINE_URL_ESCAPE_HASH
| WINE_URL_ESCAPE_SLASH
;
3331 parsed_url
.cbSize
= sizeof(parsed_url
);
3332 if (ParseURLW(url
, &parsed_url
) != S_OK
)
3333 parsed_url
.nScheme
= URL_SCHEME_INVALID
;
3335 TRACE("scheme = %d (%s)\n", parsed_url
.nScheme
, debugstr_wn(parsed_url
.pszProtocol
, parsed_url
.cchProtocol
));
3337 if (flags
& URL_DONT_ESCAPE_EXTRA_INFO
)
3338 int_flags
= WINE_URL_STOP_ON_HASH
| WINE_URL_STOP_ON_QUESTION
;
3340 switch(parsed_url
.nScheme
) {
3341 case URL_SCHEME_FILE
:
3342 int_flags
|= WINE_URL_BASH_AS_SLASH
| WINE_URL_COLLAPSE_SLASHES
| WINE_URL_ESCAPE_HASH
;
3343 int_flags
&= ~WINE_URL_STOP_ON_HASH
;
3346 case URL_SCHEME_HTTP
:
3347 case URL_SCHEME_HTTPS
:
3348 int_flags
|= WINE_URL_BASH_AS_SLASH
;
3349 if(parsed_url
.pszSuffix
[0] != '/' && parsed_url
.pszSuffix
[0] != '\\')
3350 int_flags
|= WINE_URL_ESCAPE_SLASH
;
3353 case URL_SCHEME_MAILTO
:
3354 int_flags
|= WINE_URL_ESCAPE_SLASH
| WINE_URL_ESCAPE_QUESTION
| WINE_URL_ESCAPE_HASH
;
3355 int_flags
&= ~(WINE_URL_STOP_ON_QUESTION
| WINE_URL_STOP_ON_HASH
);
3358 case URL_SCHEME_INVALID
:
3361 case URL_SCHEME_FTP
:
3363 if(parsed_url
.pszSuffix
[0] != '/')
3364 int_flags
|= WINE_URL_ESCAPE_SLASH
;
3369 for (src
= url
; *src
; )
3374 if ((int_flags
& WINE_URL_COLLAPSE_SLASHES
) && src
== url
+ parsed_url
.cchProtocol
+ 1)
3376 while (cur
== '/' || cur
== '\\')
3381 if (slashes
== 2 && !wcsnicmp(src
, L
"localhost", 9)) { /* file://localhost/ -> file:/// */
3382 if(src
[9] == '/' || src
[9] == '\\') src
+= 10;
3390 next
[0] = next
[1] = next
[2] = '/';
3397 next
[0] = next
[1] = '/';
3404 if (cur
== '#' && (int_flags
& WINE_URL_STOP_ON_HASH
))
3405 stop_escaping
= TRUE
;
3407 if (cur
== '?' && (int_flags
& WINE_URL_STOP_ON_QUESTION
))
3408 stop_escaping
= TRUE
;
3410 if (cur
== '\\' && (int_flags
& WINE_URL_BASH_AS_SLASH
) && !stop_escaping
) cur
= '/';
3412 if (url_needs_escape(cur
, flags
, int_flags
) && !stop_escaping
)
3414 if (flags
& URL_ESCAPE_AS_UTF8
)
3418 if ((cur
>= 0xd800 && cur
<= 0xdfff) && (src
[1] >= 0xdc00 && src
[1] <= 0xdfff))
3420 len
= WideCharToMultiByte(CP_UTF8
, WC_ERR_INVALID_CHARS
, src
, 2, utf
, sizeof(utf
), NULL
, NULL
);
3424 len
= WideCharToMultiByte(CP_UTF8
, WC_ERR_INVALID_CHARS
, &cur
, 1, utf
, sizeof(utf
), NULL
, NULL
);
3434 for (i
= 0; i
< len
; ++i
)
3437 next
[i
*3+1] = hexDigits
[(utf
[i
] >> 4) & 0xf];
3438 next
[i
*3+2] = hexDigits
[utf
[i
] & 0xf];
3445 next
[1] = hexDigits
[(cur
>> 4) & 0xf];
3446 next
[2] = hexDigits
[cur
& 0xf];
3458 if (needed
+ len
<= *escaped_len
)
3460 memcpy(dst
, next
, len
*sizeof(WCHAR
));
3466 if (needed
< *escaped_len
)
3469 memcpy(escaped
, dst_ptr
, (needed
+1)*sizeof(WCHAR
));
3474 needed
++; /* add one for the '\0' */
3477 *escaped_len
= needed
;
3483 HRESULT WINAPI
UrlCanonicalizeA(const char *src_url
, char *canonicalized
, DWORD
*canonicalized_len
, DWORD flags
)
3485 LPWSTR url
, canonical
;
3488 TRACE("%s, %p, %p, %#x\n", wine_dbgstr_a(src_url
), canonicalized
, canonicalized_len
, flags
);
3490 if (!src_url
|| !canonicalized
|| !canonicalized_len
|| !*canonicalized_len
)
3491 return E_INVALIDARG
;
3493 url
= heap_strdupAtoW(src_url
);
3494 canonical
= heap_alloc(*canonicalized_len
* sizeof(WCHAR
));
3495 if (!url
|| !canonical
)
3498 heap_free(canonical
);
3499 return E_OUTOFMEMORY
;
3502 hr
= UrlCanonicalizeW(url
, canonical
, canonicalized_len
, flags
);
3504 WideCharToMultiByte(CP_ACP
, 0, canonical
, -1, canonicalized
, *canonicalized_len
+ 1, NULL
, NULL
);
3507 heap_free(canonical
);
3511 HRESULT WINAPI
UrlCanonicalizeW(const WCHAR
*src_url
, WCHAR
*canonicalized
, DWORD
*canonicalized_len
, DWORD flags
)
3513 WCHAR
*url_copy
, *url
, *wk2
, *mp
, *mp2
;
3514 DWORD nByteLen
, nLen
, nWkLen
;
3515 const WCHAR
*wk1
, *root
;
3522 TRACE("%s, %p, %p, %#x\n", wine_dbgstr_w(src_url
), canonicalized
, canonicalized_len
, flags
);
3524 if (!src_url
|| !canonicalized
|| !canonicalized_len
|| !*canonicalized_len
)
3525 return E_INVALIDARG
;
3533 /* Remove '\t' characters from URL */
3534 nByteLen
= (lstrlenW(src_url
) + 1) * sizeof(WCHAR
); /* length in bytes */
3535 url
= HeapAlloc(GetProcessHeap(), 0, nByteLen
);
3537 return E_OUTOFMEMORY
;
3548 /* Allocate memory for simplified URL (before escaping) */
3549 nByteLen
= (wk2
-url
)*sizeof(WCHAR
);
3550 url_copy
= heap_alloc(nByteLen
+ sizeof(L
"file:///"));
3554 return E_OUTOFMEMORY
;
3557 is_file_url
= !wcsncmp(url
, L
"file:", 5);
3559 if ((nByteLen
>= 5*sizeof(WCHAR
) && !wcsncmp(url
, L
"http:", 5)) || is_file_url
)
3562 if ((flags
& (URL_FILE_USE_PATHURL
| URL_WININET_COMPATIBILITY
)) && is_file_url
)
3565 if (nByteLen
>= 4*sizeof(WCHAR
) && !wcsncmp(url
, L
"res:", 4))
3567 flags
&= ~URL_FILE_USE_PATHURL
;
3574 * 1 have 2[+] alnum 2,3
3575 * 2 have scheme (found :) 4,6,3
3576 * 3 failed (no location)
3578 * 5 have 1[+] alnum 6,3
3579 * 6 have location (found /) save root location
3589 lstrcpyW(wk2
, L
"file:///");
3590 wk2
+= lstrlenW(wk2
);
3591 if (flags
& (URL_FILE_USE_PATHURL
| URL_WININET_COMPATIBILITY
))
3597 flags
|= URL_ESCAPE_UNSAFE
;
3601 else if (url
[0] == '/')
3612 if (!isalnum(*wk1
)) {state
= 3; break;}
3614 if (!isalnum(*wk1
)) {state
= 3; break;}
3620 if (*wk1
++ == ':') state
= 2;
3624 if (*wk1
!= '/') {state
= 6; break;}
3626 if ((flags
& URL_FILE_USE_PATHURL
) && nByteLen
>= 9*sizeof(WCHAR
) && is_file_url
3627 && !wcsncmp(wk1
, L
"localhost", 9))
3630 while (*wk1
== '\\' && (flags
& URL_FILE_USE_PATHURL
))
3634 if (*wk1
== '/' && (flags
& URL_FILE_USE_PATHURL
))
3636 else if (is_file_url
)
3638 const WCHAR
*body
= wk1
;
3640 while (*body
== '/')
3643 if (is_drive_spec( body
))
3645 if (!(flags
& (URL_WININET_COMPATIBILITY
| URL_FILE_USE_PATHURL
)))
3655 if (flags
& URL_WININET_COMPATIBILITY
)
3657 if (*wk1
== '/' && *(wk1
+ 1) != '/')
3669 if (*wk1
== '/' && *(wk1
+1) != '/')
3683 nWkLen
= lstrlenW(wk1
);
3684 memcpy(wk2
, wk1
, (nWkLen
+ 1) * sizeof(WCHAR
));
3693 if (*mp
== '/' || *mp
== '\\')
3700 if (!isalnum(*wk1
) && (*wk1
!= '-') && (*wk1
!= '.') && (*wk1
!= ':'))
3705 while (isalnum(*wk1
) || (*wk1
== '-') || (*wk1
== '.') || (*wk1
== ':'))
3717 if (*wk1
!= '/' && *wk1
!= '\\')
3722 while (*wk1
== '/' || *wk1
== '\\')
3733 if (flags
& URL_DONT_SIMPLIFY
)
3739 /* Now at root location, cannot back up any more. */
3740 /* "root" will point at the '/' */
3745 mp
= wcschr(wk1
, '/');
3746 mp2
= wcschr(wk1
, '\\');
3747 if (mp2
&& (!mp
|| mp2
< mp
))
3751 nWkLen
= lstrlenW(wk1
);
3752 memcpy(wk2
, wk1
, (nWkLen
+ 1) * sizeof(WCHAR
));
3760 memcpy(wk2
, wk1
, nLen
* sizeof(WCHAR
));
3772 TRACE("found '/.'\n");
3773 if (wk1
[1] == '/' || wk1
[1] == '\\')
3775 /* case of /./ -> skip the ./ */
3778 else if (wk1
[1] == '.' && (wk1
[2] == '/' || wk1
[2] == '\\' || wk1
[2] == '?'
3779 || wk1
[2] == '#' || !wk1
[2]))
3781 /* case /../ -> need to backup wk2 */
3782 TRACE("found '/../'\n");
3783 *(wk2
-1) = '\0'; /* set end of string */
3784 mp
= wcsrchr(root
, '/');
3785 mp2
= wcsrchr(root
, '\\');
3786 if (mp2
&& (!mp
|| mp2
< mp
))
3788 if (mp
&& (mp
>= root
))
3790 /* found valid backup point */
3792 if(wk1
[2] != '/' && wk1
[2] != '\\')
3799 /* did not find point, restore '/' */
3811 FIXME("how did we get here - state=%d\n", state
);
3812 heap_free(url_copy
);
3814 return E_INVALIDARG
;
3817 TRACE("Simplified, orig <%s>, simple <%s>\n", wine_dbgstr_w(src_url
), wine_dbgstr_w(url_copy
));
3819 nLen
= lstrlenW(url_copy
);
3820 while ((nLen
> 0) && ((url_copy
[nLen
-1] <= ' ')))
3823 if ((flags
& URL_UNESCAPE
) ||
3824 ((flags
& URL_FILE_USE_PATHURL
) && nByteLen
>= 5*sizeof(WCHAR
) && !wcsncmp(url
, L
"file:", 5)))
3826 UrlUnescapeW(url_copy
, NULL
, &nLen
, URL_UNESCAPE_INPLACE
);
3829 escape_flags
= flags
& (URL_ESCAPE_UNSAFE
| URL_ESCAPE_SPACES_ONLY
| URL_ESCAPE_PERCENT
|
3830 URL_DONT_ESCAPE_EXTRA_INFO
| URL_ESCAPE_SEGMENT_ONLY
);
3834 escape_flags
&= ~URL_ESCAPE_UNSAFE
;
3835 hr
= UrlEscapeW(url_copy
, canonicalized
, canonicalized_len
, escape_flags
);
3839 /* No escaping needed, just copy the string */
3840 nLen
= lstrlenW(url_copy
);
3841 if (nLen
< *canonicalized_len
)
3842 memcpy(canonicalized
, url_copy
, (nLen
+ 1)*sizeof(WCHAR
));
3848 *canonicalized_len
= nLen
;
3851 heap_free(url_copy
);
3855 TRACE("result %s\n", wine_dbgstr_w(canonicalized
));
3860 HRESULT WINAPI
UrlApplySchemeA(const char *url
, char *out
, DWORD
*out_len
, DWORD flags
)
3866 TRACE("%s, %p, %p:out size %d, %#x\n", wine_dbgstr_a(url
), out
, out_len
, out_len
? *out_len
: 0, flags
);
3868 if (!url
|| !out
|| !out_len
)
3869 return E_INVALIDARG
;
3871 inW
= heap_alloc(2 * INTERNET_MAX_URL_LENGTH
* sizeof(WCHAR
));
3872 outW
= inW
+ INTERNET_MAX_URL_LENGTH
;
3874 MultiByteToWideChar(CP_ACP
, 0, url
, -1, inW
, INTERNET_MAX_URL_LENGTH
);
3875 len
= INTERNET_MAX_URL_LENGTH
;
3877 hr
= UrlApplySchemeW(inW
, outW
, &len
, flags
);
3884 len
= WideCharToMultiByte(CP_ACP
, 0, outW
, -1, NULL
, 0, NULL
, NULL
);
3891 WideCharToMultiByte(CP_ACP
, 0, outW
, -1, out
, *out_len
, NULL
, NULL
);
3900 static HRESULT
url_guess_scheme(const WCHAR
*url
, WCHAR
*out
, DWORD
*out_len
)
3902 WCHAR reg_path
[MAX_PATH
], value
[MAX_PATH
], data
[MAX_PATH
];
3903 DWORD value_len
, data_len
, dwType
, i
;
3909 MultiByteToWideChar(CP_ACP
, 0,
3910 "Software\\Microsoft\\Windows\\CurrentVersion\\URL\\Prefixes", -1, reg_path
, MAX_PATH
);
3911 RegOpenKeyExW(HKEY_LOCAL_MACHINE
, reg_path
, 0, 1, &newkey
);
3913 while (value_len
= data_len
= MAX_PATH
,
3914 RegEnumValueW(newkey
, index
, value
, &value_len
, 0, &dwType
, (LPVOID
)data
, &data_len
) == 0)
3916 TRACE("guess %d %s is %s\n", index
, wine_dbgstr_w(value
), wine_dbgstr_w(data
));
3919 for (i
= 0; i
< value_len
; ++i
)
3923 /* remember that TRUE is not-equal */
3924 j
= ChrCmpIW(Wxx
, Wyy
);
3927 if ((i
== value_len
) && !j
)
3929 if (lstrlenW(data
) + lstrlenW(url
) + 1 > *out_len
)
3931 *out_len
= lstrlenW(data
) + lstrlenW(url
) + 1;
3932 RegCloseKey(newkey
);
3935 lstrcpyW(out
, data
);
3937 *out_len
= lstrlenW(out
);
3938 TRACE("matched and set to %s\n", wine_dbgstr_w(out
));
3939 RegCloseKey(newkey
);
3944 RegCloseKey(newkey
);
3948 static HRESULT
url_create_from_path(const WCHAR
*path
, WCHAR
*url
, DWORD
*url_len
)
3950 PARSEDURLW parsed_url
;
3955 parsed_url
.cbSize
= sizeof(parsed_url
);
3956 if (ParseURLW(path
, &parsed_url
) == S_OK
)
3958 if (parsed_url
.nScheme
!= URL_SCHEME_INVALID
&& parsed_url
.cchProtocol
> 1)
3960 needed
= lstrlenW(path
);
3961 if (needed
>= *url_len
)
3963 *url_len
= needed
+ 1;
3974 new_url
= heap_alloc((lstrlenW(path
) + 9) * sizeof(WCHAR
)); /* "file:///" + path length + 1 */
3975 lstrcpyW(new_url
, L
"file:");
3976 if (is_drive_spec( path
)) lstrcatW(new_url
, L
"///");
3977 lstrcatW(new_url
, path
);
3978 hr
= UrlEscapeW(new_url
, url
, url_len
, URL_ESCAPE_PERCENT
);
3983 static HRESULT
url_apply_default_scheme(const WCHAR
*url
, WCHAR
*out
, DWORD
*length
)
3985 DWORD data_len
, dwType
;
3986 WCHAR data
[MAX_PATH
];
3989 /* get and prepend default */
3990 RegOpenKeyExW(HKEY_LOCAL_MACHINE
, L
"Software\\Microsoft\\Windows\\CurrentVersion\\URL\\DefaultPrefix",
3992 data_len
= sizeof(data
);
3993 RegQueryValueExW(newkey
, NULL
, 0, &dwType
, (BYTE
*)data
, &data_len
);
3994 RegCloseKey(newkey
);
3995 if (lstrlenW(data
) + lstrlenW(url
) + 1 > *length
)
3997 *length
= lstrlenW(data
) + lstrlenW(url
) + 1;
4000 lstrcpyW(out
, data
);
4002 *length
= lstrlenW(out
);
4003 TRACE("used default %s\n", wine_dbgstr_w(out
));
4007 HRESULT WINAPI
UrlApplySchemeW(const WCHAR
*url
, WCHAR
*out
, DWORD
*length
, DWORD flags
)
4009 PARSEDURLW in_scheme
;
4013 TRACE("%s, %p, %p:out size %d, %#x\n", wine_dbgstr_w(url
), out
, length
, length
? *length
: 0, flags
);
4015 if (!url
|| !out
|| !length
)
4016 return E_INVALIDARG
;
4018 if (flags
& URL_APPLY_GUESSFILE
)
4020 if (*length
> 1 && ':' == url
[1])
4023 hr
= url_create_from_path(url
, out
, &res1
);
4024 if (hr
== S_OK
|| hr
== E_POINTER
)
4029 else if (hr
== S_FALSE
)
4036 in_scheme
.cbSize
= sizeof(in_scheme
);
4037 /* See if the base has a scheme */
4038 res1
= ParseURLW(url
, &in_scheme
);
4041 /* no scheme in input, need to see if we need to guess */
4042 if (flags
& URL_APPLY_GUESSSCHEME
)
4044 if ((hr
= url_guess_scheme(url
, out
, length
)) != E_FAIL
)
4049 /* If we are here, then either invalid scheme,
4050 * or no scheme and can't/failed guess.
4052 if ((((res1
== 0) && (flags
& URL_APPLY_FORCEAPPLY
)) || ((res1
!= 0)) ) && (flags
& URL_APPLY_DEFAULT
))
4053 return url_apply_default_scheme(url
, out
, length
);
4058 INT WINAPI
UrlCompareA(const char *url1
, const char *url2
, BOOL ignore_slash
)
4060 INT ret
, len
, len1
, len2
;
4063 return strcmp(url1
, url2
);
4064 len1
= strlen(url1
);
4065 if (url1
[len1
-1] == '/') len1
--;
4066 len2
= strlen(url2
);
4067 if (url2
[len2
-1] == '/') len2
--;
4069 return strncmp(url1
, url2
, len1
);
4070 len
= min(len1
, len2
);
4071 ret
= strncmp(url1
, url2
, len
);
4072 if (ret
) return ret
;
4073 if (len1
> len2
) return 1;
4077 INT WINAPI
UrlCompareW(const WCHAR
*url1
, const WCHAR
*url2
, BOOL ignore_slash
)
4079 size_t len
, len1
, len2
;
4083 return lstrcmpW(url1
, url2
);
4084 len1
= lstrlenW(url1
);
4085 if (url1
[len1
-1] == '/') len1
--;
4086 len2
= lstrlenW(url2
);
4087 if (url2
[len2
-1] == '/') len2
--;
4089 return wcsncmp(url1
, url2
, len1
);
4090 len
= min(len1
, len2
);
4091 ret
= wcsncmp(url1
, url2
, len
);
4092 if (ret
) return ret
;
4093 if (len1
> len2
) return 1;
4097 HRESULT WINAPI
UrlFixupW(const WCHAR
*url
, WCHAR
*translatedUrl
, DWORD maxChars
)
4101 FIXME("%s, %p, %d stub\n", wine_dbgstr_w(url
), translatedUrl
, maxChars
);
4106 srcLen
= lstrlenW(url
) + 1;
4108 /* For now just copy the URL directly */
4109 lstrcpynW(translatedUrl
, url
, (maxChars
< srcLen
) ? maxChars
: srcLen
);
4114 const char * WINAPI
UrlGetLocationA(const char *url
)
4118 base
.cbSize
= sizeof(base
);
4119 if (ParseURLA(url
, &base
) != S_OK
) return NULL
; /* invalid scheme */
4121 /* if scheme is file: then never return pointer */
4122 if (!strncmp(base
.pszProtocol
, "file", min(4, base
.cchProtocol
)))
4125 /* Look for '#' and return its addr */
4126 return strchr(base
.pszSuffix
, '#');
4129 const WCHAR
* WINAPI
UrlGetLocationW(const WCHAR
*url
)
4133 base
.cbSize
= sizeof(base
);
4134 if (ParseURLW(url
, &base
) != S_OK
) return NULL
; /* invalid scheme */
4136 /* if scheme is file: then never return pointer */
4137 if (!wcsncmp(base
.pszProtocol
, L
"file", min(4, base
.cchProtocol
)))
4140 /* Look for '#' and return its addr */
4141 return wcschr(base
.pszSuffix
, '#');
4144 HRESULT WINAPI
UrlGetPartA(const char *url
, char *out
, DWORD
*out_len
, DWORD part
, DWORD flags
)
4150 if (!url
|| !out
|| !out_len
|| !*out_len
)
4151 return E_INVALIDARG
;
4153 inW
= heap_alloc(2 * INTERNET_MAX_URL_LENGTH
* sizeof(WCHAR
));
4154 outW
= inW
+ INTERNET_MAX_URL_LENGTH
;
4156 MultiByteToWideChar(CP_ACP
, 0, url
, -1, inW
, INTERNET_MAX_URL_LENGTH
);
4158 len
= INTERNET_MAX_URL_LENGTH
;
4159 hr
= UrlGetPartW(inW
, outW
, &len
, part
, flags
);
4166 len2
= WideCharToMultiByte(CP_ACP
, 0, outW
, len
, NULL
, 0, NULL
, NULL
);
4167 if (len2
> *out_len
)
4169 *out_len
= len2
+ 1;
4173 len2
= WideCharToMultiByte(CP_ACP
, 0, outW
, len
+ 1, out
, *out_len
, NULL
, NULL
);
4174 *out_len
= len2
- 1;
4179 static const WCHAR
* scan_url(const WCHAR
*start
, DWORD
*size
, enum url_scan_type type
)
4186 while ((*start
>= 'a' && *start
<= 'z') || (*start
>= '0' && *start
<= '9') ||
4187 *start
== '+' || *start
== '-' || *start
== '.')
4199 if (isalnum(*start
) ||
4200 /* user/password only characters */
4205 /* *extra* characters */
4212 /* *safe* characters */
4223 else if (*start
== '%' && isxdigit(start
[1]) && isxdigit(start
[2]))
4233 while (*start
>= '0' && *start
<= '9')
4241 while (isalnum(*start
) || *start
== '-' || *start
== '.' || *start
== ' ' || *start
== '*')
4249 FIXME("unknown type %d\n", type
);
4256 static LONG
parse_url(const WCHAR
*url
, struct parsed_url
*pl
)
4260 memset(pl
, 0, sizeof(*pl
));
4262 work
= scan_url(pl
->scheme
, &pl
->scheme_len
, SCHEME
);
4263 if (!*work
|| (*work
!= ':')) goto ErrorExit
;
4265 if ((*work
!= '/') || (*(work
+1) != '/')) goto SuccessExit
;
4267 pl
->username
= work
+ 2;
4268 work
= scan_url(pl
->username
, &pl
->username_len
, USERPASS
);
4271 /* parse password */
4273 pl
->password
= work
;
4274 work
= scan_url(pl
->password
, &pl
->password_len
, USERPASS
);
4277 /* what we just parsed must be the hostname and port
4278 * so reset pointers and clear then let it parse */
4279 pl
->username_len
= pl
->password_len
= 0;
4280 work
= pl
->username
- 1;
4281 pl
->username
= pl
->password
= 0;
4284 else if (*work
== '@')
4287 pl
->password_len
= 0;
4290 else if (!*work
|| *work
== '/' || *work
== '.')
4292 /* what was parsed was hostname, so reset pointers and let it parse */
4293 pl
->username_len
= pl
->password_len
= 0;
4294 work
= pl
->username
- 1;
4295 pl
->username
= pl
->password
= 0;
4297 else goto ErrorExit
;
4299 /* now start parsing hostname or hostnumber */
4301 pl
->hostname
= work
;
4302 work
= scan_url(pl
->hostname
, &pl
->hostname_len
, HOST
);
4308 work
= scan_url(pl
->port
, &pl
->port_len
, PORT
);
4312 /* see if query string */
4313 pl
->query
= wcschr(work
, '?');
4314 if (pl
->query
) pl
->query_len
= lstrlenW(pl
->query
);
4317 TRACE("parse successful: scheme=%p(%d), user=%p(%d), pass=%p(%d), host=%p(%d), port=%p(%d), query=%p(%d)\n",
4318 pl
->scheme
, pl
->scheme_len
, pl
->username
, pl
->username_len
, pl
->password
, pl
->password_len
, pl
->hostname
,
4319 pl
->hostname_len
, pl
->port
, pl
->port_len
, pl
->query
, pl
->query_len
);
4324 FIXME("failed to parse %s\n", debugstr_w(url
));
4325 return E_INVALIDARG
;
4328 HRESULT WINAPI
UrlGetPartW(const WCHAR
*url
, WCHAR
*out
, DWORD
*out_len
, DWORD part
, DWORD flags
)
4330 DWORD scheme
, size
, schsize
;
4331 LPCWSTR addr
, schaddr
;
4332 struct parsed_url pl
;
4335 TRACE("%s, %p, %p(%d), %#x, %#x\n", wine_dbgstr_w(url
), out
, out_len
, *out_len
, part
, flags
);
4337 if (!url
|| !out
|| !out_len
|| !*out_len
)
4338 return E_INVALIDARG
;
4342 addr
= wcschr(url
, ':');
4344 scheme
= URL_SCHEME_UNKNOWN
;
4346 scheme
= get_scheme_code(url
, addr
- url
);
4348 hr
= parse_url(url
, &pl
);
4352 case URL_PART_SCHEME
:
4359 size
= pl
.scheme_len
;
4362 case URL_PART_HOSTNAME
:
4365 case URL_SCHEME_FTP
:
4366 case URL_SCHEME_HTTP
:
4367 case URL_SCHEME_GOPHER
:
4368 case URL_SCHEME_TELNET
:
4369 case URL_SCHEME_FILE
:
4370 case URL_SCHEME_HTTPS
:
4377 if (scheme
== URL_SCHEME_FILE
&& (!pl
.hostname_len
|| (pl
.hostname_len
== 1 && *(pl
.hostname
+ 1) == ':')))
4383 if (!pl
.hostname_len
)
4389 size
= pl
.hostname_len
;
4392 case URL_PART_USERNAME
:
4393 if (!pl
.username_len
)
4399 size
= pl
.username_len
;
4402 case URL_PART_PASSWORD
:
4403 if (!pl
.password_len
)
4409 size
= pl
.password_len
;
4422 case URL_PART_QUERY
:
4429 size
= pl
.query_len
;
4434 return E_INVALIDARG
;
4437 if (flags
== URL_PARTFLAG_KEEPSCHEME
)
4439 if (!pl
.scheme
|| !pl
.scheme_len
)
4444 schaddr
= pl
.scheme
;
4445 schsize
= pl
.scheme_len
;
4446 if (*out_len
< schsize
+ size
+ 2)
4448 *out_len
= schsize
+ size
+ 2;
4451 memcpy(out
, schaddr
, schsize
*sizeof(WCHAR
));
4453 memcpy(out
+ schsize
+1, addr
, size
*sizeof(WCHAR
));
4454 out
[schsize
+1+size
] = 0;
4455 *out_len
= schsize
+ 1 + size
;
4459 if (*out_len
< size
+ 1)
4461 *out_len
= size
+ 1;
4464 memcpy(out
, addr
, size
*sizeof(WCHAR
));
4468 TRACE("len=%d %s\n", *out_len
, wine_dbgstr_w(out
));
4473 BOOL WINAPI
UrlIsA(const char *url
, URLIS Urlis
)
4478 TRACE("%s, %d\n", debugstr_a(url
), Urlis
);
4486 base
.cbSize
= sizeof(base
);
4487 if (ParseURLA(url
, &base
) != S_OK
) return FALSE
; /* invalid scheme */
4488 switch (base
.nScheme
)
4490 case URL_SCHEME_MAILTO
:
4491 case URL_SCHEME_SHELL
:
4492 case URL_SCHEME_JAVASCRIPT
:
4493 case URL_SCHEME_VBSCRIPT
:
4494 case URL_SCHEME_ABOUT
:
4500 return (CompareStringA(LOCALE_INVARIANT
, NORM_IGNORECASE
, url
, 5, "file:", 5) == CSTR_EQUAL
);
4502 case URLIS_DIRECTORY
:
4503 last
= url
+ strlen(url
) - 1;
4504 return (last
>= url
&& (*last
== '/' || *last
== '\\' ));
4507 return PathIsURLA(url
);
4509 case URLIS_NOHISTORY
:
4510 case URLIS_APPLIABLE
:
4511 case URLIS_HASQUERY
:
4513 FIXME("(%s %d): stub\n", debugstr_a(url
), Urlis
);
4519 BOOL WINAPI
UrlIsW(const WCHAR
*url
, URLIS Urlis
)
4524 TRACE("%s, %d\n", debugstr_w(url
), Urlis
);
4532 base
.cbSize
= sizeof(base
);
4533 if (ParseURLW(url
, &base
) != S_OK
) return FALSE
; /* invalid scheme */
4534 switch (base
.nScheme
)
4536 case URL_SCHEME_MAILTO
:
4537 case URL_SCHEME_SHELL
:
4538 case URL_SCHEME_JAVASCRIPT
:
4539 case URL_SCHEME_VBSCRIPT
:
4540 case URL_SCHEME_ABOUT
:
4546 return !wcsnicmp( url
, L
"file:", 5 );
4548 case URLIS_DIRECTORY
:
4549 last
= url
+ lstrlenW(url
) - 1;
4550 return (last
>= url
&& (*last
== '/' || *last
== '\\'));
4553 return PathIsURLW(url
);
4555 case URLIS_NOHISTORY
:
4556 case URLIS_APPLIABLE
:
4557 case URLIS_HASQUERY
:
4559 FIXME("(%s %d): stub\n", debugstr_w(url
), Urlis
);
4565 BOOL WINAPI
UrlIsOpaqueA(const char *url
)
4567 return UrlIsA(url
, URLIS_OPAQUE
);
4570 BOOL WINAPI
UrlIsOpaqueW(const WCHAR
*url
)
4572 return UrlIsW(url
, URLIS_OPAQUE
);
4575 BOOL WINAPI
UrlIsNoHistoryA(const char *url
)
4577 return UrlIsA(url
, URLIS_NOHISTORY
);
4580 BOOL WINAPI
UrlIsNoHistoryW(const WCHAR
*url
)
4582 return UrlIsW(url
, URLIS_NOHISTORY
);
4585 HRESULT WINAPI
UrlCreateFromPathA(const char *path
, char *url
, DWORD
*url_len
, DWORD reserved
)
4587 WCHAR bufW
[INTERNET_MAX_URL_LENGTH
];
4588 DWORD lenW
= ARRAY_SIZE(bufW
), lenA
;
4589 UNICODE_STRING pathW
;
4593 if (!RtlCreateUnicodeStringFromAsciiz(&pathW
, path
))
4594 return E_INVALIDARG
;
4596 if ((hr
= UrlCreateFromPathW(pathW
.Buffer
, urlW
, &lenW
, reserved
)) == E_POINTER
)
4598 urlW
= heap_alloc(lenW
* sizeof(WCHAR
));
4599 hr
= UrlCreateFromPathW(pathW
.Buffer
, urlW
, &lenW
, reserved
);
4604 RtlUnicodeToMultiByteSize(&lenA
, urlW
, lenW
* sizeof(WCHAR
));
4605 if (*url_len
> lenA
)
4607 RtlUnicodeToMultiByteN(url
, *url_len
- 1, &lenA
, urlW
, lenW
* sizeof(WCHAR
));
4613 *url_len
= lenA
+ 1;
4619 RtlFreeUnicodeString(&pathW
);
4623 HRESULT WINAPI
UrlCreateFromPathW(const WCHAR
*path
, WCHAR
*url
, DWORD
*url_len
, DWORD reserved
)
4627 TRACE("%s, %p, %p, %#x\n", debugstr_w(path
), url
, url_len
, reserved
);
4629 if (reserved
|| !url
|| !url_len
)
4630 return E_INVALIDARG
;
4632 hr
= url_create_from_path(path
, url
, url_len
);
4634 lstrcpyW(url
, path
);
4639 HRESULT WINAPI
UrlCombineA(const char *base
, const char *relative
, char *combined
, DWORD
*combined_len
, DWORD flags
)
4641 WCHAR
*baseW
, *relativeW
, *combinedW
;
4645 TRACE("%s, %s, %d, %#x\n", debugstr_a(base
), debugstr_a(relative
), combined_len
? *combined_len
: 0, flags
);
4647 if (!base
|| !relative
|| !combined_len
)
4648 return E_INVALIDARG
;
4650 baseW
= heap_alloc(3 * INTERNET_MAX_URL_LENGTH
* sizeof(WCHAR
));
4651 relativeW
= baseW
+ INTERNET_MAX_URL_LENGTH
;
4652 combinedW
= relativeW
+ INTERNET_MAX_URL_LENGTH
;
4654 MultiByteToWideChar(CP_ACP
, 0, base
, -1, baseW
, INTERNET_MAX_URL_LENGTH
);
4655 MultiByteToWideChar(CP_ACP
, 0, relative
, -1, relativeW
, INTERNET_MAX_URL_LENGTH
);
4656 len
= *combined_len
;
4658 hr
= UrlCombineW(baseW
, relativeW
, combined
? combinedW
: NULL
, &len
, flags
);
4661 *combined_len
= len
;
4666 len2
= WideCharToMultiByte(CP_ACP
, 0, combinedW
, len
, NULL
, 0, NULL
, NULL
);
4667 if (len2
> *combined_len
)
4669 *combined_len
= len2
;
4673 WideCharToMultiByte(CP_ACP
, 0, combinedW
, len
+1, combined
, *combined_len
+ 1, NULL
, NULL
);
4674 *combined_len
= len2
;
4679 HRESULT WINAPI
UrlCombineW(const WCHAR
*baseW
, const WCHAR
*relativeW
, WCHAR
*combined
, DWORD
*combined_len
, DWORD flags
)
4681 DWORD i
, len
, process_case
= 0, myflags
, sizeloc
= 0;
4682 LPWSTR work
, preliminary
, mbase
, mrelative
;
4683 PARSEDURLW base
, relative
;
4686 TRACE("%s, %s, %d, %#x\n", debugstr_w(baseW
), debugstr_w(relativeW
), combined_len
? *combined_len
: 0, flags
);
4688 if (!baseW
|| !relativeW
|| !combined_len
)
4689 return E_INVALIDARG
;
4691 base
.cbSize
= sizeof(base
);
4692 relative
.cbSize
= sizeof(relative
);
4694 /* Get space for duplicates of the input and the output */
4695 preliminary
= heap_alloc(3 * INTERNET_MAX_URL_LENGTH
* sizeof(WCHAR
));
4696 mbase
= preliminary
+ INTERNET_MAX_URL_LENGTH
;
4697 mrelative
= mbase
+ INTERNET_MAX_URL_LENGTH
;
4698 *preliminary
= '\0';
4700 /* Canonicalize the base input prior to looking for the scheme */
4701 myflags
= flags
& (URL_DONT_SIMPLIFY
| URL_UNESCAPE
);
4702 len
= INTERNET_MAX_URL_LENGTH
;
4703 UrlCanonicalizeW(baseW
, mbase
, &len
, myflags
);
4705 /* Canonicalize the relative input prior to looking for the scheme */
4706 len
= INTERNET_MAX_URL_LENGTH
;
4707 UrlCanonicalizeW(relativeW
, mrelative
, &len
, myflags
);
4709 /* See if the base has a scheme */
4710 if (ParseURLW(mbase
, &base
) != S_OK
)
4712 /* If base has no scheme return relative. */
4713 TRACE("no scheme detected in Base\n");
4718 BOOL manual_search
= FALSE
;
4720 work
= (LPWSTR
)base
.pszProtocol
;
4721 for (i
= 0; i
< base
.cchProtocol
; ++i
)
4722 work
[i
] = RtlDowncaseUnicodeChar(work
[i
]);
4724 /* mk is a special case */
4725 if (base
.nScheme
== URL_SCHEME_MK
)
4727 WCHAR
*ptr
= wcsstr(base
.pszSuffix
, L
"::");
4733 delta
= ptr
-base
.pszSuffix
;
4734 base
.cchProtocol
+= delta
;
4735 base
.pszSuffix
+= delta
;
4736 base
.cchSuffix
-= delta
;
4741 /* get size of location field (if it exists) */
4742 work
= (LPWSTR
)base
.pszSuffix
;
4748 /* At this point have start of location and
4749 * it ends at next '/' or end of string.
4751 while (*work
&& (*work
!= '/')) work
++;
4752 sizeloc
= (DWORD
)(work
- base
.pszSuffix
);
4757 /* If there is a '?', then the remaining part can only contain a
4758 * query string or fragment, so start looking for the last leaf
4759 * from the '?'. Otherwise, if there is a '#' and the characters
4760 * immediately preceding it are ".htm[l]", then begin looking for
4761 * the last leaf starting from the '#'. Otherwise the '#' is not
4762 * meaningful and just start looking from the end. */
4763 if ((work
= wcspbrk(base
.pszSuffix
+ sizeloc
, L
"#?")))
4765 if (*work
== '?' || base
.nScheme
== URL_SCHEME_HTTP
|| base
.nScheme
== URL_SCHEME_HTTPS
)
4766 manual_search
= TRUE
;
4767 else if (work
- base
.pszSuffix
> 4)
4769 if (!wcsnicmp(work
- 4, L
".htm", 4)) manual_search
= TRUE
;
4772 if (!manual_search
&& work
- base
.pszSuffix
> 5)
4774 if (!wcsnicmp(work
- 5, L
".html", 5)) manual_search
= TRUE
;
4780 /* search backwards starting from the current position */
4781 while (*work
!= '/' && work
> base
.pszSuffix
+ sizeloc
)
4783 base
.cchSuffix
= work
- base
.pszSuffix
+ 1;
4787 /* search backwards starting from the end of the string */
4788 work
= wcsrchr((base
.pszSuffix
+sizeloc
), '/');
4791 len
= (DWORD
)(work
- base
.pszSuffix
+ 1);
4792 base
.cchSuffix
= len
;
4795 base
.cchSuffix
= sizeloc
;
4800 * .pszSuffix points to location (starting with '//')
4801 * .cchSuffix length of location (above) and rest less the last
4803 * sizeloc length of location (above) up to but not including
4807 if (ParseURLW(mrelative
, &relative
) != S_OK
)
4809 /* No scheme in relative */
4810 TRACE("no scheme detected in Relative\n");
4811 relative
.pszSuffix
= mrelative
; /* case 3,4,5 depends on this */
4812 relative
.cchSuffix
= lstrlenW(mrelative
);
4813 if (*relativeW
== ':')
4815 /* Case that is either left alone or uses base. */
4816 if (flags
& URL_PLUGGABLE_PROTOCOL
)
4824 if (is_drive_spec( mrelative
))
4826 /* case that becomes "file:///" */
4827 lstrcpyW(preliminary
, L
"file:///");
4831 if (*mrelative
== '/' && *(mrelative
+1) == '/')
4833 /* Relative has location and the rest. */
4837 if (*mrelative
== '/')
4839 /* Relative is root to location. */
4843 if (*mrelative
== '#')
4845 if (!(work
= wcschr(base
.pszSuffix
+base
.cchSuffix
, '#')))
4846 work
= (LPWSTR
)base
.pszSuffix
+ lstrlenW(base
.pszSuffix
);
4848 memcpy(preliminary
, base
.pszProtocol
, (work
-base
.pszProtocol
)*sizeof(WCHAR
));
4849 preliminary
[work
-base
.pszProtocol
] = '\0';
4853 process_case
= (*base
.pszSuffix
== '/' || base
.nScheme
== URL_SCHEME_MK
) ? 5 : 3;
4858 work
= (LPWSTR
)relative
.pszProtocol
;
4859 for (i
= 0; i
< relative
.cchProtocol
; ++i
)
4860 work
[i
] = RtlDowncaseUnicodeChar(work
[i
]);
4863 /* Handle cases where relative has scheme. */
4864 if ((base
.cchProtocol
== relative
.cchProtocol
) && !wcsncmp(base
.pszProtocol
, relative
.pszProtocol
, base
.cchProtocol
))
4866 /* since the schemes are the same */
4867 if (*relative
.pszSuffix
== '/' && *(relative
.pszSuffix
+1) == '/')
4869 /* Relative replaces location and what follows. */
4873 if (*relative
.pszSuffix
== '/')
4875 /* Relative is root to location */
4879 /* replace either just location if base's location starts with a
4880 * slash or otherwise everything */
4881 process_case
= (*base
.pszSuffix
== '/') ? 5 : 1;
4885 if (*relative
.pszSuffix
== '/' && *(relative
.pszSuffix
+1) == '/')
4887 /* Relative replaces scheme, location, and following and handles PLUGGABLE */
4893 } while (FALSE
); /* a little trick to allow easy exit from nested if's */
4896 switch (process_case
)
4899 /* Return relative appended to whatever is in combined (which may the string "file:///" */
4900 lstrcatW(preliminary
, mrelative
);
4904 /* Relative replaces scheme and location */
4905 lstrcpyW(preliminary
, mrelative
);
4909 /* Return the base scheme with relative. Basically keeps the scheme and replaces the domain and following. */
4910 memcpy(preliminary
, base
.pszProtocol
, (base
.cchProtocol
+ 1)*sizeof(WCHAR
));
4911 work
= preliminary
+ base
.cchProtocol
+ 1;
4912 lstrcpyW(work
, relative
.pszSuffix
);
4916 /* Return the base scheme and location but everything after the location is relative. (Replace document from root on.) */
4917 memcpy(preliminary
, base
.pszProtocol
, (base
.cchProtocol
+1+sizeloc
)*sizeof(WCHAR
));
4918 work
= preliminary
+ base
.cchProtocol
+ 1 + sizeloc
;
4919 if (flags
& URL_PLUGGABLE_PROTOCOL
)
4921 lstrcpyW(work
, relative
.pszSuffix
);
4925 /* Return the base without its document (if any) and append relative after its scheme. */
4926 memcpy(preliminary
, base
.pszProtocol
, (base
.cchProtocol
+ 1 + base
.cchSuffix
)*sizeof(WCHAR
));
4927 work
= preliminary
+ base
.cchProtocol
+ 1 + base
.cchSuffix
- 1;
4930 lstrcpyW(work
, relative
.pszSuffix
);
4934 FIXME("Unexpected case %d.\n", process_case
);
4940 /* Reuse mrelative as temp storage as it's already allocated and not needed anymore */
4941 if (*combined_len
== 0)
4943 hr
= UrlCanonicalizeW(preliminary
, mrelative
, combined_len
, flags
& ~URL_FILE_USE_PATHURL
);
4944 if (SUCCEEDED(hr
) && combined
)
4945 lstrcpyW(combined
, mrelative
);
4947 TRACE("return-%d len=%d, %s\n", process_case
, *combined_len
, debugstr_w(combined
));
4950 heap_free(preliminary
);
4954 HRESULT WINAPI
HashData(const unsigned char *src
, DWORD src_len
, unsigned char *dest
, DWORD dest_len
)
4956 INT src_count
= src_len
- 1, dest_count
= dest_len
- 1;
4959 return E_INVALIDARG
;
4961 while (dest_count
>= 0)
4963 dest
[dest_count
] = (dest_count
& 0xff);
4967 while (src_count
>= 0)
4969 dest_count
= dest_len
- 1;
4970 while (dest_count
>= 0)
4972 dest
[dest_count
] = hashdata_lookup
[src
[src_count
] ^ dest
[dest_count
]];
4981 HRESULT WINAPI
UrlHashA(const char *url
, unsigned char *dest
, DWORD dest_len
)
4985 HashData((const BYTE
*)url
, (int)strlen(url
), dest
, dest_len
);
4989 return E_INVALIDARG
;
4995 HRESULT WINAPI
UrlHashW(const WCHAR
*url
, unsigned char *dest
, DWORD dest_len
)
4997 char urlA
[MAX_PATH
];
4999 TRACE("%s, %p, %d\n", debugstr_w(url
), dest
, dest_len
);
5003 WideCharToMultiByte(CP_ACP
, 0, url
, -1, urlA
, MAX_PATH
, NULL
, NULL
);
5004 HashData((const BYTE
*)urlA
, (int)strlen(urlA
), dest
, dest_len
);
5008 return E_INVALIDARG
;
5014 BOOL WINAPI
IsInternetESCEnabled(void)