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
)
1905 char *start
, *first
;
1907 TRACE("%s\n", wine_dbgstr_a(path
));
1909 if (!path
|| !*path
)
1912 start
= first
= path
;
1914 while (*path
== ' ')
1915 path
= CharNextA(path
);
1921 while (start
[-1] == ' ')
1927 void WINAPI
PathRemoveBlanksW(WCHAR
*path
)
1929 WCHAR
*start
, *first
;
1931 TRACE("%s\n", wine_dbgstr_w(path
));
1933 if (!path
|| !*path
)
1936 start
= first
= path
;
1938 while (*path
== ' ')
1945 while (start
[-1] == ' ')
1951 void WINAPI
PathRemoveExtensionA(char *path
)
1953 TRACE("%s\n", wine_dbgstr_a(path
));
1958 path
= PathFindExtensionA(path
);
1963 void WINAPI
PathRemoveExtensionW(WCHAR
*path
)
1965 TRACE("%s\n", wine_dbgstr_w(path
));
1970 path
= PathFindExtensionW(path
);
1975 BOOL WINAPI
PathRenameExtensionA(char *path
, const char *ext
)
1979 TRACE("%s, %s\n", wine_dbgstr_a(path
), wine_dbgstr_a(ext
));
1981 extension
= PathFindExtensionA(path
);
1983 if (!extension
|| (extension
- path
+ strlen(ext
) >= MAX_PATH
))
1986 strcpy(extension
, ext
);
1990 BOOL WINAPI
PathRenameExtensionW(WCHAR
*path
, const WCHAR
*ext
)
1994 TRACE("%s, %s\n", wine_dbgstr_w(path
), wine_dbgstr_w(ext
));
1996 extension
= PathFindExtensionW(path
);
1998 if (!extension
|| (extension
- path
+ lstrlenW(ext
) >= MAX_PATH
))
2001 lstrcpyW(extension
, ext
);
2005 void WINAPI
PathUnquoteSpacesA(char *path
)
2009 TRACE("%s\n", wine_dbgstr_a(path
));
2011 if (!path
|| *path
!= '"')
2014 len
= strlen(path
) - 1;
2015 if (path
[len
] == '"')
2018 for (; *path
; path
++)
2023 void WINAPI
PathUnquoteSpacesW(WCHAR
*path
)
2027 TRACE("%s\n", wine_dbgstr_w(path
));
2029 if (!path
|| *path
!= '"')
2032 len
= lstrlenW(path
) - 1;
2033 if (path
[len
] == '"')
2036 for (; *path
; path
++)
2041 char * WINAPI
PathRemoveBackslashA(char *path
)
2045 TRACE("%s\n", wine_dbgstr_a(path
));
2050 ptr
= CharPrevA(path
, path
+ strlen(path
));
2051 if (!PathIsRootA(path
) && *ptr
== '\\')
2057 WCHAR
* WINAPI
PathRemoveBackslashW(WCHAR
*path
)
2061 TRACE("%s\n", wine_dbgstr_w(path
));
2066 ptr
= path
+ lstrlenW(path
);
2067 if (ptr
> path
) ptr
--;
2068 if (!PathIsRootW(path
) && *ptr
== '\\')
2074 BOOL WINAPI
PathIsLFNFileSpecA(const char *path
)
2076 unsigned int name_len
= 0, ext_len
= 0;
2078 TRACE("%s\n", wine_dbgstr_a(path
));
2086 return TRUE
; /* DOS names cannot have spaces */
2090 return TRUE
; /* DOS names have only one dot */
2097 return TRUE
; /* DOS extensions are <= 3 chars*/
2103 return TRUE
; /* DOS names are <= 8 chars */
2105 path
= CharNextA(path
);
2108 return FALSE
; /* Valid DOS path */
2111 BOOL WINAPI
PathIsLFNFileSpecW(const WCHAR
*path
)
2113 unsigned int name_len
= 0, ext_len
= 0;
2115 TRACE("%s\n", wine_dbgstr_w(path
));
2123 return TRUE
; /* DOS names cannot have spaces */
2127 return TRUE
; /* DOS names have only one dot */
2134 return TRUE
; /* DOS extensions are <= 3 chars*/
2140 return TRUE
; /* DOS names are <= 8 chars */
2145 return FALSE
; /* Valid DOS path */
2148 #define PATH_CHAR_CLASS_LETTER 0x00000001
2149 #define PATH_CHAR_CLASS_ASTERIX 0x00000002
2150 #define PATH_CHAR_CLASS_DOT 0x00000004
2151 #define PATH_CHAR_CLASS_BACKSLASH 0x00000008
2152 #define PATH_CHAR_CLASS_COLON 0x00000010
2153 #define PATH_CHAR_CLASS_SEMICOLON 0x00000020
2154 #define PATH_CHAR_CLASS_COMMA 0x00000040
2155 #define PATH_CHAR_CLASS_SPACE 0x00000080
2156 #define PATH_CHAR_CLASS_OTHER_VALID 0x00000100
2157 #define PATH_CHAR_CLASS_DOUBLEQUOTE 0x00000200
2159 #define PATH_CHAR_CLASS_INVALID 0x00000000
2160 #define PATH_CHAR_CLASS_ANY 0xffffffff
2162 static const DWORD path_charclass
[] =
2164 /* 0x00 */ PATH_CHAR_CLASS_INVALID
, /* 0x01 */ PATH_CHAR_CLASS_INVALID
,
2165 /* 0x02 */ PATH_CHAR_CLASS_INVALID
, /* 0x03 */ PATH_CHAR_CLASS_INVALID
,
2166 /* 0x04 */ PATH_CHAR_CLASS_INVALID
, /* 0x05 */ PATH_CHAR_CLASS_INVALID
,
2167 /* 0x06 */ PATH_CHAR_CLASS_INVALID
, /* 0x07 */ PATH_CHAR_CLASS_INVALID
,
2168 /* 0x08 */ PATH_CHAR_CLASS_INVALID
, /* 0x09 */ PATH_CHAR_CLASS_INVALID
,
2169 /* 0x0a */ PATH_CHAR_CLASS_INVALID
, /* 0x0b */ PATH_CHAR_CLASS_INVALID
,
2170 /* 0x0c */ PATH_CHAR_CLASS_INVALID
, /* 0x0d */ PATH_CHAR_CLASS_INVALID
,
2171 /* 0x0e */ PATH_CHAR_CLASS_INVALID
, /* 0x0f */ PATH_CHAR_CLASS_INVALID
,
2172 /* 0x10 */ PATH_CHAR_CLASS_INVALID
, /* 0x11 */ PATH_CHAR_CLASS_INVALID
,
2173 /* 0x12 */ PATH_CHAR_CLASS_INVALID
, /* 0x13 */ PATH_CHAR_CLASS_INVALID
,
2174 /* 0x14 */ PATH_CHAR_CLASS_INVALID
, /* 0x15 */ PATH_CHAR_CLASS_INVALID
,
2175 /* 0x16 */ PATH_CHAR_CLASS_INVALID
, /* 0x17 */ PATH_CHAR_CLASS_INVALID
,
2176 /* 0x18 */ PATH_CHAR_CLASS_INVALID
, /* 0x19 */ PATH_CHAR_CLASS_INVALID
,
2177 /* 0x1a */ PATH_CHAR_CLASS_INVALID
, /* 0x1b */ PATH_CHAR_CLASS_INVALID
,
2178 /* 0x1c */ PATH_CHAR_CLASS_INVALID
, /* 0x1d */ PATH_CHAR_CLASS_INVALID
,
2179 /* 0x1e */ PATH_CHAR_CLASS_INVALID
, /* 0x1f */ PATH_CHAR_CLASS_INVALID
,
2180 /* ' ' */ PATH_CHAR_CLASS_SPACE
, /* '!' */ PATH_CHAR_CLASS_OTHER_VALID
,
2181 /* '"' */ PATH_CHAR_CLASS_DOUBLEQUOTE
, /* '#' */ PATH_CHAR_CLASS_OTHER_VALID
,
2182 /* '$' */ PATH_CHAR_CLASS_OTHER_VALID
, /* '%' */ PATH_CHAR_CLASS_OTHER_VALID
,
2183 /* '&' */ PATH_CHAR_CLASS_OTHER_VALID
, /* '\'' */ PATH_CHAR_CLASS_OTHER_VALID
,
2184 /* '(' */ PATH_CHAR_CLASS_OTHER_VALID
, /* ')' */ PATH_CHAR_CLASS_OTHER_VALID
,
2185 /* '*' */ PATH_CHAR_CLASS_ASTERIX
, /* '+' */ PATH_CHAR_CLASS_OTHER_VALID
,
2186 /* ',' */ PATH_CHAR_CLASS_COMMA
, /* '-' */ PATH_CHAR_CLASS_OTHER_VALID
,
2187 /* '.' */ PATH_CHAR_CLASS_DOT
, /* '/' */ PATH_CHAR_CLASS_INVALID
,
2188 /* '0' */ PATH_CHAR_CLASS_OTHER_VALID
, /* '1' */ PATH_CHAR_CLASS_OTHER_VALID
,
2189 /* '2' */ PATH_CHAR_CLASS_OTHER_VALID
, /* '3' */ PATH_CHAR_CLASS_OTHER_VALID
,
2190 /* '4' */ PATH_CHAR_CLASS_OTHER_VALID
, /* '5' */ PATH_CHAR_CLASS_OTHER_VALID
,
2191 /* '6' */ PATH_CHAR_CLASS_OTHER_VALID
, /* '7' */ PATH_CHAR_CLASS_OTHER_VALID
,
2192 /* '8' */ PATH_CHAR_CLASS_OTHER_VALID
, /* '9' */ PATH_CHAR_CLASS_OTHER_VALID
,
2193 /* ':' */ PATH_CHAR_CLASS_COLON
, /* ';' */ PATH_CHAR_CLASS_SEMICOLON
,
2194 /* '<' */ PATH_CHAR_CLASS_INVALID
, /* '=' */ PATH_CHAR_CLASS_OTHER_VALID
,
2195 /* '>' */ PATH_CHAR_CLASS_INVALID
, /* '?' */ PATH_CHAR_CLASS_LETTER
,
2196 /* '@' */ PATH_CHAR_CLASS_OTHER_VALID
, /* 'A' */ PATH_CHAR_CLASS_ANY
,
2197 /* 'B' */ PATH_CHAR_CLASS_ANY
, /* 'C' */ PATH_CHAR_CLASS_ANY
,
2198 /* 'D' */ PATH_CHAR_CLASS_ANY
, /* 'E' */ PATH_CHAR_CLASS_ANY
,
2199 /* 'F' */ PATH_CHAR_CLASS_ANY
, /* 'G' */ PATH_CHAR_CLASS_ANY
,
2200 /* 'H' */ PATH_CHAR_CLASS_ANY
, /* 'I' */ PATH_CHAR_CLASS_ANY
,
2201 /* 'J' */ PATH_CHAR_CLASS_ANY
, /* 'K' */ PATH_CHAR_CLASS_ANY
,
2202 /* 'L' */ PATH_CHAR_CLASS_ANY
, /* 'M' */ PATH_CHAR_CLASS_ANY
,
2203 /* 'N' */ PATH_CHAR_CLASS_ANY
, /* 'O' */ PATH_CHAR_CLASS_ANY
,
2204 /* 'P' */ PATH_CHAR_CLASS_ANY
, /* 'Q' */ PATH_CHAR_CLASS_ANY
,
2205 /* 'R' */ PATH_CHAR_CLASS_ANY
, /* 'S' */ PATH_CHAR_CLASS_ANY
,
2206 /* 'T' */ PATH_CHAR_CLASS_ANY
, /* 'U' */ PATH_CHAR_CLASS_ANY
,
2207 /* 'V' */ PATH_CHAR_CLASS_ANY
, /* 'W' */ PATH_CHAR_CLASS_ANY
,
2208 /* 'X' */ PATH_CHAR_CLASS_ANY
, /* 'Y' */ PATH_CHAR_CLASS_ANY
,
2209 /* 'Z' */ PATH_CHAR_CLASS_ANY
, /* '[' */ PATH_CHAR_CLASS_OTHER_VALID
,
2210 /* '\\' */ PATH_CHAR_CLASS_BACKSLASH
, /* ']' */ PATH_CHAR_CLASS_OTHER_VALID
,
2211 /* '^' */ PATH_CHAR_CLASS_OTHER_VALID
, /* '_' */ PATH_CHAR_CLASS_OTHER_VALID
,
2212 /* '`' */ PATH_CHAR_CLASS_OTHER_VALID
, /* 'a' */ PATH_CHAR_CLASS_ANY
,
2213 /* 'b' */ PATH_CHAR_CLASS_ANY
, /* 'c' */ PATH_CHAR_CLASS_ANY
,
2214 /* 'd' */ PATH_CHAR_CLASS_ANY
, /* 'e' */ PATH_CHAR_CLASS_ANY
,
2215 /* 'f' */ PATH_CHAR_CLASS_ANY
, /* 'g' */ PATH_CHAR_CLASS_ANY
,
2216 /* 'h' */ PATH_CHAR_CLASS_ANY
, /* 'i' */ PATH_CHAR_CLASS_ANY
,
2217 /* 'j' */ PATH_CHAR_CLASS_ANY
, /* 'k' */ PATH_CHAR_CLASS_ANY
,
2218 /* 'l' */ PATH_CHAR_CLASS_ANY
, /* 'm' */ PATH_CHAR_CLASS_ANY
,
2219 /* 'n' */ PATH_CHAR_CLASS_ANY
, /* 'o' */ PATH_CHAR_CLASS_ANY
,
2220 /* 'p' */ PATH_CHAR_CLASS_ANY
, /* 'q' */ PATH_CHAR_CLASS_ANY
,
2221 /* 'r' */ PATH_CHAR_CLASS_ANY
, /* 's' */ PATH_CHAR_CLASS_ANY
,
2222 /* 't' */ PATH_CHAR_CLASS_ANY
, /* 'u' */ PATH_CHAR_CLASS_ANY
,
2223 /* 'v' */ PATH_CHAR_CLASS_ANY
, /* 'w' */ PATH_CHAR_CLASS_ANY
,
2224 /* 'x' */ PATH_CHAR_CLASS_ANY
, /* 'y' */ PATH_CHAR_CLASS_ANY
,
2225 /* 'z' */ PATH_CHAR_CLASS_ANY
, /* '{' */ PATH_CHAR_CLASS_OTHER_VALID
,
2226 /* '|' */ PATH_CHAR_CLASS_INVALID
, /* '}' */ PATH_CHAR_CLASS_OTHER_VALID
,
2227 /* '~' */ PATH_CHAR_CLASS_OTHER_VALID
2230 BOOL WINAPI
PathIsValidCharA(char c
, DWORD
class)
2232 if ((unsigned)c
> 0x7e)
2233 return class & PATH_CHAR_CLASS_OTHER_VALID
;
2235 return class & path_charclass
[(unsigned)c
];
2238 BOOL WINAPI
PathIsValidCharW(WCHAR c
, DWORD
class)
2241 return class & PATH_CHAR_CLASS_OTHER_VALID
;
2243 return class & path_charclass
[c
];
2246 char * WINAPI
PathFindNextComponentA(const char *path
)
2250 TRACE("%s\n", wine_dbgstr_a(path
));
2252 if (!path
|| !*path
)
2255 if ((slash
= StrChrA(path
, '\\')))
2257 if (slash
[1] == '\\')
2262 return (char *)path
+ strlen(path
);
2265 WCHAR
* WINAPI
PathFindNextComponentW(const WCHAR
*path
)
2269 TRACE("%s\n", wine_dbgstr_w(path
));
2271 if (!path
|| !*path
)
2274 if ((slash
= StrChrW(path
, '\\')))
2276 if (slash
[1] == '\\')
2281 return (WCHAR
*)path
+ lstrlenW(path
);
2284 char * WINAPI
PathSkipRootA(const char *path
)
2286 TRACE("%s\n", wine_dbgstr_a(path
));
2288 if (!path
|| !*path
)
2291 if (*path
== '\\' && path
[1] == '\\')
2293 /* Network share: skip share server and mount point */
2295 if ((path
= StrChrA(path
, '\\')) && (path
= StrChrA(path
+ 1, '\\')))
2297 return (char *)path
;
2300 if (IsDBCSLeadByte(*path
))
2304 if (path
[0] && path
[1] == ':' && path
[2] == '\\')
2305 return (char *)path
+ 3;
2310 WCHAR
* WINAPI
PathSkipRootW(const WCHAR
*path
)
2312 TRACE("%s\n", wine_dbgstr_w(path
));
2314 if (!path
|| !*path
)
2317 if (*path
== '\\' && path
[1] == '\\')
2319 /* Network share: skip share server and mount point */
2321 if ((path
= StrChrW(path
, '\\')) && (path
= StrChrW(path
+ 1, '\\')))
2323 return (WCHAR
*)path
;
2327 if (path
[0] && path
[1] == ':' && path
[2] == '\\')
2328 return (WCHAR
*)path
+ 3;
2333 void WINAPI
PathStripPathA(char *path
)
2335 TRACE("%s\n", wine_dbgstr_a(path
));
2339 char *filename
= PathFindFileNameA(path
);
2340 if (filename
!= path
)
2341 RtlMoveMemory(path
, filename
, strlen(filename
) + 1);
2345 void WINAPI
PathStripPathW(WCHAR
*path
)
2349 TRACE("%s\n", wine_dbgstr_w(path
));
2350 filename
= PathFindFileNameW(path
);
2351 if (filename
!= path
)
2352 RtlMoveMemory(path
, filename
, (lstrlenW(filename
) + 1) * sizeof(WCHAR
));
2355 BOOL WINAPI
PathSearchAndQualifyA(const char *path
, char *buffer
, UINT length
)
2357 TRACE("%s, %p, %u\n", wine_dbgstr_a(path
), buffer
, length
);
2359 if (SearchPathA(NULL
, path
, NULL
, length
, buffer
, NULL
))
2362 return !!GetFullPathNameA(path
, length
, buffer
, NULL
);
2365 BOOL WINAPI
PathSearchAndQualifyW(const WCHAR
*path
, WCHAR
*buffer
, UINT length
)
2367 TRACE("%s, %p, %u\n", wine_dbgstr_w(path
), buffer
, length
);
2369 if (SearchPathW(NULL
, path
, NULL
, length
, buffer
, NULL
))
2371 return !!GetFullPathNameW(path
, length
, buffer
, NULL
);
2374 BOOL WINAPI
PathRelativePathToA(char *path
, const char *from
, DWORD attributes_from
, const char *to
,
2375 DWORD attributes_to
)
2377 WCHAR pathW
[MAX_PATH
], fromW
[MAX_PATH
], toW
[MAX_PATH
];
2380 TRACE("%p, %s, %#x, %s, %#x\n", path
, wine_dbgstr_a(from
), attributes_from
, wine_dbgstr_a(to
), attributes_to
);
2382 if (!path
|| !from
|| !to
)
2385 MultiByteToWideChar(CP_ACP
, 0, from
, -1, fromW
, ARRAY_SIZE(fromW
));
2386 MultiByteToWideChar(CP_ACP
, 0, to
, -1, toW
, ARRAY_SIZE(toW
));
2387 ret
= PathRelativePathToW(pathW
, fromW
, attributes_from
, toW
, attributes_to
);
2388 WideCharToMultiByte(CP_ACP
, 0, pathW
, -1, path
, MAX_PATH
, 0, 0);
2393 BOOL WINAPI
PathRelativePathToW(WCHAR
*path
, const WCHAR
*from
, DWORD attributes_from
, const WCHAR
*to
,
2394 DWORD attributes_to
)
2396 WCHAR fromW
[MAX_PATH
], toW
[MAX_PATH
];
2399 TRACE("%p, %s, %#x, %s, %#x\n", path
, wine_dbgstr_w(from
), attributes_from
, wine_dbgstr_w(to
), attributes_to
);
2401 if (!path
|| !from
|| !to
)
2405 lstrcpynW(fromW
, from
, ARRAY_SIZE(fromW
));
2406 lstrcpynW(toW
, to
, ARRAY_SIZE(toW
));
2408 if (!(attributes_from
& FILE_ATTRIBUTE_DIRECTORY
))
2409 PathRemoveFileSpecW(fromW
);
2410 if (!(attributes_to
& FILE_ATTRIBUTE_DIRECTORY
))
2411 PathRemoveFileSpecW(toW
);
2413 /* Paths can only be relative if they have a common root */
2414 if (!(len
= PathCommonPrefixW(fromW
, toW
, 0)))
2417 /* Strip off 'from' components to the root, by adding "..\" */
2429 from
= PathFindNextComponentW(from
);
2430 lstrcatW(path
, *from
? L
"..\\" : L
"..");
2433 /* From the root add the components of 'to' */
2435 /* We check to[-1] to avoid skipping end of string. See the notes for this function. */
2440 len
= lstrlenW(path
);
2441 if (len
+ lstrlenW(to
) >= MAX_PATH
)
2446 lstrcpyW(path
+ len
, to
);
2452 BOOL WINAPI
PathMatchSpecA(const char *path
, const char *mask
)
2454 WCHAR
*pathW
, *maskW
;
2457 TRACE("%s, %s\n", wine_dbgstr_a(path
), wine_dbgstr_a(mask
));
2459 if (!lstrcmpA(mask
, "*.*"))
2460 return TRUE
; /* Matches every path */
2462 pathW
= heap_strdupAtoW( path
);
2463 maskW
= heap_strdupAtoW( mask
);
2464 ret
= PathMatchSpecW( pathW
, maskW
);
2470 static BOOL
path_match_maskW(const WCHAR
*name
, const WCHAR
*mask
)
2472 while (*name
&& *mask
&& *mask
!= ';')
2478 if (path_match_maskW(name
, mask
+ 1))
2479 return TRUE
; /* try substrings */
2484 if (towupper(*mask
) != towupper(*name
) && *mask
!= '?')
2493 while (*mask
== '*')
2495 if (!*mask
|| *mask
== ';')
2502 BOOL WINAPI
PathMatchSpecW(const WCHAR
*path
, const WCHAR
*mask
)
2504 TRACE("%s, %s\n", wine_dbgstr_w(path
), wine_dbgstr_w(mask
));
2506 if (!lstrcmpW(mask
, L
"*.*"))
2507 return TRUE
; /* Matches every path */
2511 while (*mask
== ' ')
2512 mask
++; /* Eat leading spaces */
2514 if (path_match_maskW(path
, mask
))
2515 return TRUE
; /* Matches the current path */
2517 while (*mask
&& *mask
!= ';')
2518 mask
++; /* masks separated by ';' */
2527 void WINAPI
PathQuoteSpacesA(char *path
)
2529 TRACE("%s\n", wine_dbgstr_a(path
));
2531 if (path
&& StrChrA(path
, ' '))
2533 size_t len
= strlen(path
) + 1;
2535 if (len
+ 2 < MAX_PATH
)
2537 memmove(path
+ 1, path
, len
);
2540 path
[len
+ 1] = '\0';
2545 void WINAPI
PathQuoteSpacesW(WCHAR
*path
)
2547 TRACE("%s\n", wine_dbgstr_w(path
));
2549 if (path
&& StrChrW(path
, ' '))
2551 int len
= lstrlenW(path
) + 1;
2553 if (len
+ 2 < MAX_PATH
)
2555 memmove(path
+ 1, path
, len
* sizeof(WCHAR
));
2558 path
[len
+ 1] = '\0';
2563 BOOL WINAPI
PathIsSameRootA(const char *path1
, const char *path2
)
2568 TRACE("%s, %s\n", wine_dbgstr_a(path1
), wine_dbgstr_a(path2
));
2570 if (!path1
|| !path2
|| !(start
= PathSkipRootA(path1
)))
2573 len
= PathCommonPrefixA(path1
, path2
, NULL
) + 1;
2574 return start
- path1
<= len
;
2577 BOOL WINAPI
PathIsSameRootW(const WCHAR
*path1
, const WCHAR
*path2
)
2582 TRACE("%s, %s\n", wine_dbgstr_w(path1
), wine_dbgstr_w(path2
));
2584 if (!path1
|| !path2
|| !(start
= PathSkipRootW(path1
)))
2587 len
= PathCommonPrefixW(path1
, path2
, NULL
) + 1;
2588 return start
- path1
<= len
;
2591 BOOL WINAPI
PathFileExistsA(const char *path
)
2596 TRACE("%s\n", wine_dbgstr_a(path
));
2601 /* Prevent a dialog box if path is on a disk that has been ejected. */
2602 prev_mode
= SetErrorMode(SEM_FAILCRITICALERRORS
);
2603 attrs
= GetFileAttributesA(path
);
2604 SetErrorMode(prev_mode
);
2605 return attrs
!= INVALID_FILE_ATTRIBUTES
;
2608 BOOL WINAPI
PathFileExistsW(const WCHAR
*path
)
2613 TRACE("%s\n", wine_dbgstr_w(path
));
2618 prev_mode
= SetErrorMode(SEM_FAILCRITICALERRORS
);
2619 attrs
= GetFileAttributesW(path
);
2620 SetErrorMode(prev_mode
);
2621 return attrs
!= INVALID_FILE_ATTRIBUTES
;
2624 int WINAPI
PathParseIconLocationA(char *path
)
2629 TRACE("%s\n", debugstr_a(path
));
2634 if ((comma
= strchr(path
, ',')))
2637 ret
= StrToIntA(comma
);
2639 PathUnquoteSpacesA(path
);
2640 PathRemoveBlanksA(path
);
2645 int WINAPI
PathParseIconLocationW(WCHAR
*path
)
2650 TRACE("%s\n", debugstr_w(path
));
2655 if ((comma
= StrChrW(path
, ',')))
2658 ret
= StrToIntW(comma
);
2660 PathUnquoteSpacesW(path
);
2661 PathRemoveBlanksW(path
);
2666 BOOL WINAPI
PathUnExpandEnvStringsA(const char *path
, char *buffer
, UINT buf_len
)
2668 WCHAR bufferW
[MAX_PATH
], *pathW
;
2672 TRACE("%s, %p, %d\n", debugstr_a(path
), buffer
, buf_len
);
2674 pathW
= heap_strdupAtoW(path
);
2675 if (!pathW
) return FALSE
;
2677 ret
= PathUnExpandEnvStringsW(pathW
, bufferW
, MAX_PATH
);
2678 HeapFree(GetProcessHeap(), 0, pathW
);
2679 if (!ret
) return FALSE
;
2681 len
= WideCharToMultiByte(CP_ACP
, 0, bufferW
, -1, NULL
, 0, NULL
, NULL
);
2682 if (buf_len
< len
+ 1) return FALSE
;
2684 WideCharToMultiByte(CP_ACP
, 0, bufferW
, -1, buffer
, buf_len
, NULL
, NULL
);
2691 WCHAR path
[MAX_PATH
];
2695 static void init_envvars_map(struct envvars_map
*map
)
2699 map
->len
= ExpandEnvironmentStringsW(map
->var
, map
->path
, ARRAY_SIZE(map
->path
));
2700 /* exclude null from length */
2701 if (map
->len
) map
->len
--;
2706 BOOL WINAPI
PathUnExpandEnvStringsW(const WCHAR
*path
, WCHAR
*buffer
, UINT buf_len
)
2708 static struct envvars_map null_var
= {L
"", {0}, 0};
2709 struct envvars_map
*match
= &null_var
, *cur
;
2710 struct envvars_map envvars
[] =
2712 { L
"%ALLUSERSPROFILE%" },
2714 { L
"%ProgramFiles%" },
2715 { L
"%SystemRoot%" },
2716 { L
"%SystemDrive%" },
2717 { L
"%USERPROFILE%" },
2723 TRACE("%s, %p, %d\n", debugstr_w(path
), buffer
, buf_len
);
2725 pathlen
= lstrlenW(path
);
2726 init_envvars_map(envvars
);
2730 /* path can't contain expanded value or value wasn't retrieved */
2731 if (cur
->len
== 0 || cur
->len
> pathlen
||
2732 CompareStringOrdinal( cur
->path
, cur
->len
, path
, cur
->len
, TRUE
) != CSTR_EQUAL
)
2738 if (cur
->len
> match
->len
)
2743 needed
= lstrlenW(match
->var
) + 1 + pathlen
- match
->len
;
2744 if (match
->len
== 0 || needed
> buf_len
) return FALSE
;
2746 lstrcpyW(buffer
, match
->var
);
2747 lstrcatW(buffer
, &path
[match
->len
]);
2748 TRACE("ret %s\n", debugstr_w(buffer
));
2755 URL_SCHEME scheme_number
;
2756 const WCHAR
*scheme_name
;
2760 { URL_SCHEME_FTP
, L
"ftp"},
2761 { URL_SCHEME_HTTP
, L
"http"},
2762 { URL_SCHEME_GOPHER
, L
"gopher"},
2763 { URL_SCHEME_MAILTO
, L
"mailto"},
2764 { URL_SCHEME_NEWS
, L
"news"},
2765 { URL_SCHEME_NNTP
, L
"nntp"},
2766 { URL_SCHEME_TELNET
, L
"telnet"},
2767 { URL_SCHEME_WAIS
, L
"wais"},
2768 { URL_SCHEME_FILE
, L
"file"},
2769 { URL_SCHEME_MK
, L
"mk"},
2770 { URL_SCHEME_HTTPS
, L
"https"},
2771 { URL_SCHEME_SHELL
, L
"shell"},
2772 { URL_SCHEME_SNEWS
, L
"snews"},
2773 { URL_SCHEME_LOCAL
, L
"local"},
2774 { URL_SCHEME_JAVASCRIPT
, L
"javascript"},
2775 { URL_SCHEME_VBSCRIPT
, L
"vbscript"},
2776 { URL_SCHEME_ABOUT
, L
"about"},
2777 { URL_SCHEME_RES
, L
"res"},
2780 static DWORD
get_scheme_code(const WCHAR
*scheme
, DWORD scheme_len
)
2784 for (i
= 0; i
< ARRAY_SIZE(url_schemes
); ++i
)
2786 if (scheme_len
== lstrlenW(url_schemes
[i
].scheme_name
)
2787 && !wcsnicmp(scheme
, url_schemes
[i
].scheme_name
, scheme_len
))
2788 return url_schemes
[i
].scheme_number
;
2791 return URL_SCHEME_UNKNOWN
;
2794 HRESULT WINAPI
ParseURLA(const char *url
, PARSEDURLA
*result
)
2796 WCHAR scheme
[INTERNET_MAX_SCHEME_LENGTH
];
2797 const char *ptr
= url
;
2800 TRACE("%s, %p\n", wine_dbgstr_a(url
), result
);
2802 if (result
->cbSize
!= sizeof(*result
))
2803 return E_INVALIDARG
;
2805 while (*ptr
&& ((*ptr
>= 'a' && *ptr
<= 'z') || (*ptr
>= 'A' && *ptr
<= 'Z') ||
2806 (*ptr
>= '0' && *ptr
<= '9') || *ptr
== '-' || *ptr
== '+' || *ptr
== '.'))
2809 if (*ptr
!= ':' || ptr
<= url
+ 1)
2811 result
->pszProtocol
= NULL
;
2812 return URL_E_INVALID_SYNTAX
;
2815 result
->pszProtocol
= url
;
2816 result
->cchProtocol
= ptr
- url
;
2817 result
->pszSuffix
= ptr
+ 1;
2818 result
->cchSuffix
= strlen(result
->pszSuffix
);
2820 len
= MultiByteToWideChar(CP_ACP
, 0, url
, ptr
- url
, scheme
, ARRAY_SIZE(scheme
));
2821 result
->nScheme
= get_scheme_code(scheme
, len
);
2826 HRESULT WINAPI
ParseURLW(const WCHAR
*url
, PARSEDURLW
*result
)
2828 const WCHAR
*ptr
= url
;
2830 TRACE("%s, %p\n", wine_dbgstr_w(url
), result
);
2832 if (result
->cbSize
!= sizeof(*result
))
2833 return E_INVALIDARG
;
2835 while (*ptr
&& (isalnum(*ptr
) || *ptr
== '-' || *ptr
== '+' || *ptr
== '.'))
2838 if (*ptr
!= ':' || ptr
<= url
+ 1)
2840 result
->pszProtocol
= NULL
;
2841 return URL_E_INVALID_SYNTAX
;
2844 result
->pszProtocol
= url
;
2845 result
->cchProtocol
= ptr
- url
;
2846 result
->pszSuffix
= ptr
+ 1;
2847 result
->cchSuffix
= lstrlenW(result
->pszSuffix
);
2848 result
->nScheme
= get_scheme_code(url
, ptr
- url
);
2853 HRESULT WINAPI
UrlUnescapeA(char *url
, char *unescaped
, DWORD
*unescaped_len
, DWORD flags
)
2855 BOOL stop_unescaping
= FALSE
;
2861 TRACE("%s, %p, %p, %#x\n", wine_dbgstr_a(url
), unescaped
, unescaped_len
, flags
);
2864 return E_INVALIDARG
;
2866 if (flags
& URL_UNESCAPE_INPLACE
)
2870 if (!unescaped
|| !unescaped_len
) return E_INVALIDARG
;
2874 for (src
= url
, needed
= 0; *src
; src
++, needed
++)
2876 if (flags
& URL_DONT_UNESCAPE_EXTRA_INFO
&& (*src
== '#' || *src
== '?'))
2878 stop_unescaping
= TRUE
;
2881 else if (*src
== '%' && isxdigit(*(src
+ 1)) && isxdigit(*(src
+ 2)) && !stop_unescaping
)
2885 memcpy(buf
, src
+ 1, 2);
2887 ih
= strtol(buf
, NULL
, 16);
2889 src
+= 2; /* Advance to end of escape */
2894 if (flags
& URL_UNESCAPE_INPLACE
|| needed
< *unescaped_len
)
2898 if (flags
& URL_UNESCAPE_INPLACE
|| needed
< *unescaped_len
)
2905 needed
++; /* add one for the '\0' */
2909 if (!(flags
& URL_UNESCAPE_INPLACE
))
2910 *unescaped_len
= needed
;
2913 TRACE("result %s\n", flags
& URL_UNESCAPE_INPLACE
? wine_dbgstr_a(url
) : wine_dbgstr_a(unescaped
));
2918 HRESULT WINAPI
UrlUnescapeW(WCHAR
*url
, WCHAR
*unescaped
, DWORD
*unescaped_len
, DWORD flags
)
2920 BOOL stop_unescaping
= FALSE
;
2926 TRACE("%s, %p, %p, %#x\n", wine_dbgstr_w(url
), unescaped
, unescaped_len
, flags
);
2929 return E_INVALIDARG
;
2931 if (flags
& URL_UNESCAPE_INPLACE
)
2935 if (!unescaped
|| !unescaped_len
) return E_INVALIDARG
;
2939 for (src
= url
, needed
= 0; *src
; src
++, needed
++)
2941 if (flags
& URL_DONT_UNESCAPE_EXTRA_INFO
&& (*src
== '#' || *src
== '?'))
2943 stop_unescaping
= TRUE
;
2946 else if (*src
== '%' && isxdigit(*(src
+ 1)) && isxdigit(*(src
+ 2)) && !stop_unescaping
)
2949 WCHAR buf
[5] = L
"0x";
2950 memcpy(buf
+ 2, src
+ 1, 2*sizeof(WCHAR
));
2952 StrToIntExW(buf
, STIF_SUPPORT_HEX
, &ih
);
2954 src
+= 2; /* Advance to end of escape */
2959 if (flags
& URL_UNESCAPE_INPLACE
|| needed
< *unescaped_len
)
2963 if (flags
& URL_UNESCAPE_INPLACE
|| needed
< *unescaped_len
)
2970 needed
++; /* add one for the '\0' */
2974 if (!(flags
& URL_UNESCAPE_INPLACE
))
2975 *unescaped_len
= needed
;
2978 TRACE("result %s\n", flags
& URL_UNESCAPE_INPLACE
? wine_dbgstr_w(url
) : wine_dbgstr_w(unescaped
));
2983 HRESULT WINAPI
PathCreateFromUrlA(const char *pszUrl
, char *pszPath
, DWORD
*pcchPath
, DWORD dwReserved
)
2985 WCHAR bufW
[MAX_PATH
];
2986 WCHAR
*pathW
= bufW
;
2987 UNICODE_STRING urlW
;
2989 DWORD lenW
= ARRAY_SIZE(bufW
), lenA
;
2991 if (!pszUrl
|| !pszPath
|| !pcchPath
|| !*pcchPath
)
2992 return E_INVALIDARG
;
2994 if(!RtlCreateUnicodeStringFromAsciiz(&urlW
, pszUrl
))
2995 return E_INVALIDARG
;
2996 if((ret
= PathCreateFromUrlW(urlW
.Buffer
, pathW
, &lenW
, dwReserved
)) == E_POINTER
) {
2997 pathW
= HeapAlloc(GetProcessHeap(), 0, lenW
* sizeof(WCHAR
));
2998 ret
= PathCreateFromUrlW(urlW
.Buffer
, pathW
, &lenW
, dwReserved
);
3001 RtlUnicodeToMultiByteSize(&lenA
, pathW
, lenW
* sizeof(WCHAR
));
3002 if(*pcchPath
> lenA
) {
3003 RtlUnicodeToMultiByteN(pszPath
, *pcchPath
- 1, &lenA
, pathW
, lenW
* sizeof(WCHAR
));
3007 *pcchPath
= lenA
+ 1;
3011 if(pathW
!= bufW
) HeapFree(GetProcessHeap(), 0, pathW
);
3012 RtlFreeUnicodeString(&urlW
);
3016 HRESULT WINAPI
PathCreateFromUrlW(const WCHAR
*url
, WCHAR
*path
, DWORD
*pcchPath
, DWORD dwReserved
)
3018 DWORD nslashes
, unescape
, len
;
3023 TRACE("%s, %p, %p, %#x\n", wine_dbgstr_w(url
), path
, pcchPath
, dwReserved
);
3025 if (!url
|| !path
|| !pcchPath
|| !*pcchPath
)
3026 return E_INVALIDARG
;
3028 if (wcsnicmp( url
, L
"file:", 5))
3029 return E_INVALIDARG
;
3035 while (*src
== '/' || *src
== '\\')
3041 /* We need a temporary buffer so we can compute what size to ask for.
3042 * We know that the final string won't be longer than the current pszUrl
3043 * plus at most two backslashes. All the other transformations make it
3046 len
= 2 + lstrlenW(url
) + 1;
3047 if (*pcchPath
< len
)
3048 tpath
= heap_alloc(len
* sizeof(WCHAR
));
3058 /* 'file:' + escaped DOS path */
3061 /* 'file:/' + escaped DOS path */
3064 /* 'file:///' (implied localhost) + escaped DOS path */
3065 if (!is_escaped_drive_spec( src
))
3069 if (lstrlenW(src
) >= 10 && !wcsnicmp( src
, L
"localhost", 9) && (src
[9] == '/' || src
[9] == '\\'))
3071 /* 'file://localhost/' + escaped DOS path */
3074 else if (is_escaped_drive_spec( src
))
3076 /* 'file://' + unescaped DOS path */
3081 /* 'file://hostname:port/path' (where path is escaped)
3082 * or 'file:' + escaped UNC path (\\server\share\path)
3083 * The second form is clearly specific to Windows and it might
3084 * even be doing a network lookup to try to figure it out.
3086 while (*src
&& *src
!= '/' && *src
!= '\\')
3089 StrCpyNW(dst
, url
, len
+ 1);
3091 if (*src
&& is_escaped_drive_spec( src
+ 1 ))
3093 /* 'Forget' to add a trailing '/', just like Windows */
3099 /* 'file://' + unescaped UNC path (\\server\share\path) */
3101 if (is_escaped_drive_spec( src
))
3105 /* 'file:/...' + escaped UNC path (\\server\share\path) */
3109 /* Copy the remainder of the path */
3110 len
+= lstrlenW(src
);
3113 /* First do the Windows-specific path conversions */
3114 for (dst
= tpath
; *dst
; dst
++)
3115 if (*dst
== '/') *dst
= '\\';
3116 if (is_escaped_drive_spec( tpath
))
3117 tpath
[1] = ':'; /* c| -> c: */
3119 /* And only then unescape the path (i.e. escaped slashes are left as is) */
3122 hr
= UrlUnescapeW(tpath
, NULL
, &len
, URL_UNESCAPE_INPLACE
);
3125 /* When working in-place UrlUnescapeW() does not set len */
3126 len
= lstrlenW(tpath
);
3130 if (*pcchPath
< len
+ 1)
3133 *pcchPath
= len
+ 1;
3139 lstrcpyW(path
, tpath
);
3144 TRACE("Returning (%u) %s\n", *pcchPath
, wine_dbgstr_w(path
));
3148 HRESULT WINAPI
PathCreateFromUrlAlloc(const WCHAR
*url
, WCHAR
**path
, DWORD reserved
)
3150 WCHAR pathW
[MAX_PATH
];
3155 hr
= PathCreateFromUrlW(url
, pathW
, &size
, reserved
);
3158 /* Yes, this is supposed to crash if 'path' is NULL */
3159 *path
= StrDupW(pathW
);
3165 BOOL WINAPI
PathIsURLA(const char *path
)
3170 TRACE("%s\n", wine_dbgstr_a(path
));
3172 if (!path
|| !*path
)
3176 base
.cbSize
= sizeof(base
);
3177 hr
= ParseURLA(path
, &base
);
3178 return hr
== S_OK
&& (base
.nScheme
!= URL_SCHEME_INVALID
);
3181 BOOL WINAPI
PathIsURLW(const WCHAR
*path
)
3186 TRACE("%s\n", wine_dbgstr_w(path
));
3188 if (!path
|| !*path
)
3192 base
.cbSize
= sizeof(base
);
3193 hr
= ParseURLW(path
, &base
);
3194 return hr
== S_OK
&& (base
.nScheme
!= URL_SCHEME_INVALID
);
3197 #define WINE_URL_BASH_AS_SLASH 0x01
3198 #define WINE_URL_COLLAPSE_SLASHES 0x02
3199 #define WINE_URL_ESCAPE_SLASH 0x04
3200 #define WINE_URL_ESCAPE_HASH 0x08
3201 #define WINE_URL_ESCAPE_QUESTION 0x10
3202 #define WINE_URL_STOP_ON_HASH 0x20
3203 #define WINE_URL_STOP_ON_QUESTION 0x40
3205 static BOOL
url_needs_escape(WCHAR ch
, DWORD flags
, DWORD int_flags
)
3207 if (flags
& URL_ESCAPE_SPACES_ONLY
)
3210 if ((flags
& URL_ESCAPE_PERCENT
) && (ch
== '%'))
3213 if ((flags
& URL_ESCAPE_AS_UTF8
) && (ch
>= 0x80))
3216 if (ch
<= 31 || (ch
>= 127 && ch
<= 255) )
3238 return !!(int_flags
& WINE_URL_ESCAPE_SLASH
);
3240 return !!(int_flags
& WINE_URL_ESCAPE_QUESTION
);
3242 return !!(int_flags
& WINE_URL_ESCAPE_HASH
);
3248 HRESULT WINAPI
UrlEscapeA(const char *url
, char *escaped
, DWORD
*escaped_len
, DWORD flags
)
3250 WCHAR bufW
[INTERNET_MAX_URL_LENGTH
];
3251 WCHAR
*escapedW
= bufW
;
3252 UNICODE_STRING urlW
;
3254 DWORD lenW
= ARRAY_SIZE(bufW
), lenA
;
3256 if (!escaped
|| !escaped_len
|| !*escaped_len
)
3257 return E_INVALIDARG
;
3259 if (!RtlCreateUnicodeStringFromAsciiz(&urlW
, url
))
3260 return E_INVALIDARG
;
3262 if (flags
& URL_ESCAPE_AS_UTF8
)
3264 RtlFreeUnicodeString(&urlW
);
3268 if ((hr
= UrlEscapeW(urlW
.Buffer
, escapedW
, &lenW
, flags
)) == E_POINTER
)
3270 escapedW
= heap_alloc(lenW
* sizeof(WCHAR
));
3271 hr
= UrlEscapeW(urlW
.Buffer
, escapedW
, &lenW
, flags
);
3276 RtlUnicodeToMultiByteSize(&lenA
, escapedW
, lenW
* sizeof(WCHAR
));
3277 if (*escaped_len
> lenA
)
3279 RtlUnicodeToMultiByteN(escaped
, *escaped_len
- 1, &lenA
, escapedW
, lenW
* sizeof(WCHAR
));
3281 *escaped_len
= lenA
;
3285 *escaped_len
= lenA
+ 1;
3289 if (escapedW
!= bufW
)
3290 heap_free(escapedW
);
3291 RtlFreeUnicodeString(&urlW
);
3295 HRESULT WINAPI
UrlEscapeW(const WCHAR
*url
, WCHAR
*escaped
, DWORD
*escaped_len
, DWORD flags
)
3297 DWORD needed
= 0, slashes
= 0, int_flags
;
3298 WCHAR next
[12], *dst
, *dst_ptr
;
3299 BOOL stop_escaping
= FALSE
;
3300 PARSEDURLW parsed_url
;
3305 TRACE("%p, %s, %p, %p, %#x\n", url
, wine_dbgstr_w(url
), escaped
, escaped_len
, flags
);
3307 if (!url
|| !escaped_len
|| !escaped
|| *escaped_len
== 0)
3308 return E_INVALIDARG
;
3310 if (flags
& ~(URL_ESCAPE_SPACES_ONLY
| URL_ESCAPE_SEGMENT_ONLY
| URL_DONT_ESCAPE_EXTRA_INFO
|
3311 URL_ESCAPE_PERCENT
| URL_ESCAPE_AS_UTF8
))
3313 FIXME("Unimplemented flags: %08x\n", flags
);
3316 dst_ptr
= dst
= heap_alloc(*escaped_len
* sizeof(WCHAR
));
3318 return E_OUTOFMEMORY
;
3321 if (flags
& URL_ESCAPE_SPACES_ONLY
)
3322 /* if SPACES_ONLY specified, reset the other controls */
3323 flags
&= ~(URL_DONT_ESCAPE_EXTRA_INFO
| URL_ESCAPE_PERCENT
| URL_ESCAPE_SEGMENT_ONLY
);
3325 /* if SPACES_ONLY *not* specified the assume DONT_ESCAPE_EXTRA_INFO */
3326 flags
|= URL_DONT_ESCAPE_EXTRA_INFO
;
3329 if (flags
& URL_ESCAPE_SEGMENT_ONLY
)
3330 int_flags
= WINE_URL_ESCAPE_QUESTION
| WINE_URL_ESCAPE_HASH
| WINE_URL_ESCAPE_SLASH
;
3333 parsed_url
.cbSize
= sizeof(parsed_url
);
3334 if (ParseURLW(url
, &parsed_url
) != S_OK
)
3335 parsed_url
.nScheme
= URL_SCHEME_INVALID
;
3337 TRACE("scheme = %d (%s)\n", parsed_url
.nScheme
, debugstr_wn(parsed_url
.pszProtocol
, parsed_url
.cchProtocol
));
3339 if (flags
& URL_DONT_ESCAPE_EXTRA_INFO
)
3340 int_flags
= WINE_URL_STOP_ON_HASH
| WINE_URL_STOP_ON_QUESTION
;
3342 switch(parsed_url
.nScheme
) {
3343 case URL_SCHEME_FILE
:
3344 int_flags
|= WINE_URL_BASH_AS_SLASH
| WINE_URL_COLLAPSE_SLASHES
| WINE_URL_ESCAPE_HASH
;
3345 int_flags
&= ~WINE_URL_STOP_ON_HASH
;
3348 case URL_SCHEME_HTTP
:
3349 case URL_SCHEME_HTTPS
:
3350 int_flags
|= WINE_URL_BASH_AS_SLASH
;
3351 if(parsed_url
.pszSuffix
[0] != '/' && parsed_url
.pszSuffix
[0] != '\\')
3352 int_flags
|= WINE_URL_ESCAPE_SLASH
;
3355 case URL_SCHEME_MAILTO
:
3356 int_flags
|= WINE_URL_ESCAPE_SLASH
| WINE_URL_ESCAPE_QUESTION
| WINE_URL_ESCAPE_HASH
;
3357 int_flags
&= ~(WINE_URL_STOP_ON_QUESTION
| WINE_URL_STOP_ON_HASH
);
3360 case URL_SCHEME_INVALID
:
3363 case URL_SCHEME_FTP
:
3365 if(parsed_url
.pszSuffix
[0] != '/')
3366 int_flags
|= WINE_URL_ESCAPE_SLASH
;
3371 for (src
= url
; *src
; )
3376 if ((int_flags
& WINE_URL_COLLAPSE_SLASHES
) && src
== url
+ parsed_url
.cchProtocol
+ 1)
3378 while (cur
== '/' || cur
== '\\')
3383 if (slashes
== 2 && !wcsnicmp(src
, L
"localhost", 9)) { /* file://localhost/ -> file:/// */
3384 if(src
[9] == '/' || src
[9] == '\\') src
+= 10;
3392 next
[0] = next
[1] = next
[2] = '/';
3399 next
[0] = next
[1] = '/';
3406 if (cur
== '#' && (int_flags
& WINE_URL_STOP_ON_HASH
))
3407 stop_escaping
= TRUE
;
3409 if (cur
== '?' && (int_flags
& WINE_URL_STOP_ON_QUESTION
))
3410 stop_escaping
= TRUE
;
3412 if (cur
== '\\' && (int_flags
& WINE_URL_BASH_AS_SLASH
) && !stop_escaping
) cur
= '/';
3414 if (url_needs_escape(cur
, flags
, int_flags
) && !stop_escaping
)
3416 if (flags
& URL_ESCAPE_AS_UTF8
)
3420 if ((cur
>= 0xd800 && cur
<= 0xdfff) && (src
[1] >= 0xdc00 && src
[1] <= 0xdfff))
3422 len
= WideCharToMultiByte(CP_UTF8
, WC_ERR_INVALID_CHARS
, src
, 2, utf
, sizeof(utf
), NULL
, NULL
);
3426 len
= WideCharToMultiByte(CP_UTF8
, WC_ERR_INVALID_CHARS
, &cur
, 1, utf
, sizeof(utf
), NULL
, NULL
);
3436 for (i
= 0; i
< len
; ++i
)
3439 next
[i
*3+1] = hexDigits
[(utf
[i
] >> 4) & 0xf];
3440 next
[i
*3+2] = hexDigits
[utf
[i
] & 0xf];
3447 next
[1] = hexDigits
[(cur
>> 4) & 0xf];
3448 next
[2] = hexDigits
[cur
& 0xf];
3460 if (needed
+ len
<= *escaped_len
)
3462 memcpy(dst
, next
, len
*sizeof(WCHAR
));
3468 if (needed
< *escaped_len
)
3471 memcpy(escaped
, dst_ptr
, (needed
+1)*sizeof(WCHAR
));
3476 needed
++; /* add one for the '\0' */
3479 *escaped_len
= needed
;
3485 HRESULT WINAPI
UrlCanonicalizeA(const char *src_url
, char *canonicalized
, DWORD
*canonicalized_len
, DWORD flags
)
3487 LPWSTR url
, canonical
;
3490 TRACE("%s, %p, %p, %#x\n", wine_dbgstr_a(src_url
), canonicalized
, canonicalized_len
, flags
);
3492 if (!src_url
|| !canonicalized
|| !canonicalized_len
|| !*canonicalized_len
)
3493 return E_INVALIDARG
;
3495 url
= heap_strdupAtoW(src_url
);
3496 canonical
= heap_alloc(*canonicalized_len
* sizeof(WCHAR
));
3497 if (!url
|| !canonical
)
3500 heap_free(canonical
);
3501 return E_OUTOFMEMORY
;
3504 hr
= UrlCanonicalizeW(url
, canonical
, canonicalized_len
, flags
);
3506 WideCharToMultiByte(CP_ACP
, 0, canonical
, -1, canonicalized
, *canonicalized_len
+ 1, NULL
, NULL
);
3509 heap_free(canonical
);
3513 HRESULT WINAPI
UrlCanonicalizeW(const WCHAR
*src_url
, WCHAR
*canonicalized
, DWORD
*canonicalized_len
, DWORD flags
)
3515 WCHAR
*url_copy
, *url
, *wk2
, *mp
, *mp2
;
3516 DWORD nByteLen
, nLen
, nWkLen
;
3517 const WCHAR
*wk1
, *root
;
3524 TRACE("%s, %p, %p, %#x\n", wine_dbgstr_w(src_url
), canonicalized
, canonicalized_len
, flags
);
3526 if (!src_url
|| !canonicalized
|| !canonicalized_len
|| !*canonicalized_len
)
3527 return E_INVALIDARG
;
3535 /* Remove '\t' characters from URL */
3536 nByteLen
= (lstrlenW(src_url
) + 1) * sizeof(WCHAR
); /* length in bytes */
3537 url
= HeapAlloc(GetProcessHeap(), 0, nByteLen
);
3539 return E_OUTOFMEMORY
;
3550 /* Allocate memory for simplified URL (before escaping) */
3551 nByteLen
= (wk2
-url
)*sizeof(WCHAR
);
3552 url_copy
= heap_alloc(nByteLen
+ sizeof(L
"file:///"));
3556 return E_OUTOFMEMORY
;
3559 is_file_url
= !wcsncmp(url
, L
"file:", 5);
3561 if ((nByteLen
>= 5*sizeof(WCHAR
) && !wcsncmp(url
, L
"http:", 5)) || is_file_url
)
3564 if ((flags
& (URL_FILE_USE_PATHURL
| URL_WININET_COMPATIBILITY
)) && is_file_url
)
3567 if (nByteLen
>= 4*sizeof(WCHAR
) && !wcsncmp(url
, L
"res:", 4))
3569 flags
&= ~URL_FILE_USE_PATHURL
;
3576 * 1 have 2[+] alnum 2,3
3577 * 2 have scheme (found :) 4,6,3
3578 * 3 failed (no location)
3580 * 5 have 1[+] alnum 6,3
3581 * 6 have location (found /) save root location
3591 lstrcpyW(wk2
, L
"file:///");
3592 wk2
+= lstrlenW(wk2
);
3593 if (flags
& (URL_FILE_USE_PATHURL
| URL_WININET_COMPATIBILITY
))
3599 flags
|= URL_ESCAPE_UNSAFE
;
3603 else if (url
[0] == '/')
3614 if (!isalnum(*wk1
)) {state
= 3; break;}
3616 if (!isalnum(*wk1
)) {state
= 3; break;}
3622 if (*wk1
++ == ':') state
= 2;
3626 if (*wk1
!= '/') {state
= 6; break;}
3628 if ((flags
& URL_FILE_USE_PATHURL
) && nByteLen
>= 9*sizeof(WCHAR
) && is_file_url
3629 && !wcsncmp(wk1
, L
"localhost", 9))
3632 while (*wk1
== '\\' && (flags
& URL_FILE_USE_PATHURL
))
3636 if (*wk1
== '/' && (flags
& URL_FILE_USE_PATHURL
))
3638 else if (is_file_url
)
3640 const WCHAR
*body
= wk1
;
3642 while (*body
== '/')
3645 if (is_drive_spec( body
))
3647 if (!(flags
& (URL_WININET_COMPATIBILITY
| URL_FILE_USE_PATHURL
)))
3657 if (flags
& URL_WININET_COMPATIBILITY
)
3659 if (*wk1
== '/' && *(wk1
+ 1) != '/')
3671 if (*wk1
== '/' && *(wk1
+1) != '/')
3685 nWkLen
= lstrlenW(wk1
);
3686 memcpy(wk2
, wk1
, (nWkLen
+ 1) * sizeof(WCHAR
));
3695 if (*mp
== '/' || *mp
== '\\')
3702 if (!isalnum(*wk1
) && (*wk1
!= '-') && (*wk1
!= '.') && (*wk1
!= ':'))
3707 while (isalnum(*wk1
) || (*wk1
== '-') || (*wk1
== '.') || (*wk1
== ':'))
3719 if (*wk1
!= '/' && *wk1
!= '\\')
3724 while (*wk1
== '/' || *wk1
== '\\')
3735 if (flags
& URL_DONT_SIMPLIFY
)
3741 /* Now at root location, cannot back up any more. */
3742 /* "root" will point at the '/' */
3747 mp
= wcschr(wk1
, '/');
3748 mp2
= wcschr(wk1
, '\\');
3749 if (mp2
&& (!mp
|| mp2
< mp
))
3753 nWkLen
= lstrlenW(wk1
);
3754 memcpy(wk2
, wk1
, (nWkLen
+ 1) * sizeof(WCHAR
));
3762 memcpy(wk2
, wk1
, nLen
* sizeof(WCHAR
));
3774 TRACE("found '/.'\n");
3775 if (wk1
[1] == '/' || wk1
[1] == '\\')
3777 /* case of /./ -> skip the ./ */
3780 else if (wk1
[1] == '.' && (wk1
[2] == '/' || wk1
[2] == '\\' || wk1
[2] == '?'
3781 || wk1
[2] == '#' || !wk1
[2]))
3783 /* case /../ -> need to backup wk2 */
3784 TRACE("found '/../'\n");
3785 *(wk2
-1) = '\0'; /* set end of string */
3786 mp
= wcsrchr(root
, '/');
3787 mp2
= wcsrchr(root
, '\\');
3788 if (mp2
&& (!mp
|| mp2
< mp
))
3790 if (mp
&& (mp
>= root
))
3792 /* found valid backup point */
3794 if(wk1
[2] != '/' && wk1
[2] != '\\')
3801 /* did not find point, restore '/' */
3813 FIXME("how did we get here - state=%d\n", state
);
3814 heap_free(url_copy
);
3816 return E_INVALIDARG
;
3819 TRACE("Simplified, orig <%s>, simple <%s>\n", wine_dbgstr_w(src_url
), wine_dbgstr_w(url_copy
));
3821 nLen
= lstrlenW(url_copy
);
3822 while ((nLen
> 0) && ((url_copy
[nLen
-1] <= ' ')))
3825 if ((flags
& URL_UNESCAPE
) ||
3826 ((flags
& URL_FILE_USE_PATHURL
) && nByteLen
>= 5*sizeof(WCHAR
) && !wcsncmp(url
, L
"file:", 5)))
3828 UrlUnescapeW(url_copy
, NULL
, &nLen
, URL_UNESCAPE_INPLACE
);
3831 escape_flags
= flags
& (URL_ESCAPE_UNSAFE
| URL_ESCAPE_SPACES_ONLY
| URL_ESCAPE_PERCENT
|
3832 URL_DONT_ESCAPE_EXTRA_INFO
| URL_ESCAPE_SEGMENT_ONLY
);
3836 escape_flags
&= ~URL_ESCAPE_UNSAFE
;
3837 hr
= UrlEscapeW(url_copy
, canonicalized
, canonicalized_len
, escape_flags
);
3841 /* No escaping needed, just copy the string */
3842 nLen
= lstrlenW(url_copy
);
3843 if (nLen
< *canonicalized_len
)
3844 memcpy(canonicalized
, url_copy
, (nLen
+ 1)*sizeof(WCHAR
));
3850 *canonicalized_len
= nLen
;
3853 heap_free(url_copy
);
3857 TRACE("result %s\n", wine_dbgstr_w(canonicalized
));
3862 HRESULT WINAPI
UrlApplySchemeA(const char *url
, char *out
, DWORD
*out_len
, DWORD flags
)
3868 TRACE("%s, %p, %p:out size %d, %#x\n", wine_dbgstr_a(url
), out
, out_len
, out_len
? *out_len
: 0, flags
);
3870 if (!url
|| !out
|| !out_len
)
3871 return E_INVALIDARG
;
3873 inW
= heap_alloc(2 * INTERNET_MAX_URL_LENGTH
* sizeof(WCHAR
));
3874 outW
= inW
+ INTERNET_MAX_URL_LENGTH
;
3876 MultiByteToWideChar(CP_ACP
, 0, url
, -1, inW
, INTERNET_MAX_URL_LENGTH
);
3877 len
= INTERNET_MAX_URL_LENGTH
;
3879 hr
= UrlApplySchemeW(inW
, outW
, &len
, flags
);
3886 len
= WideCharToMultiByte(CP_ACP
, 0, outW
, -1, NULL
, 0, NULL
, NULL
);
3893 WideCharToMultiByte(CP_ACP
, 0, outW
, -1, out
, *out_len
, NULL
, NULL
);
3902 static HRESULT
url_guess_scheme(const WCHAR
*url
, WCHAR
*out
, DWORD
*out_len
)
3904 WCHAR reg_path
[MAX_PATH
], value
[MAX_PATH
], data
[MAX_PATH
];
3905 DWORD value_len
, data_len
, dwType
, i
;
3911 MultiByteToWideChar(CP_ACP
, 0,
3912 "Software\\Microsoft\\Windows\\CurrentVersion\\URL\\Prefixes", -1, reg_path
, MAX_PATH
);
3913 RegOpenKeyExW(HKEY_LOCAL_MACHINE
, reg_path
, 0, 1, &newkey
);
3915 while (value_len
= data_len
= MAX_PATH
,
3916 RegEnumValueW(newkey
, index
, value
, &value_len
, 0, &dwType
, (LPVOID
)data
, &data_len
) == 0)
3918 TRACE("guess %d %s is %s\n", index
, wine_dbgstr_w(value
), wine_dbgstr_w(data
));
3921 for (i
= 0; i
< value_len
; ++i
)
3925 /* remember that TRUE is not-equal */
3926 j
= ChrCmpIW(Wxx
, Wyy
);
3929 if ((i
== value_len
) && !j
)
3931 if (lstrlenW(data
) + lstrlenW(url
) + 1 > *out_len
)
3933 *out_len
= lstrlenW(data
) + lstrlenW(url
) + 1;
3934 RegCloseKey(newkey
);
3937 lstrcpyW(out
, data
);
3939 *out_len
= lstrlenW(out
);
3940 TRACE("matched and set to %s\n", wine_dbgstr_w(out
));
3941 RegCloseKey(newkey
);
3946 RegCloseKey(newkey
);
3950 static HRESULT
url_create_from_path(const WCHAR
*path
, WCHAR
*url
, DWORD
*url_len
)
3952 PARSEDURLW parsed_url
;
3957 parsed_url
.cbSize
= sizeof(parsed_url
);
3958 if (ParseURLW(path
, &parsed_url
) == S_OK
)
3960 if (parsed_url
.nScheme
!= URL_SCHEME_INVALID
&& parsed_url
.cchProtocol
> 1)
3962 needed
= lstrlenW(path
);
3963 if (needed
>= *url_len
)
3965 *url_len
= needed
+ 1;
3976 new_url
= heap_alloc((lstrlenW(path
) + 9) * sizeof(WCHAR
)); /* "file:///" + path length + 1 */
3977 lstrcpyW(new_url
, L
"file:");
3978 if (is_drive_spec( path
)) lstrcatW(new_url
, L
"///");
3979 lstrcatW(new_url
, path
);
3980 hr
= UrlEscapeW(new_url
, url
, url_len
, URL_ESCAPE_PERCENT
);
3985 static HRESULT
url_apply_default_scheme(const WCHAR
*url
, WCHAR
*out
, DWORD
*length
)
3987 DWORD data_len
, dwType
;
3988 WCHAR data
[MAX_PATH
];
3991 /* get and prepend default */
3992 RegOpenKeyExW(HKEY_LOCAL_MACHINE
, L
"Software\\Microsoft\\Windows\\CurrentVersion\\URL\\DefaultPrefix",
3994 data_len
= sizeof(data
);
3995 RegQueryValueExW(newkey
, NULL
, 0, &dwType
, (BYTE
*)data
, &data_len
);
3996 RegCloseKey(newkey
);
3997 if (lstrlenW(data
) + lstrlenW(url
) + 1 > *length
)
3999 *length
= lstrlenW(data
) + lstrlenW(url
) + 1;
4002 lstrcpyW(out
, data
);
4004 *length
= lstrlenW(out
);
4005 TRACE("used default %s\n", wine_dbgstr_w(out
));
4009 HRESULT WINAPI
UrlApplySchemeW(const WCHAR
*url
, WCHAR
*out
, DWORD
*length
, DWORD flags
)
4011 PARSEDURLW in_scheme
;
4015 TRACE("%s, %p, %p:out size %d, %#x\n", wine_dbgstr_w(url
), out
, length
, length
? *length
: 0, flags
);
4017 if (!url
|| !out
|| !length
)
4018 return E_INVALIDARG
;
4020 if (flags
& URL_APPLY_GUESSFILE
)
4022 if (*length
> 1 && ':' == url
[1])
4025 hr
= url_create_from_path(url
, out
, &res1
);
4026 if (hr
== S_OK
|| hr
== E_POINTER
)
4031 else if (hr
== S_FALSE
)
4038 in_scheme
.cbSize
= sizeof(in_scheme
);
4039 /* See if the base has a scheme */
4040 res1
= ParseURLW(url
, &in_scheme
);
4043 /* no scheme in input, need to see if we need to guess */
4044 if (flags
& URL_APPLY_GUESSSCHEME
)
4046 if ((hr
= url_guess_scheme(url
, out
, length
)) != E_FAIL
)
4051 /* If we are here, then either invalid scheme,
4052 * or no scheme and can't/failed guess.
4054 if ((((res1
== 0) && (flags
& URL_APPLY_FORCEAPPLY
)) || ((res1
!= 0)) ) && (flags
& URL_APPLY_DEFAULT
))
4055 return url_apply_default_scheme(url
, out
, length
);
4060 INT WINAPI
UrlCompareA(const char *url1
, const char *url2
, BOOL ignore_slash
)
4062 INT ret
, len
, len1
, len2
;
4065 return strcmp(url1
, url2
);
4066 len1
= strlen(url1
);
4067 if (url1
[len1
-1] == '/') len1
--;
4068 len2
= strlen(url2
);
4069 if (url2
[len2
-1] == '/') len2
--;
4071 return strncmp(url1
, url2
, len1
);
4072 len
= min(len1
, len2
);
4073 ret
= strncmp(url1
, url2
, len
);
4074 if (ret
) return ret
;
4075 if (len1
> len2
) return 1;
4079 INT WINAPI
UrlCompareW(const WCHAR
*url1
, const WCHAR
*url2
, BOOL ignore_slash
)
4081 size_t len
, len1
, len2
;
4085 return lstrcmpW(url1
, url2
);
4086 len1
= lstrlenW(url1
);
4087 if (url1
[len1
-1] == '/') len1
--;
4088 len2
= lstrlenW(url2
);
4089 if (url2
[len2
-1] == '/') len2
--;
4091 return wcsncmp(url1
, url2
, len1
);
4092 len
= min(len1
, len2
);
4093 ret
= wcsncmp(url1
, url2
, len
);
4094 if (ret
) return ret
;
4095 if (len1
> len2
) return 1;
4099 HRESULT WINAPI
UrlFixupW(const WCHAR
*url
, WCHAR
*translatedUrl
, DWORD maxChars
)
4103 FIXME("%s, %p, %d stub\n", wine_dbgstr_w(url
), translatedUrl
, maxChars
);
4108 srcLen
= lstrlenW(url
) + 1;
4110 /* For now just copy the URL directly */
4111 lstrcpynW(translatedUrl
, url
, (maxChars
< srcLen
) ? maxChars
: srcLen
);
4116 const char * WINAPI
UrlGetLocationA(const char *url
)
4120 base
.cbSize
= sizeof(base
);
4121 if (ParseURLA(url
, &base
) != S_OK
) return NULL
; /* invalid scheme */
4123 /* if scheme is file: then never return pointer */
4124 if (!strncmp(base
.pszProtocol
, "file", min(4, base
.cchProtocol
)))
4127 /* Look for '#' and return its addr */
4128 return strchr(base
.pszSuffix
, '#');
4131 const WCHAR
* WINAPI
UrlGetLocationW(const WCHAR
*url
)
4135 base
.cbSize
= sizeof(base
);
4136 if (ParseURLW(url
, &base
) != S_OK
) return NULL
; /* invalid scheme */
4138 /* if scheme is file: then never return pointer */
4139 if (!wcsncmp(base
.pszProtocol
, L
"file", min(4, base
.cchProtocol
)))
4142 /* Look for '#' and return its addr */
4143 return wcschr(base
.pszSuffix
, '#');
4146 HRESULT WINAPI
UrlGetPartA(const char *url
, char *out
, DWORD
*out_len
, DWORD part
, DWORD flags
)
4152 if (!url
|| !out
|| !out_len
|| !*out_len
)
4153 return E_INVALIDARG
;
4155 inW
= heap_alloc(2 * INTERNET_MAX_URL_LENGTH
* sizeof(WCHAR
));
4156 outW
= inW
+ INTERNET_MAX_URL_LENGTH
;
4158 MultiByteToWideChar(CP_ACP
, 0, url
, -1, inW
, INTERNET_MAX_URL_LENGTH
);
4160 len
= INTERNET_MAX_URL_LENGTH
;
4161 hr
= UrlGetPartW(inW
, outW
, &len
, part
, flags
);
4168 len2
= WideCharToMultiByte(CP_ACP
, 0, outW
, len
, NULL
, 0, NULL
, NULL
);
4169 if (len2
> *out_len
)
4171 *out_len
= len2
+ 1;
4175 len2
= WideCharToMultiByte(CP_ACP
, 0, outW
, len
+ 1, out
, *out_len
, NULL
, NULL
);
4176 *out_len
= len2
- 1;
4181 static const WCHAR
* scan_url(const WCHAR
*start
, DWORD
*size
, enum url_scan_type type
)
4188 while ((*start
>= 'a' && *start
<= 'z') || (*start
>= '0' && *start
<= '9') ||
4189 *start
== '+' || *start
== '-' || *start
== '.')
4201 if (isalnum(*start
) ||
4202 /* user/password only characters */
4207 /* *extra* characters */
4214 /* *safe* characters */
4225 else if (*start
== '%' && isxdigit(start
[1]) && isxdigit(start
[2]))
4235 while (*start
>= '0' && *start
<= '9')
4243 while (isalnum(*start
) || *start
== '-' || *start
== '.' || *start
== ' ' || *start
== '*')
4251 FIXME("unknown type %d\n", type
);
4258 static LONG
parse_url(const WCHAR
*url
, struct parsed_url
*pl
)
4262 memset(pl
, 0, sizeof(*pl
));
4264 work
= scan_url(pl
->scheme
, &pl
->scheme_len
, SCHEME
);
4265 if (!*work
|| (*work
!= ':')) goto ErrorExit
;
4267 if ((*work
!= '/') || (*(work
+1) != '/')) goto SuccessExit
;
4269 pl
->username
= work
+ 2;
4270 work
= scan_url(pl
->username
, &pl
->username_len
, USERPASS
);
4273 /* parse password */
4275 pl
->password
= work
;
4276 work
= scan_url(pl
->password
, &pl
->password_len
, USERPASS
);
4279 /* what we just parsed must be the hostname and port
4280 * so reset pointers and clear then let it parse */
4281 pl
->username_len
= pl
->password_len
= 0;
4282 work
= pl
->username
- 1;
4283 pl
->username
= pl
->password
= 0;
4286 else if (*work
== '@')
4289 pl
->password_len
= 0;
4292 else if (!*work
|| *work
== '/' || *work
== '.')
4294 /* what was parsed was hostname, so reset pointers and let it parse */
4295 pl
->username_len
= pl
->password_len
= 0;
4296 work
= pl
->username
- 1;
4297 pl
->username
= pl
->password
= 0;
4299 else goto ErrorExit
;
4301 /* now start parsing hostname or hostnumber */
4303 pl
->hostname
= work
;
4304 work
= scan_url(pl
->hostname
, &pl
->hostname_len
, HOST
);
4310 work
= scan_url(pl
->port
, &pl
->port_len
, PORT
);
4314 /* see if query string */
4315 pl
->query
= wcschr(work
, '?');
4316 if (pl
->query
) pl
->query_len
= lstrlenW(pl
->query
);
4319 TRACE("parse successful: scheme=%p(%d), user=%p(%d), pass=%p(%d), host=%p(%d), port=%p(%d), query=%p(%d)\n",
4320 pl
->scheme
, pl
->scheme_len
, pl
->username
, pl
->username_len
, pl
->password
, pl
->password_len
, pl
->hostname
,
4321 pl
->hostname_len
, pl
->port
, pl
->port_len
, pl
->query
, pl
->query_len
);
4326 FIXME("failed to parse %s\n", debugstr_w(url
));
4327 return E_INVALIDARG
;
4330 HRESULT WINAPI
UrlGetPartW(const WCHAR
*url
, WCHAR
*out
, DWORD
*out_len
, DWORD part
, DWORD flags
)
4332 DWORD scheme
, size
, schsize
;
4333 LPCWSTR addr
, schaddr
;
4334 struct parsed_url pl
;
4337 TRACE("%s, %p, %p(%d), %#x, %#x\n", wine_dbgstr_w(url
), out
, out_len
, *out_len
, part
, flags
);
4339 if (!url
|| !out
|| !out_len
|| !*out_len
)
4340 return E_INVALIDARG
;
4344 addr
= wcschr(url
, ':');
4346 scheme
= URL_SCHEME_UNKNOWN
;
4348 scheme
= get_scheme_code(url
, addr
- url
);
4350 hr
= parse_url(url
, &pl
);
4354 case URL_PART_SCHEME
:
4361 size
= pl
.scheme_len
;
4364 case URL_PART_HOSTNAME
:
4367 case URL_SCHEME_FTP
:
4368 case URL_SCHEME_HTTP
:
4369 case URL_SCHEME_GOPHER
:
4370 case URL_SCHEME_TELNET
:
4371 case URL_SCHEME_FILE
:
4372 case URL_SCHEME_HTTPS
:
4379 if (scheme
== URL_SCHEME_FILE
&& (!pl
.hostname_len
|| (pl
.hostname_len
== 1 && *(pl
.hostname
+ 1) == ':')))
4385 if (!pl
.hostname_len
)
4391 size
= pl
.hostname_len
;
4394 case URL_PART_USERNAME
:
4395 if (!pl
.username_len
)
4401 size
= pl
.username_len
;
4404 case URL_PART_PASSWORD
:
4405 if (!pl
.password_len
)
4411 size
= pl
.password_len
;
4424 case URL_PART_QUERY
:
4431 size
= pl
.query_len
;
4436 return E_INVALIDARG
;
4439 if (flags
== URL_PARTFLAG_KEEPSCHEME
)
4441 if (!pl
.scheme
|| !pl
.scheme_len
)
4446 schaddr
= pl
.scheme
;
4447 schsize
= pl
.scheme_len
;
4448 if (*out_len
< schsize
+ size
+ 2)
4450 *out_len
= schsize
+ size
+ 2;
4453 memcpy(out
, schaddr
, schsize
*sizeof(WCHAR
));
4455 memcpy(out
+ schsize
+1, addr
, size
*sizeof(WCHAR
));
4456 out
[schsize
+1+size
] = 0;
4457 *out_len
= schsize
+ 1 + size
;
4461 if (*out_len
< size
+ 1)
4463 *out_len
= size
+ 1;
4466 memcpy(out
, addr
, size
*sizeof(WCHAR
));
4470 TRACE("len=%d %s\n", *out_len
, wine_dbgstr_w(out
));
4475 BOOL WINAPI
UrlIsA(const char *url
, URLIS Urlis
)
4480 TRACE("%s, %d\n", debugstr_a(url
), Urlis
);
4488 base
.cbSize
= sizeof(base
);
4489 if (ParseURLA(url
, &base
) != S_OK
) return FALSE
; /* invalid scheme */
4490 switch (base
.nScheme
)
4492 case URL_SCHEME_MAILTO
:
4493 case URL_SCHEME_SHELL
:
4494 case URL_SCHEME_JAVASCRIPT
:
4495 case URL_SCHEME_VBSCRIPT
:
4496 case URL_SCHEME_ABOUT
:
4502 return (CompareStringA(LOCALE_INVARIANT
, NORM_IGNORECASE
, url
, 5, "file:", 5) == CSTR_EQUAL
);
4504 case URLIS_DIRECTORY
:
4505 last
= url
+ strlen(url
) - 1;
4506 return (last
>= url
&& (*last
== '/' || *last
== '\\' ));
4509 return PathIsURLA(url
);
4511 case URLIS_NOHISTORY
:
4512 case URLIS_APPLIABLE
:
4513 case URLIS_HASQUERY
:
4515 FIXME("(%s %d): stub\n", debugstr_a(url
), Urlis
);
4521 BOOL WINAPI
UrlIsW(const WCHAR
*url
, URLIS Urlis
)
4526 TRACE("%s, %d\n", debugstr_w(url
), Urlis
);
4534 base
.cbSize
= sizeof(base
);
4535 if (ParseURLW(url
, &base
) != S_OK
) return FALSE
; /* invalid scheme */
4536 switch (base
.nScheme
)
4538 case URL_SCHEME_MAILTO
:
4539 case URL_SCHEME_SHELL
:
4540 case URL_SCHEME_JAVASCRIPT
:
4541 case URL_SCHEME_VBSCRIPT
:
4542 case URL_SCHEME_ABOUT
:
4548 return !wcsnicmp( url
, L
"file:", 5 );
4550 case URLIS_DIRECTORY
:
4551 last
= url
+ lstrlenW(url
) - 1;
4552 return (last
>= url
&& (*last
== '/' || *last
== '\\'));
4555 return PathIsURLW(url
);
4557 case URLIS_NOHISTORY
:
4558 case URLIS_APPLIABLE
:
4559 case URLIS_HASQUERY
:
4561 FIXME("(%s %d): stub\n", debugstr_w(url
), Urlis
);
4567 BOOL WINAPI
UrlIsOpaqueA(const char *url
)
4569 return UrlIsA(url
, URLIS_OPAQUE
);
4572 BOOL WINAPI
UrlIsOpaqueW(const WCHAR
*url
)
4574 return UrlIsW(url
, URLIS_OPAQUE
);
4577 BOOL WINAPI
UrlIsNoHistoryA(const char *url
)
4579 return UrlIsA(url
, URLIS_NOHISTORY
);
4582 BOOL WINAPI
UrlIsNoHistoryW(const WCHAR
*url
)
4584 return UrlIsW(url
, URLIS_NOHISTORY
);
4587 HRESULT WINAPI
UrlCreateFromPathA(const char *path
, char *url
, DWORD
*url_len
, DWORD reserved
)
4589 WCHAR bufW
[INTERNET_MAX_URL_LENGTH
];
4590 DWORD lenW
= ARRAY_SIZE(bufW
), lenA
;
4591 UNICODE_STRING pathW
;
4595 if (!RtlCreateUnicodeStringFromAsciiz(&pathW
, path
))
4596 return E_INVALIDARG
;
4598 if ((hr
= UrlCreateFromPathW(pathW
.Buffer
, urlW
, &lenW
, reserved
)) == E_POINTER
)
4600 urlW
= heap_alloc(lenW
* sizeof(WCHAR
));
4601 hr
= UrlCreateFromPathW(pathW
.Buffer
, urlW
, &lenW
, reserved
);
4606 RtlUnicodeToMultiByteSize(&lenA
, urlW
, lenW
* sizeof(WCHAR
));
4607 if (*url_len
> lenA
)
4609 RtlUnicodeToMultiByteN(url
, *url_len
- 1, &lenA
, urlW
, lenW
* sizeof(WCHAR
));
4615 *url_len
= lenA
+ 1;
4621 RtlFreeUnicodeString(&pathW
);
4625 HRESULT WINAPI
UrlCreateFromPathW(const WCHAR
*path
, WCHAR
*url
, DWORD
*url_len
, DWORD reserved
)
4629 TRACE("%s, %p, %p, %#x\n", debugstr_w(path
), url
, url_len
, reserved
);
4631 if (reserved
|| !url
|| !url_len
)
4632 return E_INVALIDARG
;
4634 hr
= url_create_from_path(path
, url
, url_len
);
4636 lstrcpyW(url
, path
);
4641 HRESULT WINAPI
UrlCombineA(const char *base
, const char *relative
, char *combined
, DWORD
*combined_len
, DWORD flags
)
4643 WCHAR
*baseW
, *relativeW
, *combinedW
;
4647 TRACE("%s, %s, %d, %#x\n", debugstr_a(base
), debugstr_a(relative
), combined_len
? *combined_len
: 0, flags
);
4649 if (!base
|| !relative
|| !combined_len
)
4650 return E_INVALIDARG
;
4652 baseW
= heap_alloc(3 * INTERNET_MAX_URL_LENGTH
* sizeof(WCHAR
));
4653 relativeW
= baseW
+ INTERNET_MAX_URL_LENGTH
;
4654 combinedW
= relativeW
+ INTERNET_MAX_URL_LENGTH
;
4656 MultiByteToWideChar(CP_ACP
, 0, base
, -1, baseW
, INTERNET_MAX_URL_LENGTH
);
4657 MultiByteToWideChar(CP_ACP
, 0, relative
, -1, relativeW
, INTERNET_MAX_URL_LENGTH
);
4658 len
= *combined_len
;
4660 hr
= UrlCombineW(baseW
, relativeW
, combined
? combinedW
: NULL
, &len
, flags
);
4663 *combined_len
= len
;
4668 len2
= WideCharToMultiByte(CP_ACP
, 0, combinedW
, len
, NULL
, 0, NULL
, NULL
);
4669 if (len2
> *combined_len
)
4671 *combined_len
= len2
;
4675 WideCharToMultiByte(CP_ACP
, 0, combinedW
, len
+1, combined
, *combined_len
+ 1, NULL
, NULL
);
4676 *combined_len
= len2
;
4681 HRESULT WINAPI
UrlCombineW(const WCHAR
*baseW
, const WCHAR
*relativeW
, WCHAR
*combined
, DWORD
*combined_len
, DWORD flags
)
4683 DWORD i
, len
, process_case
= 0, myflags
, sizeloc
= 0;
4684 LPWSTR work
, preliminary
, mbase
, mrelative
;
4685 PARSEDURLW base
, relative
;
4688 TRACE("%s, %s, %d, %#x\n", debugstr_w(baseW
), debugstr_w(relativeW
), combined_len
? *combined_len
: 0, flags
);
4690 if (!baseW
|| !relativeW
|| !combined_len
)
4691 return E_INVALIDARG
;
4693 base
.cbSize
= sizeof(base
);
4694 relative
.cbSize
= sizeof(relative
);
4696 /* Get space for duplicates of the input and the output */
4697 preliminary
= heap_alloc(3 * INTERNET_MAX_URL_LENGTH
* sizeof(WCHAR
));
4698 mbase
= preliminary
+ INTERNET_MAX_URL_LENGTH
;
4699 mrelative
= mbase
+ INTERNET_MAX_URL_LENGTH
;
4700 *preliminary
= '\0';
4702 /* Canonicalize the base input prior to looking for the scheme */
4703 myflags
= flags
& (URL_DONT_SIMPLIFY
| URL_UNESCAPE
);
4704 len
= INTERNET_MAX_URL_LENGTH
;
4705 UrlCanonicalizeW(baseW
, mbase
, &len
, myflags
);
4707 /* Canonicalize the relative input prior to looking for the scheme */
4708 len
= INTERNET_MAX_URL_LENGTH
;
4709 UrlCanonicalizeW(relativeW
, mrelative
, &len
, myflags
);
4711 /* See if the base has a scheme */
4712 if (ParseURLW(mbase
, &base
) != S_OK
)
4714 /* If base has no scheme return relative. */
4715 TRACE("no scheme detected in Base\n");
4720 BOOL manual_search
= FALSE
;
4722 work
= (LPWSTR
)base
.pszProtocol
;
4723 for (i
= 0; i
< base
.cchProtocol
; ++i
)
4724 work
[i
] = RtlDowncaseUnicodeChar(work
[i
]);
4726 /* mk is a special case */
4727 if (base
.nScheme
== URL_SCHEME_MK
)
4729 WCHAR
*ptr
= wcsstr(base
.pszSuffix
, L
"::");
4735 delta
= ptr
-base
.pszSuffix
;
4736 base
.cchProtocol
+= delta
;
4737 base
.pszSuffix
+= delta
;
4738 base
.cchSuffix
-= delta
;
4743 /* get size of location field (if it exists) */
4744 work
= (LPWSTR
)base
.pszSuffix
;
4750 /* At this point have start of location and
4751 * it ends at next '/' or end of string.
4753 while (*work
&& (*work
!= '/')) work
++;
4754 sizeloc
= (DWORD
)(work
- base
.pszSuffix
);
4759 /* If there is a '?', then the remaining part can only contain a
4760 * query string or fragment, so start looking for the last leaf
4761 * from the '?'. Otherwise, if there is a '#' and the characters
4762 * immediately preceding it are ".htm[l]", then begin looking for
4763 * the last leaf starting from the '#'. Otherwise the '#' is not
4764 * meaningful and just start looking from the end. */
4765 if ((work
= wcspbrk(base
.pszSuffix
+ sizeloc
, L
"#?")))
4767 if (*work
== '?' || base
.nScheme
== URL_SCHEME_HTTP
|| base
.nScheme
== URL_SCHEME_HTTPS
)
4768 manual_search
= TRUE
;
4769 else if (work
- base
.pszSuffix
> 4)
4771 if (!wcsnicmp(work
- 4, L
".htm", 4)) manual_search
= TRUE
;
4774 if (!manual_search
&& work
- base
.pszSuffix
> 5)
4776 if (!wcsnicmp(work
- 5, L
".html", 5)) manual_search
= TRUE
;
4782 /* search backwards starting from the current position */
4783 while (*work
!= '/' && work
> base
.pszSuffix
+ sizeloc
)
4785 base
.cchSuffix
= work
- base
.pszSuffix
+ 1;
4789 /* search backwards starting from the end of the string */
4790 work
= wcsrchr((base
.pszSuffix
+sizeloc
), '/');
4793 len
= (DWORD
)(work
- base
.pszSuffix
+ 1);
4794 base
.cchSuffix
= len
;
4797 base
.cchSuffix
= sizeloc
;
4802 * .pszSuffix points to location (starting with '//')
4803 * .cchSuffix length of location (above) and rest less the last
4805 * sizeloc length of location (above) up to but not including
4809 if (ParseURLW(mrelative
, &relative
) != S_OK
)
4811 /* No scheme in relative */
4812 TRACE("no scheme detected in Relative\n");
4813 relative
.pszSuffix
= mrelative
; /* case 3,4,5 depends on this */
4814 relative
.cchSuffix
= lstrlenW(mrelative
);
4815 if (*relativeW
== ':')
4817 /* Case that is either left alone or uses base. */
4818 if (flags
& URL_PLUGGABLE_PROTOCOL
)
4826 if (is_drive_spec( mrelative
))
4828 /* case that becomes "file:///" */
4829 lstrcpyW(preliminary
, L
"file:///");
4833 if (*mrelative
== '/' && *(mrelative
+1) == '/')
4835 /* Relative has location and the rest. */
4839 if (*mrelative
== '/')
4841 /* Relative is root to location. */
4845 if (*mrelative
== '#')
4847 if (!(work
= wcschr(base
.pszSuffix
+base
.cchSuffix
, '#')))
4848 work
= (LPWSTR
)base
.pszSuffix
+ lstrlenW(base
.pszSuffix
);
4850 memcpy(preliminary
, base
.pszProtocol
, (work
-base
.pszProtocol
)*sizeof(WCHAR
));
4851 preliminary
[work
-base
.pszProtocol
] = '\0';
4855 process_case
= (*base
.pszSuffix
== '/' || base
.nScheme
== URL_SCHEME_MK
) ? 5 : 3;
4860 work
= (LPWSTR
)relative
.pszProtocol
;
4861 for (i
= 0; i
< relative
.cchProtocol
; ++i
)
4862 work
[i
] = RtlDowncaseUnicodeChar(work
[i
]);
4865 /* Handle cases where relative has scheme. */
4866 if ((base
.cchProtocol
== relative
.cchProtocol
) && !wcsncmp(base
.pszProtocol
, relative
.pszProtocol
, base
.cchProtocol
))
4868 /* since the schemes are the same */
4869 if (*relative
.pszSuffix
== '/' && *(relative
.pszSuffix
+1) == '/')
4871 /* Relative replaces location and what follows. */
4875 if (*relative
.pszSuffix
== '/')
4877 /* Relative is root to location */
4881 /* replace either just location if base's location starts with a
4882 * slash or otherwise everything */
4883 process_case
= (*base
.pszSuffix
== '/') ? 5 : 1;
4887 if (*relative
.pszSuffix
== '/' && *(relative
.pszSuffix
+1) == '/')
4889 /* Relative replaces scheme, location, and following and handles PLUGGABLE */
4895 } while (FALSE
); /* a little trick to allow easy exit from nested if's */
4898 switch (process_case
)
4901 /* Return relative appended to whatever is in combined (which may the string "file:///" */
4902 lstrcatW(preliminary
, mrelative
);
4906 /* Relative replaces scheme and location */
4907 lstrcpyW(preliminary
, mrelative
);
4911 /* Return the base scheme with relative. Basically keeps the scheme and replaces the domain and following. */
4912 memcpy(preliminary
, base
.pszProtocol
, (base
.cchProtocol
+ 1)*sizeof(WCHAR
));
4913 work
= preliminary
+ base
.cchProtocol
+ 1;
4914 lstrcpyW(work
, relative
.pszSuffix
);
4918 /* Return the base scheme and location but everything after the location is relative. (Replace document from root on.) */
4919 memcpy(preliminary
, base
.pszProtocol
, (base
.cchProtocol
+1+sizeloc
)*sizeof(WCHAR
));
4920 work
= preliminary
+ base
.cchProtocol
+ 1 + sizeloc
;
4921 if (flags
& URL_PLUGGABLE_PROTOCOL
)
4923 lstrcpyW(work
, relative
.pszSuffix
);
4927 /* Return the base without its document (if any) and append relative after its scheme. */
4928 memcpy(preliminary
, base
.pszProtocol
, (base
.cchProtocol
+ 1 + base
.cchSuffix
)*sizeof(WCHAR
));
4929 work
= preliminary
+ base
.cchProtocol
+ 1 + base
.cchSuffix
- 1;
4932 lstrcpyW(work
, relative
.pszSuffix
);
4936 FIXME("Unexpected case %d.\n", process_case
);
4942 /* Reuse mrelative as temp storage as it's already allocated and not needed anymore */
4943 if (*combined_len
== 0)
4945 hr
= UrlCanonicalizeW(preliminary
, mrelative
, combined_len
, flags
& ~URL_FILE_USE_PATHURL
);
4946 if (SUCCEEDED(hr
) && combined
)
4947 lstrcpyW(combined
, mrelative
);
4949 TRACE("return-%d len=%d, %s\n", process_case
, *combined_len
, debugstr_w(combined
));
4952 heap_free(preliminary
);
4956 HRESULT WINAPI
HashData(const unsigned char *src
, DWORD src_len
, unsigned char *dest
, DWORD dest_len
)
4958 INT src_count
= src_len
- 1, dest_count
= dest_len
- 1;
4961 return E_INVALIDARG
;
4963 while (dest_count
>= 0)
4965 dest
[dest_count
] = (dest_count
& 0xff);
4969 while (src_count
>= 0)
4971 dest_count
= dest_len
- 1;
4972 while (dest_count
>= 0)
4974 dest
[dest_count
] = hashdata_lookup
[src
[src_count
] ^ dest
[dest_count
]];
4983 HRESULT WINAPI
UrlHashA(const char *url
, unsigned char *dest
, DWORD dest_len
)
4987 HashData((const BYTE
*)url
, (int)strlen(url
), dest
, dest_len
);
4991 return E_INVALIDARG
;
4997 HRESULT WINAPI
UrlHashW(const WCHAR
*url
, unsigned char *dest
, DWORD dest_len
)
4999 char urlA
[MAX_PATH
];
5001 TRACE("%s, %p, %d\n", debugstr_w(url
), dest
, dest_len
);
5005 WideCharToMultiByte(CP_ACP
, 0, url
, -1, urlA
, MAX_PATH
, NULL
, NULL
);
5006 HashData((const BYTE
*)urlA
, (int)strlen(urlA
), dest
, dest_len
);
5010 return E_INVALIDARG
;
5016 BOOL WINAPI
IsInternetESCEnabled(void)