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 static const char hexDigits
[] = "0123456789ABCDEF";
41 static const unsigned char hashdata_lookup
[256] =
43 0x01, 0x0e, 0x6e, 0x19, 0x61, 0xae, 0x84, 0x77, 0x8a, 0xaa, 0x7d, 0x76, 0x1b, 0xe9, 0x8c, 0x33,
44 0x57, 0xc5, 0xb1, 0x6b, 0xea, 0xa9, 0x38, 0x44, 0x1e, 0x07, 0xad, 0x49, 0xbc, 0x28, 0x24, 0x41,
45 0x31, 0xd5, 0x68, 0xbe, 0x39, 0xd3, 0x94, 0xdf, 0x30, 0x73, 0x0f, 0x02, 0x43, 0xba, 0xd2, 0x1c,
46 0x0c, 0xb5, 0x67, 0x46, 0x16, 0x3a, 0x4b, 0x4e, 0xb7, 0xa7, 0xee, 0x9d, 0x7c, 0x93, 0xac, 0x90,
47 0xb0, 0xa1, 0x8d, 0x56, 0x3c, 0x42, 0x80, 0x53, 0x9c, 0xf1, 0x4f, 0x2e, 0xa8, 0xc6, 0x29, 0xfe,
48 0xb2, 0x55, 0xfd, 0xed, 0xfa, 0x9a, 0x85, 0x58, 0x23, 0xce, 0x5f, 0x74, 0xfc, 0xc0, 0x36, 0xdd,
49 0x66, 0xda, 0xff, 0xf0, 0x52, 0x6a, 0x9e, 0xc9, 0x3d, 0x03, 0x59, 0x09, 0x2a, 0x9b, 0x9f, 0x5d,
50 0xa6, 0x50, 0x32, 0x22, 0xaf, 0xc3, 0x64, 0x63, 0x1a, 0x96, 0x10, 0x91, 0x04, 0x21, 0x08, 0xbd,
51 0x79, 0x40, 0x4d, 0x48, 0xd0, 0xf5, 0x82, 0x7a, 0x8f, 0x37, 0x69, 0x86, 0x1d, 0xa4, 0xb9, 0xc2,
52 0xc1, 0xef, 0x65, 0xf2, 0x05, 0xab, 0x7e, 0x0b, 0x4a, 0x3b, 0x89, 0xe4, 0x6c, 0xbf, 0xe8, 0x8b,
53 0x06, 0x18, 0x51, 0x14, 0x7f, 0x11, 0x5b, 0x5c, 0xfb, 0x97, 0xe1, 0xcf, 0x15, 0x62, 0x71, 0x70,
54 0x54, 0xe2, 0x12, 0xd6, 0xc7, 0xbb, 0x0d, 0x20, 0x5e, 0xdc, 0xe0, 0xd4, 0xf7, 0xcc, 0xc4, 0x2b,
55 0xf9, 0xec, 0x2d, 0xf4, 0x6f, 0xb6, 0x99, 0x88, 0x81, 0x5a, 0xd9, 0xca, 0x13, 0xa5, 0xe7, 0x47,
56 0xe6, 0x8e, 0x60, 0xe3, 0x3e, 0xb3, 0xf6, 0x72, 0xa2, 0x35, 0xa0, 0xd7, 0xcd, 0xb4, 0x2f, 0x6d,
57 0x2c, 0x26, 0x1f, 0x95, 0x87, 0x00, 0xd8, 0x34, 0x3f, 0x17, 0x25, 0x45, 0x27, 0x75, 0x92, 0xb8,
58 0xa3, 0xc8, 0xde, 0xeb, 0xf8, 0xf3, 0xdb, 0x0a, 0x98, 0x83, 0x7b, 0xe5, 0xcb, 0x4c, 0x78, 0xd1,
63 const WCHAR
*scheme
; /* [out] start of scheme */
64 DWORD scheme_len
; /* [out] size of scheme (until colon) */
65 const WCHAR
*username
; /* [out] start of Username */
66 DWORD username_len
; /* [out] size of Username (until ":" or "@") */
67 const WCHAR
*password
; /* [out] start of Password */
68 DWORD password_len
; /* [out] size of Password (until "@") */
69 const WCHAR
*hostname
; /* [out] start of Hostname */
70 DWORD hostname_len
; /* [out] size of Hostname (until ":" or "/") */
71 const WCHAR
*port
; /* [out] start of Port */
72 DWORD port_len
; /* [out] size of Port (until "/" or eos) */
73 const WCHAR
*query
; /* [out] start of Query */
74 DWORD query_len
; /* [out] size of Query (until eos) */
78 static WCHAR
*heap_strdupAtoW(const char *str
)
86 len
= MultiByteToWideChar(CP_ACP
, 0, str
, -1, NULL
, 0);
87 ret
= heap_alloc(len
* sizeof(WCHAR
));
88 MultiByteToWideChar(CP_ACP
, 0, str
, -1, ret
, len
);
94 static BOOL
is_drive_spec( const WCHAR
*str
)
96 return isalpha( str
[0] ) && str
[1] == ':';
99 static BOOL
is_escaped_drive_spec( const WCHAR
*str
)
101 return isalpha( str
[0] ) && (str
[1] == ':' || str
[1] == '|');
104 static BOOL
is_prefixed_unc(const WCHAR
*string
)
106 return !wcsnicmp(string
, L
"\\\\?\\UNC\\", 8 );
109 static BOOL
is_prefixed_disk(const WCHAR
*string
)
111 return !wcsncmp(string
, L
"\\\\?\\", 4) && is_drive_spec( string
+ 4 );
114 static BOOL
is_prefixed_volume(const WCHAR
*string
)
119 if (wcsnicmp( string
, L
"\\\\?\\Volume", 10 )) return FALSE
;
128 if (guid
[i
] != '{') return FALSE
;
134 if (guid
[i
] != '-') return FALSE
;
137 if (guid
[i
] != '}') return FALSE
;
140 if (!isxdigit(guid
[i
])) return FALSE
;
149 /* Get the next character beyond end of the segment.
150 Return TRUE if the last segment ends with a backslash */
151 static BOOL
get_next_segment(const WCHAR
*next
, const WCHAR
**next_segment
)
153 while (*next
&& *next
!= '\\') next
++;
156 *next_segment
= next
+ 1;
161 *next_segment
= next
;
166 /* Find the last character of the root in a path, if there is one, without any segments */
167 static const WCHAR
*get_root_end(const WCHAR
*path
)
170 if (is_prefixed_volume(path
))
171 return path
[48] == '\\' ? path
+ 48 : path
+ 47;
172 else if (is_prefixed_unc(path
))
174 else if (is_prefixed_disk(path
))
175 return path
[6] == '\\' ? path
+ 6 : path
+ 5;
177 else if (path
[0] == '\\' && path
[1] == '\\')
180 else if (path
[0] == '\\')
183 else if (is_drive_spec( path
))
184 return path
[2] == '\\' ? path
+ 2 : path
+ 1;
189 HRESULT WINAPI
PathAllocCanonicalize(const WCHAR
*path_in
, DWORD flags
, WCHAR
**path_out
)
193 const WCHAR
*root_end
;
194 SIZE_T buffer_size
, length
;
196 TRACE("%s %#lx %p\n", debugstr_w(path_in
), flags
, path_out
);
198 if (!path_in
|| !path_out
199 || ((flags
& PATHCCH_FORCE_ENABLE_LONG_NAME_PROCESS
) && (flags
& PATHCCH_FORCE_DISABLE_LONG_NAME_PROCESS
))
200 || (flags
& (PATHCCH_FORCE_ENABLE_LONG_NAME_PROCESS
| PATHCCH_FORCE_DISABLE_LONG_NAME_PROCESS
)
201 && !(flags
& PATHCCH_ALLOW_LONG_PATHS
))
202 || ((flags
& PATHCCH_ENSURE_IS_EXTENDED_LENGTH_PATH
) && (flags
& PATHCCH_ALLOW_LONG_PATHS
)))
204 if (path_out
) *path_out
= NULL
;
208 length
= lstrlenW(path_in
);
209 if ((length
+ 1 > MAX_PATH
&& !(flags
& (PATHCCH_ALLOW_LONG_PATHS
| PATHCCH_ENSURE_IS_EXTENDED_LENGTH_PATH
)))
210 || (length
+ 1 > PATHCCH_MAX_CCH
))
213 return HRESULT_FROM_WIN32(ERROR_FILENAME_EXCED_RANGE
);
216 /* PATHCCH_ENSURE_IS_EXTENDED_LENGTH_PATH implies PATHCCH_DO_NOT_NORMALIZE_SEGMENTS */
217 if (flags
& PATHCCH_ENSURE_IS_EXTENDED_LENGTH_PATH
) flags
|= PATHCCH_DO_NOT_NORMALIZE_SEGMENTS
;
219 /* path length + possible \\?\ addition + possible \ addition + NUL */
220 buffer_size
= (length
+ 6) * sizeof(WCHAR
);
221 buffer
= LocalAlloc(LMEM_ZEROINIT
, buffer_size
);
225 return E_OUTOFMEMORY
;
231 root_end
= get_root_end(path_in
);
232 if (root_end
) root_end
= buffer
+ (root_end
- path_in
);
237 memcpy(dst
, src
, (root_end
- buffer
+ 1) * sizeof(WCHAR
));
238 src
+= root_end
- buffer
+ 1;
239 if(PathCchStripPrefix(dst
, length
+ 6) == S_OK
)
241 /* Fill in \ in X:\ if the \ is missing */
242 if (is_drive_spec( dst
) && dst
[2]!= '\\')
247 dst
= buffer
+ lstrlenW(buffer
);
251 dst
+= root_end
- buffer
+ 1;
260 /* Keep one . after * */
261 if (dst
> buffer
&& dst
[-1] == '*')
267 /* Keep the .. if not surrounded by \ */
268 if ((src
[2] != '\\' && src
[2]) || (dst
> buffer
&& dst
[-1] != '\\'))
275 /* Remove the \ before .. if the \ is not part of root */
276 if (dst
> buffer
&& dst
[-1] == '\\' && (!root_end
|| dst
- 1 > root_end
))
279 /* Remove characters until a \ is encountered */
291 /* Remove the extra \ after .. if the \ before .. wasn't deleted */
292 else if (src
[2] == '\\')
299 /* Keep the . if not surrounded by \ */
300 if ((src
[1] != '\\' && src
[1]) || (dst
> buffer
&& dst
[-1] != '\\'))
306 /* Remove the \ before . if the \ is not part of root */
307 if (dst
> buffer
&& dst
[-1] == '\\' && (!root_end
|| dst
- 1 > root_end
)) dst
--;
308 /* Remove the extra \ after . if the \ before . wasn't deleted */
309 else if (src
[1] == '\\')
315 /* If X:\ is not complete, then complete it */
316 if (is_drive_spec( buffer
) && buffer
[2] != '\\')
318 root_end
= buffer
+ 2;
321 /* If next character is \, use the \ to fill in */
322 if (src
[0] == '\\') src
++;
332 /* Strip multiple trailing . */
333 if (!(flags
& PATHCCH_DO_NOT_NORMALIZE_SEGMENTS
))
335 while (dst
> buffer
&& dst
[-1] == '.')
337 /* Keep a . after * */
338 if (dst
- 1 > buffer
&& dst
[-2] == '*')
340 /* If . follow a : at the second character, remove the . and add a \ */
341 else if (dst
- 1 > buffer
&& dst
[-2] == ':' && dst
- 2 == buffer
+ 1)
348 /* If result path is empty, fill in \ */
355 /* Extend the path if needed */
356 length
= lstrlenW(buffer
);
357 if (((length
+ 1 > MAX_PATH
&& is_drive_spec( buffer
))
358 || (is_drive_spec( buffer
) && flags
& PATHCCH_ENSURE_IS_EXTENDED_LENGTH_PATH
))
359 && !(flags
& PATHCCH_FORCE_ENABLE_LONG_NAME_PROCESS
))
361 memmove(buffer
+ 4, buffer
, (length
+ 1) * sizeof(WCHAR
));
368 /* Add a trailing backslash to the path if needed */
369 if (flags
& PATHCCH_ENSURE_TRAILING_SLASH
)
370 PathCchAddBackslash(buffer
, buffer_size
);
376 HRESULT WINAPI
PathAllocCombine(const WCHAR
*path1
, const WCHAR
*path2
, DWORD flags
, WCHAR
**out
)
378 SIZE_T combined_length
, length2
;
379 WCHAR
*combined_path
;
380 BOOL add_backslash
= FALSE
;
383 TRACE("%s %s %#lx %p\n", wine_dbgstr_w(path1
), wine_dbgstr_w(path2
), flags
, out
);
385 if ((!path1
&& !path2
) || !out
)
387 if (out
) *out
= NULL
;
391 if (!path1
|| !path2
) return PathAllocCanonicalize(path1
? path1
: path2
, flags
, out
);
393 /* If path2 is fully qualified, use path2 only */
394 if (is_drive_spec( path2
) || (path2
[0] == '\\' && path2
[1] == '\\'))
398 add_backslash
= (is_drive_spec(path1
) && !path1
[2])
399 || (is_prefixed_disk(path1
) && !path1
[6]);
402 length2
= path2
? lstrlenW(path2
) : 0;
403 /* path1 length + path2 length + possible backslash + NULL */
404 combined_length
= lstrlenW(path1
) + length2
+ 2;
406 combined_path
= HeapAlloc(GetProcessHeap(), 0, combined_length
* sizeof(WCHAR
));
410 return E_OUTOFMEMORY
;
413 lstrcpyW(combined_path
, path1
);
414 PathCchStripPrefix(combined_path
, combined_length
);
415 if (add_backslash
) PathCchAddBackslashEx(combined_path
, combined_length
, NULL
, NULL
);
417 if (path2
&& path2
[0])
419 if (path2
[0] == '\\' && path2
[1] != '\\')
421 PathCchStripToRoot(combined_path
, combined_length
);
425 PathCchAddBackslashEx(combined_path
, combined_length
, NULL
, NULL
);
426 lstrcatW(combined_path
, path2
);
429 hr
= PathAllocCanonicalize(combined_path
, flags
, out
);
430 HeapFree(GetProcessHeap(), 0, combined_path
);
434 HRESULT WINAPI
PathCchAddBackslash(WCHAR
*path
, SIZE_T size
)
436 return PathCchAddBackslashEx(path
, size
, NULL
, NULL
);
439 HRESULT WINAPI
PathCchAddBackslashEx(WCHAR
*path
, SIZE_T size
, WCHAR
**endptr
, SIZE_T
*remaining
)
441 BOOL needs_termination
;
444 TRACE("%s, %Iu, %p, %p\n", debugstr_w(path
), size
, endptr
, remaining
);
446 length
= lstrlenW(path
);
447 needs_termination
= size
&& length
&& path
[length
- 1] != '\\';
449 if (length
>= (needs_termination
? size
- 1 : size
))
451 if (endptr
) *endptr
= NULL
;
452 if (remaining
) *remaining
= 0;
453 return STRSAFE_E_INSUFFICIENT_BUFFER
;
456 if (!needs_termination
)
458 if (endptr
) *endptr
= path
+ length
;
459 if (remaining
) *remaining
= size
- length
;
463 path
[length
++] = '\\';
466 if (endptr
) *endptr
= path
+ length
;
467 if (remaining
) *remaining
= size
- length
;
472 HRESULT WINAPI
PathCchAddExtension(WCHAR
*path
, SIZE_T size
, const WCHAR
*extension
)
474 const WCHAR
*existing_extension
, *next
;
475 SIZE_T path_length
, extension_length
, dot_length
;
479 TRACE("%s %Iu %s\n", wine_dbgstr_w(path
), size
, wine_dbgstr_w(extension
));
481 if (!path
|| !size
|| size
> PATHCCH_MAX_CCH
|| !extension
) return E_INVALIDARG
;
486 if ((*next
== '.' && next
> extension
) || *next
== ' ' || *next
== '\\') return E_INVALIDARG
;
490 has_dot
= extension
[0] == '.';
492 hr
= PathCchFindExtension(path
, size
, &existing_extension
);
493 if (FAILED(hr
)) return hr
;
494 if (*existing_extension
) return S_FALSE
;
496 path_length
= wcsnlen(path
, size
);
497 dot_length
= has_dot
? 0 : 1;
498 extension_length
= lstrlenW(extension
);
500 if (path_length
+ dot_length
+ extension_length
+ 1 > size
) return STRSAFE_E_INSUFFICIENT_BUFFER
;
502 /* If extension is empty or only dot, return S_OK with path unchanged */
503 if (!extension
[0] || (extension
[0] == '.' && !extension
[1])) return S_OK
;
507 path
[path_length
] = '.';
511 lstrcpyW(path
+ path_length
, extension
);
515 HRESULT WINAPI
PathCchAppend(WCHAR
*path1
, SIZE_T size
, const WCHAR
*path2
)
517 TRACE("%s %Iu %s\n", wine_dbgstr_w(path1
), size
, wine_dbgstr_w(path2
));
519 return PathCchAppendEx(path1
, size
, path2
, PATHCCH_NONE
);
522 HRESULT WINAPI
PathCchAppendEx(WCHAR
*path1
, SIZE_T size
, const WCHAR
*path2
, DWORD flags
)
527 TRACE("%s %Iu %s %#lx\n", wine_dbgstr_w(path1
), size
, wine_dbgstr_w(path2
), flags
);
529 if (!path1
|| !size
) return E_INVALIDARG
;
531 /* Create a temporary buffer for result because we need to keep path1 unchanged if error occurs.
532 * And PathCchCombineEx writes empty result if there is error so we can't just use path1 as output
533 * buffer for PathCchCombineEx */
534 result
= HeapAlloc(GetProcessHeap(), 0, size
* sizeof(WCHAR
));
535 if (!result
) return E_OUTOFMEMORY
;
537 /* Avoid the single backslash behavior with PathCchCombineEx when appending */
538 if (path2
&& path2
[0] == '\\' && path2
[1] != '\\') path2
++;
540 hr
= PathCchCombineEx(result
, size
, path1
, path2
, flags
);
541 if (SUCCEEDED(hr
)) memcpy(path1
, result
, size
* sizeof(WCHAR
));
543 HeapFree(GetProcessHeap(), 0, result
);
547 HRESULT WINAPI
PathCchCanonicalize(WCHAR
*out
, SIZE_T size
, const WCHAR
*in
)
549 TRACE("%p %Iu %s\n", out
, size
, wine_dbgstr_w(in
));
551 /* Not X:\ and path > MAX_PATH - 4, return HRESULT_FROM_WIN32(ERROR_FILENAME_EXCED_RANGE) */
552 if (lstrlenW(in
) > MAX_PATH
- 4 && !(is_drive_spec( in
) && in
[2] == '\\'))
553 return HRESULT_FROM_WIN32(ERROR_FILENAME_EXCED_RANGE
);
555 return PathCchCanonicalizeEx(out
, size
, in
, PATHCCH_NONE
);
558 HRESULT WINAPI
PathCchCanonicalizeEx(WCHAR
*out
, SIZE_T size
, const WCHAR
*in
, DWORD flags
)
564 TRACE("%p %Iu %s %#lx\n", out
, size
, wine_dbgstr_w(in
), flags
);
566 if (!size
) return E_INVALIDARG
;
568 hr
= PathAllocCanonicalize(in
, flags
, &buffer
);
569 if (FAILED(hr
)) return hr
;
571 length
= lstrlenW(buffer
);
572 if (size
< length
+ 1)
574 /* No root and path > MAX_PATH - 4, return HRESULT_FROM_WIN32(ERROR_FILENAME_EXCED_RANGE) */
575 if (length
> MAX_PATH
- 4 && !(in
[0] == '\\' || (is_drive_spec( in
) && in
[2] == '\\')))
576 hr
= HRESULT_FROM_WIN32(ERROR_FILENAME_EXCED_RANGE
);
578 hr
= STRSAFE_E_INSUFFICIENT_BUFFER
;
583 memcpy(out
, buffer
, (length
+ 1) * sizeof(WCHAR
));
585 /* Fill a backslash at the end of X: */
586 if (is_drive_spec( out
) && !out
[2] && size
> 3)
597 HRESULT WINAPI
PathCchCombine(WCHAR
*out
, SIZE_T size
, const WCHAR
*path1
, const WCHAR
*path2
)
599 TRACE("%p %s %s\n", out
, wine_dbgstr_w(path1
), wine_dbgstr_w(path2
));
601 return PathCchCombineEx(out
, size
, path1
, path2
, PATHCCH_NONE
);
604 HRESULT WINAPI
PathCchCombineEx(WCHAR
*out
, SIZE_T size
, const WCHAR
*path1
, const WCHAR
*path2
, DWORD flags
)
610 TRACE("%p %s %s %#lx\n", out
, wine_dbgstr_w(path1
), wine_dbgstr_w(path2
), flags
);
612 if (!out
|| !size
|| size
> PATHCCH_MAX_CCH
) return E_INVALIDARG
;
614 hr
= PathAllocCombine(path1
, path2
, flags
, &buffer
);
621 length
= lstrlenW(buffer
);
622 if (length
+ 1 > size
)
626 return STRSAFE_E_INSUFFICIENT_BUFFER
;
630 memcpy(out
, buffer
, (length
+ 1) * sizeof(WCHAR
));
636 HRESULT WINAPI
PathCchFindExtension(const WCHAR
*path
, SIZE_T size
, const WCHAR
**extension
)
638 const WCHAR
*lastpoint
= NULL
;
641 TRACE("%s %Iu %p\n", wine_dbgstr_w(path
), size
, extension
);
643 if (!path
|| !size
|| size
> PATHCCH_MAX_CCH
)
651 if (*path
== '\\' || *path
== ' ')
653 else if (*path
== '.')
658 if (counter
== size
|| counter
== PATHCCH_MAX_CCH
)
665 *extension
= lastpoint
? lastpoint
: path
;
669 BOOL WINAPI
PathCchIsRoot(const WCHAR
*path
)
671 const WCHAR
*root_end
;
675 TRACE("%s\n", wine_dbgstr_w(path
));
677 if (!path
|| !*path
) return FALSE
;
679 root_end
= get_root_end(path
);
680 if (!root_end
) return FALSE
;
682 if ((is_unc
= is_prefixed_unc(path
)) || (path
[0] == '\\' && path
[1] == '\\' && path
[2] != '?'))
685 /* No extra segments */
686 if ((is_unc
&& !*next
) || (!is_unc
&& !*next
)) return TRUE
;
688 /* Has first segment with an ending backslash but no remaining characters */
689 if (get_next_segment(next
, &next
) && !*next
) return FALSE
;
690 /* Has first segment with no ending backslash */
693 /* Has first segment with an ending backslash and has remaining characters*/
697 /* Second segment must have no backslash and no remaining characters */
698 return !get_next_segment(next
, &next
) && !*next
;
701 else if (*root_end
== '\\' && !root_end
[1])
707 HRESULT WINAPI
PathCchRemoveBackslash(WCHAR
*path
, SIZE_T path_size
)
712 TRACE("%s %Iu\n", debugstr_w(path
), path_size
);
714 return PathCchRemoveBackslashEx(path
, path_size
, &path_end
, &free_size
);
717 HRESULT WINAPI
PathCchRemoveBackslashEx(WCHAR
*path
, SIZE_T path_size
, WCHAR
**path_end
, SIZE_T
*free_size
)
719 const WCHAR
*root_end
;
722 TRACE("%s %Iu %p %p\n", debugstr_w(path
), path_size
, path_end
, free_size
);
724 if (!path_size
|| !path_end
|| !free_size
)
726 if (path_end
) *path_end
= NULL
;
727 if (free_size
) *free_size
= 0;
731 path_length
= wcsnlen(path
, path_size
);
732 if (path_length
== path_size
&& !path
[path_length
]) return E_INVALIDARG
;
734 root_end
= get_root_end(path
);
735 if (path_length
> 0 && path
[path_length
- 1] == '\\')
737 *path_end
= path
+ path_length
- 1;
738 *free_size
= path_size
- path_length
+ 1;
739 /* If the last character is beyond end of root */
740 if (!root_end
|| path
+ path_length
- 1 > root_end
)
742 path
[path_length
- 1] = 0;
750 *path_end
= path
+ path_length
;
751 *free_size
= path_size
- path_length
;
756 HRESULT WINAPI
PathCchRemoveExtension(WCHAR
*path
, SIZE_T size
)
758 const WCHAR
*extension
;
762 TRACE("%s %Iu\n", wine_dbgstr_w(path
), size
);
764 if (!path
|| !size
|| size
> PATHCCH_MAX_CCH
) return E_INVALIDARG
;
766 hr
= PathCchFindExtension(path
, size
, &extension
);
767 if (FAILED(hr
)) return hr
;
769 next
= path
+ (extension
- path
);
770 while (next
- path
< size
&& *next
) *next
++ = 0;
772 return next
== extension
? S_FALSE
: S_OK
;
775 HRESULT WINAPI
PathCchRemoveFileSpec(WCHAR
*path
, SIZE_T size
)
777 const WCHAR
*root_end
= NULL
;
781 TRACE("%s %Iu\n", wine_dbgstr_w(path
), size
);
783 if (!path
|| !size
|| size
> PATHCCH_MAX_CCH
) return E_INVALIDARG
;
785 if (PathCchIsRoot(path
)) return S_FALSE
;
787 PathCchSkipRoot(path
, &root_end
);
789 /* The backslash at the end of UNC and \\* are not considered part of root in this case */
790 if (root_end
&& root_end
> path
&& root_end
[-1] == '\\'
791 && (is_prefixed_unc(path
) || (path
[0] == '\\' && path
[1] == '\\' && path
[2] != '?')))
794 length
= lstrlenW(path
);
795 last
= path
+ length
- 1;
796 while (last
>= path
&& (!root_end
|| last
>= root_end
))
798 if (last
- path
>= size
) return E_INVALIDARG
;
809 return last
!= path
+ length
- 1 ? S_OK
: S_FALSE
;
812 HRESULT WINAPI
PathCchRenameExtension(WCHAR
*path
, SIZE_T size
, const WCHAR
*extension
)
816 TRACE("%s %Iu %s\n", wine_dbgstr_w(path
), size
, wine_dbgstr_w(extension
));
818 hr
= PathCchRemoveExtension(path
, size
);
819 if (FAILED(hr
)) return hr
;
821 hr
= PathCchAddExtension(path
, size
, extension
);
822 return FAILED(hr
) ? hr
: S_OK
;
825 HRESULT WINAPI
PathCchSkipRoot(const WCHAR
*path
, const WCHAR
**root_end
)
827 TRACE("%s %p\n", debugstr_w(path
), root_end
);
829 if (!path
|| !path
[0] || !root_end
830 || (!wcsnicmp(path
, L
"\\\\?", 3) && !is_prefixed_volume(path
) && !is_prefixed_unc(path
)
831 && !is_prefixed_disk(path
)))
834 *root_end
= get_root_end(path
);
838 if (is_prefixed_unc(path
))
840 get_next_segment(*root_end
, root_end
);
841 get_next_segment(*root_end
, root_end
);
843 else if (path
[0] == '\\' && path
[1] == '\\' && path
[2] != '?')
845 /* Skip share server */
846 get_next_segment(*root_end
, root_end
);
847 /* If mount point is empty, don't skip over mount point */
848 if (**root_end
!= '\\') get_next_segment(*root_end
, root_end
);
852 return *root_end
? S_OK
: E_INVALIDARG
;
855 HRESULT WINAPI
PathCchStripPrefix(WCHAR
*path
, SIZE_T size
)
857 TRACE("%s %Iu\n", wine_dbgstr_w(path
), size
);
859 if (!path
|| !size
|| size
> PATHCCH_MAX_CCH
) return E_INVALIDARG
;
861 if (is_prefixed_unc(path
))
863 /* \\?\UNC\a -> \\a */
864 if (size
< lstrlenW(path
+ 8) + 3) return E_INVALIDARG
;
865 lstrcpyW(path
+ 2, path
+ 8);
868 else if (is_prefixed_disk(path
))
871 if (size
< lstrlenW(path
+ 4) + 1) return E_INVALIDARG
;
872 lstrcpyW(path
, path
+ 4);
879 HRESULT WINAPI
PathCchStripToRoot(WCHAR
*path
, SIZE_T size
)
881 const WCHAR
*root_end
;
885 TRACE("%s %Iu\n", wine_dbgstr_w(path
), size
);
887 if (!path
|| !*path
|| !size
|| size
> PATHCCH_MAX_CCH
) return E_INVALIDARG
;
889 /* \\\\?\\UNC\\* and \\\\* have to have at least two extra segments to be striped,
890 * e.g. \\\\?\\UNC\\a\\b\\c -> \\\\?\\UNC\\a\\b
891 * \\\\a\\b\\c -> \\\\a\\b */
892 if ((is_unc
= is_prefixed_unc(path
)) || (path
[0] == '\\' && path
[1] == '\\' && path
[2] != '?'))
894 root_end
= is_unc
? path
+ 8 : path
+ 3;
895 if (!get_next_segment(root_end
, &root_end
)) return S_FALSE
;
896 if (!get_next_segment(root_end
, &root_end
)) return S_FALSE
;
898 if (root_end
- path
>= size
) return E_INVALIDARG
;
900 segment_end
= path
+ (root_end
- path
) - 1;
904 else if (PathCchSkipRoot(path
, &root_end
) == S_OK
)
906 if (root_end
- path
>= size
) return E_INVALIDARG
;
908 segment_end
= path
+ (root_end
- path
);
909 if (!*segment_end
) return S_FALSE
;
918 BOOL WINAPI
PathIsUNCEx(const WCHAR
*path
, const WCHAR
**server
)
920 const WCHAR
*result
= NULL
;
922 TRACE("%s %p\n", wine_dbgstr_w(path
), server
);
924 if (is_prefixed_unc(path
))
926 else if (path
[0] == '\\' && path
[1] == '\\' && path
[2] != '?')
929 if (server
) *server
= result
;
933 BOOL WINAPI
PathIsUNCA(const char *path
)
935 TRACE("%s\n", wine_dbgstr_a(path
));
937 return path
&& (path
[0] == '\\') && (path
[1] == '\\');
940 BOOL WINAPI
PathIsUNCW(const WCHAR
*path
)
942 TRACE("%s\n", wine_dbgstr_w(path
));
944 return path
&& (path
[0] == '\\') && (path
[1] == '\\');
947 BOOL WINAPI
PathIsRelativeA(const char *path
)
949 TRACE("%s\n", wine_dbgstr_a(path
));
951 if (!path
|| !*path
|| IsDBCSLeadByte(*path
))
954 return !(*path
== '\\' || (*path
&& path
[1] == ':'));
957 BOOL WINAPI
PathIsRelativeW(const WCHAR
*path
)
959 TRACE("%s\n", wine_dbgstr_w(path
));
964 return !(*path
== '\\' || (*path
&& path
[1] == ':'));
967 BOOL WINAPI
PathIsUNCServerShareA(const char *path
)
969 BOOL seen_slash
= FALSE
;
971 TRACE("%s\n", wine_dbgstr_a(path
));
973 if (path
&& *path
++ == '\\' && *path
++ == '\\')
984 path
= CharNextA(path
);
991 BOOL WINAPI
PathIsUNCServerShareW(const WCHAR
*path
)
993 BOOL seen_slash
= FALSE
;
995 TRACE("%s\n", wine_dbgstr_w(path
));
997 if (path
&& *path
++ == '\\' && *path
++ == '\\')
1015 BOOL WINAPI
PathIsRootA(const char *path
)
1017 TRACE("%s\n", wine_dbgstr_a(path
));
1019 if (!path
|| !*path
)
1025 return TRUE
; /* \ */
1026 else if (path
[1] == '\\')
1028 BOOL seen_slash
= FALSE
;
1031 /* Check for UNC root path */
1041 path
= CharNextA(path
);
1047 else if (path
[1] == ':' && path
[2] == '\\' && path
[3] == '\0')
1048 return TRUE
; /* X:\ */
1053 BOOL WINAPI
PathIsRootW(const WCHAR
*path
)
1055 TRACE("%s\n", wine_dbgstr_w(path
));
1057 if (!path
|| !*path
)
1063 return TRUE
; /* \ */
1064 else if (path
[1] == '\\')
1066 BOOL seen_slash
= FALSE
;
1069 /* Check for UNC root path */
1084 else if (path
[1] == ':' && path
[2] == '\\' && path
[3] == '\0')
1085 return TRUE
; /* X:\ */
1090 BOOL WINAPI
PathRemoveFileSpecA(char *path
)
1092 char *filespec
= path
;
1093 BOOL modified
= FALSE
;
1095 TRACE("%s\n", wine_dbgstr_a(path
));
1100 /* Skip directory or UNC path */
1109 filespec
= path
; /* Skip dir */
1110 else if (*path
== ':')
1112 filespec
= ++path
; /* Skip drive */
1116 if (!(path
= CharNextA(path
)))
1129 BOOL WINAPI
PathRemoveFileSpecW(WCHAR
*path
)
1131 WCHAR
*filespec
= path
;
1132 BOOL modified
= FALSE
;
1134 TRACE("%s\n", wine_dbgstr_w(path
));
1139 /* Skip directory or UNC path */
1148 filespec
= path
; /* Skip dir */
1149 else if (*path
== ':')
1151 filespec
= ++path
; /* Skip drive */
1168 BOOL WINAPI
PathStripToRootA(char *path
)
1170 TRACE("%s\n", wine_dbgstr_a(path
));
1175 while (!PathIsRootA(path
))
1176 if (!PathRemoveFileSpecA(path
))
1182 BOOL WINAPI
PathStripToRootW(WCHAR
*path
)
1184 TRACE("%s\n", wine_dbgstr_w(path
));
1189 while (!PathIsRootW(path
))
1190 if (!PathRemoveFileSpecW(path
))
1196 LPSTR WINAPI
PathAddBackslashA(char *path
)
1201 TRACE("%s\n", wine_dbgstr_a(path
));
1203 if (!path
|| (len
= strlen(path
)) >= MAX_PATH
)
1210 path
= CharNextA(prev
);
1225 LPWSTR WINAPI
PathAddBackslashW(WCHAR
*path
)
1229 TRACE("%s\n", wine_dbgstr_w(path
));
1231 if (!path
|| (len
= lstrlenW(path
)) >= MAX_PATH
)
1237 if (path
[-1] != '\\')
1247 LPSTR WINAPI
PathFindExtensionA(const char *path
)
1249 const char *lastpoint
= NULL
;
1251 TRACE("%s\n", wine_dbgstr_a(path
));
1257 if (*path
== '\\' || *path
== ' ')
1259 else if (*path
== '.')
1261 path
= CharNextA(path
);
1265 return (LPSTR
)(lastpoint
? lastpoint
: path
);
1268 LPWSTR WINAPI
PathFindExtensionW(const WCHAR
*path
)
1270 const WCHAR
*lastpoint
= NULL
;
1272 TRACE("%s\n", wine_dbgstr_w(path
));
1278 if (*path
== '\\' || *path
== ' ')
1280 else if (*path
== '.')
1286 return (LPWSTR
)(lastpoint
? lastpoint
: path
);
1289 BOOL WINAPI
PathAddExtensionA(char *path
, const char *ext
)
1293 TRACE("%s, %s\n", wine_dbgstr_a(path
), wine_dbgstr_a(ext
));
1295 if (!path
|| !ext
|| *(PathFindExtensionA(path
)))
1299 if (len
+ strlen(ext
) >= MAX_PATH
)
1302 strcpy(path
+ len
, ext
);
1306 BOOL WINAPI
PathAddExtensionW(WCHAR
*path
, const WCHAR
*ext
)
1310 TRACE("%s, %s\n", wine_dbgstr_w(path
), wine_dbgstr_w(ext
));
1312 if (!path
|| !ext
|| *(PathFindExtensionW(path
)))
1315 len
= lstrlenW(path
);
1316 if (len
+ lstrlenW(ext
) >= MAX_PATH
)
1319 lstrcpyW(path
+ len
, ext
);
1323 BOOL WINAPI
PathCanonicalizeW(WCHAR
*buffer
, const WCHAR
*path
)
1325 const WCHAR
*src
= path
;
1326 WCHAR
*dst
= buffer
;
1328 TRACE("%p, %s\n", buffer
, wine_dbgstr_w(path
));
1335 SetLastError(ERROR_INVALID_PARAMETER
);
1346 /* Copy path root */
1351 else if (*src
&& src
[1] == ':')
1360 /* Canonicalize the rest of the path */
1365 if (src
[1] == '\\' && (src
== path
|| src
[-1] == '\\' || src
[-1] == ':'))
1367 src
+= 2; /* Skip .\ */
1369 else if (src
[1] == '.' && dst
!= buffer
&& dst
[-1] == '\\')
1371 /* \.. backs up a directory, over the root if it has no \ following X:.
1372 * .. is ignored if it would remove a UNC server name or initial \\
1376 *dst
= '\0'; /* Allow PathIsUNCServerShareA test on lpszBuf */
1377 if (dst
> buffer
+ 1 && dst
[-1] == '\\' && (dst
[-2] != '\\' || dst
> buffer
+ 2))
1379 if (dst
[-2] == ':' && (dst
> buffer
+ 3 || dst
[-3] == ':'))
1382 while (dst
> buffer
&& *dst
!= '\\')
1385 dst
++; /* Reset to last '\' */
1387 dst
= buffer
; /* Start path again from new root */
1389 else if (dst
[-2] != ':' && !PathIsUNCServerShareW(buffer
))
1392 while (dst
> buffer
&& *dst
!= '\\')
1400 src
+= 2; /* Skip .. in src path */
1409 /* Append \ to naked drive specs */
1410 if (dst
- buffer
== 2 && dst
[-1] == ':')
1416 BOOL WINAPI
PathCanonicalizeA(char *buffer
, const char *path
)
1418 WCHAR pathW
[MAX_PATH
], bufferW
[MAX_PATH
];
1422 TRACE("%p, %s\n", buffer
, wine_dbgstr_a(path
));
1427 if (!buffer
|| !path
)
1429 SetLastError(ERROR_INVALID_PARAMETER
);
1433 len
= MultiByteToWideChar(CP_ACP
, 0, path
, -1, pathW
, ARRAY_SIZE(pathW
));
1437 ret
= PathCanonicalizeW(bufferW
, pathW
);
1438 WideCharToMultiByte(CP_ACP
, 0, bufferW
, -1, buffer
, MAX_PATH
, 0, 0);
1443 WCHAR
* WINAPI
PathCombineW(WCHAR
*dst
, const WCHAR
*dir
, const WCHAR
*file
)
1445 BOOL use_both
= FALSE
, strip
= FALSE
;
1446 WCHAR tmp
[MAX_PATH
];
1448 TRACE("%p, %s, %s\n", dst
, wine_dbgstr_w(dir
), wine_dbgstr_w(file
));
1450 /* Invalid parameters */
1460 if ((!file
|| !*file
) && dir
)
1463 lstrcpynW(tmp
, dir
, ARRAY_SIZE(tmp
));
1465 else if (!dir
|| !*dir
|| !PathIsRelativeW(file
))
1467 if (!dir
|| !*dir
|| *file
!= '\\' || PathIsUNCW(file
))
1470 lstrcpynW(tmp
, file
, ARRAY_SIZE(tmp
));
1483 lstrcpynW(tmp
, dir
, ARRAY_SIZE(tmp
));
1486 PathStripToRootW(tmp
);
1487 file
++; /* Skip '\' */
1490 if (!PathAddBackslashW(tmp
) || lstrlenW(tmp
) + lstrlenW(file
) >= MAX_PATH
)
1496 lstrcatW(tmp
, file
);
1499 PathCanonicalizeW(dst
, tmp
);
1503 LPSTR WINAPI
PathCombineA(char *dst
, const char *dir
, const char *file
)
1505 WCHAR dstW
[MAX_PATH
], dirW
[MAX_PATH
], fileW
[MAX_PATH
];
1507 TRACE("%p, %s, %s\n", dst
, wine_dbgstr_a(dir
), wine_dbgstr_a(file
));
1509 /* Invalid parameters */
1516 if (dir
&& !MultiByteToWideChar(CP_ACP
, 0, dir
, -1, dirW
, ARRAY_SIZE(dirW
)))
1519 if (file
&& !MultiByteToWideChar(CP_ACP
, 0, file
, -1, fileW
, ARRAY_SIZE(fileW
)))
1522 if (PathCombineW(dstW
, dir
? dirW
: NULL
, file
? fileW
: NULL
))
1523 if (WideCharToMultiByte(CP_ACP
, 0, dstW
, -1, dst
, MAX_PATH
, 0, 0))
1530 BOOL WINAPI
PathAppendA(char *path
, const char *append
)
1532 TRACE("%s, %s\n", wine_dbgstr_a(path
), wine_dbgstr_a(append
));
1536 if (!PathIsUNCA(append
))
1537 while (*append
== '\\')
1540 if (PathCombineA(path
, path
, append
))
1547 BOOL WINAPI
PathAppendW(WCHAR
*path
, const WCHAR
*append
)
1549 TRACE("%s, %s\n", wine_dbgstr_w(path
), wine_dbgstr_w(append
));
1553 if (!PathIsUNCW(append
))
1554 while (*append
== '\\')
1557 if (PathCombineW(path
, path
, append
))
1564 int WINAPI
PathCommonPrefixA(const char *file1
, const char *file2
, char *path
)
1566 const char *iter1
= file1
;
1567 const char *iter2
= file2
;
1568 unsigned int len
= 0;
1570 TRACE("%s, %s, %p.\n", wine_dbgstr_a(file1
), wine_dbgstr_a(file2
), path
);
1575 if (!file1
|| !file2
)
1578 /* Handle roots first */
1579 if (PathIsUNCA(file1
))
1581 if (!PathIsUNCA(file2
))
1586 else if (PathIsUNCA(file2
))
1592 if ((!*iter1
|| *iter1
== '\\') && (!*iter2
|| *iter2
== '\\'))
1593 len
= iter1
- file1
; /* Common to this point */
1595 if (!*iter1
|| (tolower(*iter1
) != tolower(*iter2
)))
1596 break; /* Strings differ at this point */
1603 len
++; /* Feature/Bug compatible with Win32 */
1607 memcpy(path
, file1
, len
);
1614 int WINAPI
PathCommonPrefixW(const WCHAR
*file1
, const WCHAR
*file2
, WCHAR
*path
)
1616 const WCHAR
*iter1
= file1
;
1617 const WCHAR
*iter2
= file2
;
1618 unsigned int len
= 0;
1620 TRACE("%s, %s, %p\n", wine_dbgstr_w(file1
), wine_dbgstr_w(file2
), path
);
1625 if (!file1
|| !file2
)
1628 /* Handle roots first */
1629 if (PathIsUNCW(file1
))
1631 if (!PathIsUNCW(file2
))
1636 else if (PathIsUNCW(file2
))
1642 if ((!*iter1
|| *iter1
== '\\') && (!*iter2
|| *iter2
== '\\'))
1643 len
= iter1
- file1
; /* Common to this point */
1645 if (!*iter1
|| (towupper(*iter1
) != towupper(*iter2
)))
1646 break; /* Strings differ at this point */
1653 len
++; /* Feature/Bug compatible with Win32 */
1657 memcpy(path
, file1
, len
* sizeof(WCHAR
));
1664 BOOL WINAPI
PathIsPrefixA(const char *prefix
, const char *path
)
1666 TRACE("%s, %s\n", wine_dbgstr_a(prefix
), wine_dbgstr_a(path
));
1668 return prefix
&& path
&& PathCommonPrefixA(path
, prefix
, NULL
) == (int)strlen(prefix
);
1671 BOOL WINAPI
PathIsPrefixW(const WCHAR
*prefix
, const WCHAR
*path
)
1673 TRACE("%s, %s\n", wine_dbgstr_w(prefix
), wine_dbgstr_w(path
));
1675 return prefix
&& path
&& PathCommonPrefixW(path
, prefix
, NULL
) == (int)lstrlenW(prefix
);
1678 char * WINAPI
PathFindFileNameA(const char *path
)
1680 const char *last_slash
= path
;
1682 TRACE("%s\n", wine_dbgstr_a(path
));
1684 while (path
&& *path
)
1686 if ((*path
== '\\' || *path
== '/' || *path
== ':') &&
1687 path
[1] && path
[1] != '\\' && path
[1] != '/')
1688 last_slash
= path
+ 1;
1689 path
= CharNextA(path
);
1692 return (char *)last_slash
;
1695 WCHAR
* WINAPI
PathFindFileNameW(const WCHAR
*path
)
1697 const WCHAR
*last_slash
= path
;
1699 TRACE("%s\n", wine_dbgstr_w(path
));
1701 while (path
&& *path
)
1703 if ((*path
== '\\' || *path
== '/' || *path
== ':') &&
1704 path
[1] && path
[1] != '\\' && path
[1] != '/')
1705 last_slash
= path
+ 1;
1709 return (WCHAR
*)last_slash
;
1712 char * WINAPI
PathGetArgsA(const char *path
)
1714 BOOL seen_quote
= FALSE
;
1716 TRACE("%s\n", wine_dbgstr_a(path
));
1723 if (*path
== ' ' && !seen_quote
)
1724 return (char *)path
+ 1;
1727 seen_quote
= !seen_quote
;
1728 path
= CharNextA(path
);
1731 return (char *)path
;
1734 WCHAR
* WINAPI
PathGetArgsW(const WCHAR
*path
)
1736 BOOL seen_quote
= FALSE
;
1738 TRACE("%s\n", wine_dbgstr_w(path
));
1745 if (*path
== ' ' && !seen_quote
)
1746 return (WCHAR
*)path
+ 1;
1749 seen_quote
= !seen_quote
;
1753 return (WCHAR
*)path
;
1756 UINT WINAPI
PathGetCharTypeW(WCHAR ch
)
1762 if (!ch
|| ch
< ' ' || ch
== '<' || ch
== '>' || ch
== '"' || ch
== '|' || ch
== '/')
1763 flags
= GCT_INVALID
; /* Invalid */
1764 else if (ch
== '*' || ch
== '?')
1765 flags
= GCT_WILD
; /* Wildchars */
1766 else if (ch
== '\\' || ch
== ':')
1767 return GCT_SEPARATOR
; /* Path separators */
1772 if (((ch
& 0x1) && ch
!= ';') || !ch
|| isalnum(ch
) || ch
== '$' || ch
== '&' || ch
== '(' ||
1773 ch
== '.' || ch
== '@' || ch
== '^' || ch
== '\'' || ch
== '`')
1775 flags
|= GCT_SHORTCHAR
; /* All these are valid for DOS */
1779 flags
|= GCT_SHORTCHAR
; /* Bug compatible with win32 */
1781 flags
|= GCT_LFNCHAR
; /* Valid for long file names */
1787 UINT WINAPI
PathGetCharTypeA(UCHAR ch
)
1789 return PathGetCharTypeW(ch
);
1792 int WINAPI
PathGetDriveNumberA(const char *path
)
1794 TRACE("%s\n", wine_dbgstr_a(path
));
1796 if (path
&& *path
&& path
[1] == ':')
1798 if (*path
>= 'a' && *path
<= 'z') return *path
- 'a';
1799 if (*path
>= 'A' && *path
<= 'Z') return *path
- 'A';
1804 int WINAPI
PathGetDriveNumberW(const WCHAR
*path
)
1806 TRACE("%s\n", wine_dbgstr_w(path
));
1811 if (!wcsncmp(path
, L
"\\\\?\\", 4)) path
+= 4;
1813 if (!path
[0] || path
[1] != ':') return -1;
1814 if (path
[0] >= 'A' && path
[0] <= 'Z') return path
[0] - 'A';
1815 if (path
[0] >= 'a' && path
[0] <= 'z') return path
[0] - 'a';
1819 BOOL WINAPI
PathIsFileSpecA(const char *path
)
1821 TRACE("%s\n", wine_dbgstr_a(path
));
1828 if (*path
== '\\' || *path
== ':')
1830 path
= CharNextA(path
);
1836 BOOL WINAPI
PathIsFileSpecW(const WCHAR
*path
)
1838 TRACE("%s\n", wine_dbgstr_w(path
));
1845 if (*path
== '\\' || *path
== ':')
1853 BOOL WINAPI
PathIsUNCServerA(const char *path
)
1855 TRACE("%s\n", wine_dbgstr_a(path
));
1857 if (!(path
&& path
[0] == '\\' && path
[1] == '\\'))
1864 path
= CharNextA(path
);
1870 BOOL WINAPI
PathIsUNCServerW(const WCHAR
*path
)
1872 TRACE("%s\n", wine_dbgstr_w(path
));
1874 if (!(path
&& path
[0] == '\\' && path
[1] == '\\'))
1877 return !wcschr(path
+ 2, '\\');
1880 void WINAPI
PathRemoveBlanksA(char *path
)
1882 char *start
, *first
;
1884 TRACE("%s\n", wine_dbgstr_a(path
));
1886 if (!path
|| !*path
)
1889 start
= first
= path
;
1891 while (*path
== ' ')
1892 path
= CharNextA(path
);
1898 while (start
[-1] == ' ')
1904 void WINAPI
PathRemoveBlanksW(WCHAR
*path
)
1906 WCHAR
*start
, *first
;
1908 TRACE("%s\n", wine_dbgstr_w(path
));
1910 if (!path
|| !*path
)
1913 start
= first
= path
;
1915 while (*path
== ' ')
1922 while (start
[-1] == ' ')
1928 void WINAPI
PathRemoveExtensionA(char *path
)
1930 TRACE("%s\n", wine_dbgstr_a(path
));
1935 path
= PathFindExtensionA(path
);
1940 void WINAPI
PathRemoveExtensionW(WCHAR
*path
)
1942 TRACE("%s\n", wine_dbgstr_w(path
));
1947 path
= PathFindExtensionW(path
);
1952 BOOL WINAPI
PathRenameExtensionA(char *path
, const char *ext
)
1956 TRACE("%s, %s\n", wine_dbgstr_a(path
), wine_dbgstr_a(ext
));
1958 extension
= PathFindExtensionA(path
);
1960 if (!extension
|| (extension
- path
+ strlen(ext
) >= MAX_PATH
))
1963 strcpy(extension
, ext
);
1967 BOOL WINAPI
PathRenameExtensionW(WCHAR
*path
, const WCHAR
*ext
)
1971 TRACE("%s, %s\n", wine_dbgstr_w(path
), wine_dbgstr_w(ext
));
1973 extension
= PathFindExtensionW(path
);
1975 if (!extension
|| (extension
- path
+ lstrlenW(ext
) >= MAX_PATH
))
1978 lstrcpyW(extension
, ext
);
1982 void WINAPI
PathUnquoteSpacesA(char *path
)
1986 TRACE("%s\n", wine_dbgstr_a(path
));
1988 if (!path
|| *path
!= '"')
1991 len
= strlen(path
) - 1;
1992 if (path
[len
] == '"')
1995 for (; *path
; path
++)
2000 void WINAPI
PathUnquoteSpacesW(WCHAR
*path
)
2004 TRACE("%s\n", wine_dbgstr_w(path
));
2006 if (!path
|| *path
!= '"')
2009 len
= lstrlenW(path
) - 1;
2010 if (path
[len
] == '"')
2013 for (; *path
; path
++)
2018 char * WINAPI
PathRemoveBackslashA(char *path
)
2022 TRACE("%s\n", wine_dbgstr_a(path
));
2027 ptr
= CharPrevA(path
, path
+ strlen(path
));
2028 if (!PathIsRootA(path
) && *ptr
== '\\')
2034 WCHAR
* WINAPI
PathRemoveBackslashW(WCHAR
*path
)
2038 TRACE("%s\n", wine_dbgstr_w(path
));
2043 ptr
= path
+ lstrlenW(path
);
2044 if (ptr
> path
) ptr
--;
2045 if (!PathIsRootW(path
) && *ptr
== '\\')
2051 BOOL WINAPI
PathIsLFNFileSpecA(const char *path
)
2053 unsigned int name_len
= 0, ext_len
= 0;
2055 TRACE("%s\n", wine_dbgstr_a(path
));
2063 return TRUE
; /* DOS names cannot have spaces */
2067 return TRUE
; /* DOS names have only one dot */
2074 return TRUE
; /* DOS extensions are <= 3 chars*/
2080 return TRUE
; /* DOS names are <= 8 chars */
2082 path
= CharNextA(path
);
2085 return FALSE
; /* Valid DOS path */
2088 BOOL WINAPI
PathIsLFNFileSpecW(const WCHAR
*path
)
2090 unsigned int name_len
= 0, ext_len
= 0;
2092 TRACE("%s\n", wine_dbgstr_w(path
));
2100 return TRUE
; /* DOS names cannot have spaces */
2104 return TRUE
; /* DOS names have only one dot */
2111 return TRUE
; /* DOS extensions are <= 3 chars*/
2117 return TRUE
; /* DOS names are <= 8 chars */
2122 return FALSE
; /* Valid DOS path */
2125 #define PATH_CHAR_CLASS_LETTER 0x00000001
2126 #define PATH_CHAR_CLASS_ASTERIX 0x00000002
2127 #define PATH_CHAR_CLASS_DOT 0x00000004
2128 #define PATH_CHAR_CLASS_BACKSLASH 0x00000008
2129 #define PATH_CHAR_CLASS_COLON 0x00000010
2130 #define PATH_CHAR_CLASS_SEMICOLON 0x00000020
2131 #define PATH_CHAR_CLASS_COMMA 0x00000040
2132 #define PATH_CHAR_CLASS_SPACE 0x00000080
2133 #define PATH_CHAR_CLASS_OTHER_VALID 0x00000100
2134 #define PATH_CHAR_CLASS_DOUBLEQUOTE 0x00000200
2136 #define PATH_CHAR_CLASS_INVALID 0x00000000
2137 #define PATH_CHAR_CLASS_ANY 0xffffffff
2139 static const DWORD path_charclass
[] =
2141 /* 0x00 */ PATH_CHAR_CLASS_INVALID
, /* 0x01 */ PATH_CHAR_CLASS_INVALID
,
2142 /* 0x02 */ PATH_CHAR_CLASS_INVALID
, /* 0x03 */ PATH_CHAR_CLASS_INVALID
,
2143 /* 0x04 */ PATH_CHAR_CLASS_INVALID
, /* 0x05 */ PATH_CHAR_CLASS_INVALID
,
2144 /* 0x06 */ PATH_CHAR_CLASS_INVALID
, /* 0x07 */ PATH_CHAR_CLASS_INVALID
,
2145 /* 0x08 */ PATH_CHAR_CLASS_INVALID
, /* 0x09 */ PATH_CHAR_CLASS_INVALID
,
2146 /* 0x0a */ PATH_CHAR_CLASS_INVALID
, /* 0x0b */ PATH_CHAR_CLASS_INVALID
,
2147 /* 0x0c */ PATH_CHAR_CLASS_INVALID
, /* 0x0d */ PATH_CHAR_CLASS_INVALID
,
2148 /* 0x0e */ PATH_CHAR_CLASS_INVALID
, /* 0x0f */ PATH_CHAR_CLASS_INVALID
,
2149 /* 0x10 */ PATH_CHAR_CLASS_INVALID
, /* 0x11 */ PATH_CHAR_CLASS_INVALID
,
2150 /* 0x12 */ PATH_CHAR_CLASS_INVALID
, /* 0x13 */ PATH_CHAR_CLASS_INVALID
,
2151 /* 0x14 */ PATH_CHAR_CLASS_INVALID
, /* 0x15 */ PATH_CHAR_CLASS_INVALID
,
2152 /* 0x16 */ PATH_CHAR_CLASS_INVALID
, /* 0x17 */ PATH_CHAR_CLASS_INVALID
,
2153 /* 0x18 */ PATH_CHAR_CLASS_INVALID
, /* 0x19 */ PATH_CHAR_CLASS_INVALID
,
2154 /* 0x1a */ PATH_CHAR_CLASS_INVALID
, /* 0x1b */ PATH_CHAR_CLASS_INVALID
,
2155 /* 0x1c */ PATH_CHAR_CLASS_INVALID
, /* 0x1d */ PATH_CHAR_CLASS_INVALID
,
2156 /* 0x1e */ PATH_CHAR_CLASS_INVALID
, /* 0x1f */ PATH_CHAR_CLASS_INVALID
,
2157 /* ' ' */ PATH_CHAR_CLASS_SPACE
, /* '!' */ PATH_CHAR_CLASS_OTHER_VALID
,
2158 /* '"' */ PATH_CHAR_CLASS_DOUBLEQUOTE
, /* '#' */ PATH_CHAR_CLASS_OTHER_VALID
,
2159 /* '$' */ PATH_CHAR_CLASS_OTHER_VALID
, /* '%' */ PATH_CHAR_CLASS_OTHER_VALID
,
2160 /* '&' */ PATH_CHAR_CLASS_OTHER_VALID
, /* '\'' */ PATH_CHAR_CLASS_OTHER_VALID
,
2161 /* '(' */ PATH_CHAR_CLASS_OTHER_VALID
, /* ')' */ PATH_CHAR_CLASS_OTHER_VALID
,
2162 /* '*' */ PATH_CHAR_CLASS_ASTERIX
, /* '+' */ PATH_CHAR_CLASS_OTHER_VALID
,
2163 /* ',' */ PATH_CHAR_CLASS_COMMA
, /* '-' */ PATH_CHAR_CLASS_OTHER_VALID
,
2164 /* '.' */ PATH_CHAR_CLASS_DOT
, /* '/' */ PATH_CHAR_CLASS_INVALID
,
2165 /* '0' */ PATH_CHAR_CLASS_OTHER_VALID
, /* '1' */ PATH_CHAR_CLASS_OTHER_VALID
,
2166 /* '2' */ PATH_CHAR_CLASS_OTHER_VALID
, /* '3' */ PATH_CHAR_CLASS_OTHER_VALID
,
2167 /* '4' */ PATH_CHAR_CLASS_OTHER_VALID
, /* '5' */ PATH_CHAR_CLASS_OTHER_VALID
,
2168 /* '6' */ PATH_CHAR_CLASS_OTHER_VALID
, /* '7' */ PATH_CHAR_CLASS_OTHER_VALID
,
2169 /* '8' */ PATH_CHAR_CLASS_OTHER_VALID
, /* '9' */ PATH_CHAR_CLASS_OTHER_VALID
,
2170 /* ':' */ PATH_CHAR_CLASS_COLON
, /* ';' */ PATH_CHAR_CLASS_SEMICOLON
,
2171 /* '<' */ PATH_CHAR_CLASS_INVALID
, /* '=' */ PATH_CHAR_CLASS_OTHER_VALID
,
2172 /* '>' */ PATH_CHAR_CLASS_INVALID
, /* '?' */ PATH_CHAR_CLASS_LETTER
,
2173 /* '@' */ PATH_CHAR_CLASS_OTHER_VALID
, /* 'A' */ PATH_CHAR_CLASS_ANY
,
2174 /* 'B' */ PATH_CHAR_CLASS_ANY
, /* 'C' */ PATH_CHAR_CLASS_ANY
,
2175 /* 'D' */ PATH_CHAR_CLASS_ANY
, /* 'E' */ PATH_CHAR_CLASS_ANY
,
2176 /* 'F' */ PATH_CHAR_CLASS_ANY
, /* 'G' */ PATH_CHAR_CLASS_ANY
,
2177 /* 'H' */ PATH_CHAR_CLASS_ANY
, /* 'I' */ PATH_CHAR_CLASS_ANY
,
2178 /* 'J' */ PATH_CHAR_CLASS_ANY
, /* 'K' */ PATH_CHAR_CLASS_ANY
,
2179 /* 'L' */ PATH_CHAR_CLASS_ANY
, /* 'M' */ PATH_CHAR_CLASS_ANY
,
2180 /* 'N' */ PATH_CHAR_CLASS_ANY
, /* 'O' */ PATH_CHAR_CLASS_ANY
,
2181 /* 'P' */ PATH_CHAR_CLASS_ANY
, /* 'Q' */ PATH_CHAR_CLASS_ANY
,
2182 /* 'R' */ PATH_CHAR_CLASS_ANY
, /* 'S' */ PATH_CHAR_CLASS_ANY
,
2183 /* 'T' */ PATH_CHAR_CLASS_ANY
, /* 'U' */ PATH_CHAR_CLASS_ANY
,
2184 /* 'V' */ PATH_CHAR_CLASS_ANY
, /* 'W' */ PATH_CHAR_CLASS_ANY
,
2185 /* 'X' */ PATH_CHAR_CLASS_ANY
, /* 'Y' */ PATH_CHAR_CLASS_ANY
,
2186 /* 'Z' */ PATH_CHAR_CLASS_ANY
, /* '[' */ PATH_CHAR_CLASS_OTHER_VALID
,
2187 /* '\\' */ PATH_CHAR_CLASS_BACKSLASH
, /* ']' */ PATH_CHAR_CLASS_OTHER_VALID
,
2188 /* '^' */ PATH_CHAR_CLASS_OTHER_VALID
, /* '_' */ PATH_CHAR_CLASS_OTHER_VALID
,
2189 /* '`' */ PATH_CHAR_CLASS_OTHER_VALID
, /* 'a' */ PATH_CHAR_CLASS_ANY
,
2190 /* 'b' */ PATH_CHAR_CLASS_ANY
, /* 'c' */ PATH_CHAR_CLASS_ANY
,
2191 /* 'd' */ PATH_CHAR_CLASS_ANY
, /* 'e' */ PATH_CHAR_CLASS_ANY
,
2192 /* 'f' */ PATH_CHAR_CLASS_ANY
, /* 'g' */ PATH_CHAR_CLASS_ANY
,
2193 /* 'h' */ PATH_CHAR_CLASS_ANY
, /* 'i' */ PATH_CHAR_CLASS_ANY
,
2194 /* 'j' */ PATH_CHAR_CLASS_ANY
, /* 'k' */ PATH_CHAR_CLASS_ANY
,
2195 /* 'l' */ PATH_CHAR_CLASS_ANY
, /* 'm' */ PATH_CHAR_CLASS_ANY
,
2196 /* 'n' */ PATH_CHAR_CLASS_ANY
, /* 'o' */ PATH_CHAR_CLASS_ANY
,
2197 /* 'p' */ PATH_CHAR_CLASS_ANY
, /* 'q' */ PATH_CHAR_CLASS_ANY
,
2198 /* 'r' */ PATH_CHAR_CLASS_ANY
, /* 's' */ PATH_CHAR_CLASS_ANY
,
2199 /* 't' */ PATH_CHAR_CLASS_ANY
, /* 'u' */ PATH_CHAR_CLASS_ANY
,
2200 /* 'v' */ PATH_CHAR_CLASS_ANY
, /* 'w' */ PATH_CHAR_CLASS_ANY
,
2201 /* 'x' */ PATH_CHAR_CLASS_ANY
, /* 'y' */ PATH_CHAR_CLASS_ANY
,
2202 /* 'z' */ PATH_CHAR_CLASS_ANY
, /* '{' */ PATH_CHAR_CLASS_OTHER_VALID
,
2203 /* '|' */ PATH_CHAR_CLASS_INVALID
, /* '}' */ PATH_CHAR_CLASS_OTHER_VALID
,
2204 /* '~' */ PATH_CHAR_CLASS_OTHER_VALID
2207 BOOL WINAPI
PathIsValidCharA(char c
, DWORD
class)
2209 if ((unsigned)c
> 0x7e)
2210 return class & PATH_CHAR_CLASS_OTHER_VALID
;
2212 return class & path_charclass
[(unsigned)c
];
2215 BOOL WINAPI
PathIsValidCharW(WCHAR c
, DWORD
class)
2218 return class & PATH_CHAR_CLASS_OTHER_VALID
;
2220 return class & path_charclass
[c
];
2223 char * WINAPI
PathFindNextComponentA(const char *path
)
2227 TRACE("%s\n", wine_dbgstr_a(path
));
2229 if (!path
|| !*path
)
2232 if ((slash
= StrChrA(path
, '\\')))
2234 if (slash
[1] == '\\')
2239 return (char *)path
+ strlen(path
);
2242 WCHAR
* WINAPI
PathFindNextComponentW(const WCHAR
*path
)
2246 TRACE("%s\n", wine_dbgstr_w(path
));
2248 if (!path
|| !*path
)
2251 if ((slash
= StrChrW(path
, '\\')))
2253 if (slash
[1] == '\\')
2258 return (WCHAR
*)path
+ lstrlenW(path
);
2261 char * WINAPI
PathSkipRootA(const char *path
)
2263 TRACE("%s\n", wine_dbgstr_a(path
));
2265 if (!path
|| !*path
)
2268 if (*path
== '\\' && path
[1] == '\\')
2270 /* Network share: skip share server and mount point */
2272 if ((path
= StrChrA(path
, '\\')) && (path
= StrChrA(path
+ 1, '\\')))
2274 return (char *)path
;
2277 if (IsDBCSLeadByte(*path
))
2281 if (path
[0] && path
[1] == ':' && path
[2] == '\\')
2282 return (char *)path
+ 3;
2287 WCHAR
* WINAPI
PathSkipRootW(const WCHAR
*path
)
2289 TRACE("%s\n", wine_dbgstr_w(path
));
2291 if (!path
|| !*path
)
2294 if (*path
== '\\' && path
[1] == '\\')
2296 /* Network share: skip share server and mount point */
2298 if ((path
= StrChrW(path
, '\\')) && (path
= StrChrW(path
+ 1, '\\')))
2300 return (WCHAR
*)path
;
2304 if (path
[0] && path
[1] == ':' && path
[2] == '\\')
2305 return (WCHAR
*)path
+ 3;
2310 void WINAPI
PathStripPathA(char *path
)
2312 TRACE("%s\n", wine_dbgstr_a(path
));
2316 char *filename
= PathFindFileNameA(path
);
2317 if (filename
!= path
)
2318 RtlMoveMemory(path
, filename
, strlen(filename
) + 1);
2322 void WINAPI
PathStripPathW(WCHAR
*path
)
2326 TRACE("%s\n", wine_dbgstr_w(path
));
2327 filename
= PathFindFileNameW(path
);
2328 if (filename
!= path
)
2329 RtlMoveMemory(path
, filename
, (lstrlenW(filename
) + 1) * sizeof(WCHAR
));
2332 BOOL WINAPI
PathSearchAndQualifyA(const char *path
, char *buffer
, UINT length
)
2334 TRACE("%s, %p, %u\n", wine_dbgstr_a(path
), buffer
, length
);
2336 if (SearchPathA(NULL
, path
, NULL
, length
, buffer
, NULL
))
2339 return !!GetFullPathNameA(path
, length
, buffer
, NULL
);
2342 BOOL WINAPI
PathSearchAndQualifyW(const WCHAR
*path
, WCHAR
*buffer
, UINT length
)
2344 TRACE("%s, %p, %u\n", wine_dbgstr_w(path
), buffer
, length
);
2346 if (SearchPathW(NULL
, path
, NULL
, length
, buffer
, NULL
))
2348 return !!GetFullPathNameW(path
, length
, buffer
, NULL
);
2351 BOOL WINAPI
PathRelativePathToA(char *path
, const char *from
, DWORD attributes_from
, const char *to
,
2352 DWORD attributes_to
)
2354 WCHAR pathW
[MAX_PATH
], fromW
[MAX_PATH
], toW
[MAX_PATH
];
2357 TRACE("%p, %s, %#lx, %s, %#lx\n", path
, wine_dbgstr_a(from
), attributes_from
, wine_dbgstr_a(to
), attributes_to
);
2359 if (!path
|| !from
|| !to
)
2362 MultiByteToWideChar(CP_ACP
, 0, from
, -1, fromW
, ARRAY_SIZE(fromW
));
2363 MultiByteToWideChar(CP_ACP
, 0, to
, -1, toW
, ARRAY_SIZE(toW
));
2364 ret
= PathRelativePathToW(pathW
, fromW
, attributes_from
, toW
, attributes_to
);
2365 WideCharToMultiByte(CP_ACP
, 0, pathW
, -1, path
, MAX_PATH
, 0, 0);
2370 BOOL WINAPI
PathRelativePathToW(WCHAR
*path
, const WCHAR
*from
, DWORD attributes_from
, const WCHAR
*to
,
2371 DWORD attributes_to
)
2373 WCHAR fromW
[MAX_PATH
], toW
[MAX_PATH
];
2376 TRACE("%p, %s, %#lx, %s, %#lx\n", path
, wine_dbgstr_w(from
), attributes_from
, wine_dbgstr_w(to
), attributes_to
);
2378 if (!path
|| !from
|| !to
)
2382 lstrcpynW(fromW
, from
, ARRAY_SIZE(fromW
));
2383 lstrcpynW(toW
, to
, ARRAY_SIZE(toW
));
2385 if (!(attributes_from
& FILE_ATTRIBUTE_DIRECTORY
))
2386 PathRemoveFileSpecW(fromW
);
2387 if (!(attributes_to
& FILE_ATTRIBUTE_DIRECTORY
))
2388 PathRemoveFileSpecW(toW
);
2390 /* Paths can only be relative if they have a common root */
2391 if (!(len
= PathCommonPrefixW(fromW
, toW
, 0)))
2394 /* Strip off 'from' components to the root, by adding "..\" */
2406 from
= PathFindNextComponentW(from
);
2407 lstrcatW(path
, *from
? L
"..\\" : L
"..");
2410 /* From the root add the components of 'to' */
2412 /* We check to[-1] to avoid skipping end of string. See the notes for this function. */
2417 len
= lstrlenW(path
);
2418 if (len
+ lstrlenW(to
) >= MAX_PATH
)
2423 lstrcpyW(path
+ len
, to
);
2429 HRESULT WINAPI
PathMatchSpecExA(const char *path
, const char *mask
, DWORD flags
)
2431 WCHAR
*pathW
, *maskW
;
2434 TRACE("%s, %s\n", wine_dbgstr_a(path
), wine_dbgstr_a(mask
));
2437 FIXME("Ignoring flags %#lx.\n", flags
);
2439 if (!lstrcmpA(mask
, "*.*"))
2440 return S_OK
; /* Matches every path */
2442 pathW
= heap_strdupAtoW( path
);
2443 maskW
= heap_strdupAtoW( mask
);
2444 ret
= PathMatchSpecExW( pathW
, maskW
, flags
);
2450 BOOL WINAPI
PathMatchSpecA(const char *path
, const char *mask
)
2452 return PathMatchSpecExA(path
, mask
, 0) == S_OK
;
2455 static BOOL
path_match_maskW(const WCHAR
*name
, const WCHAR
*mask
)
2457 while (*name
&& *mask
&& *mask
!= ';')
2463 if (path_match_maskW(name
, mask
+ 1))
2464 return TRUE
; /* try substrings */
2469 if (towupper(*mask
) != towupper(*name
) && *mask
!= '?')
2478 while (*mask
== '*')
2480 if (!*mask
|| *mask
== ';')
2487 HRESULT WINAPI
PathMatchSpecExW(const WCHAR
*path
, const WCHAR
*mask
, DWORD flags
)
2489 TRACE("%s, %s\n", wine_dbgstr_w(path
), wine_dbgstr_w(mask
));
2492 FIXME("Ignoring flags %#lx.\n", flags
);
2494 if (!lstrcmpW(mask
, L
"*.*"))
2495 return S_OK
; /* Matches every path */
2499 while (*mask
== ' ')
2500 mask
++; /* Eat leading spaces */
2502 if (path_match_maskW(path
, mask
))
2503 return S_OK
; /* Matches the current path */
2505 while (*mask
&& *mask
!= ';')
2506 mask
++; /* masks separated by ';' */
2515 BOOL WINAPI
PathMatchSpecW(const WCHAR
*path
, const WCHAR
*mask
)
2517 return PathMatchSpecExW(path
, mask
, 0) == S_OK
;
2520 void WINAPI
PathQuoteSpacesA(char *path
)
2522 TRACE("%s\n", wine_dbgstr_a(path
));
2524 if (path
&& StrChrA(path
, ' '))
2526 size_t len
= strlen(path
) + 1;
2528 if (len
+ 2 < MAX_PATH
)
2530 memmove(path
+ 1, path
, len
);
2533 path
[len
+ 1] = '\0';
2538 void WINAPI
PathQuoteSpacesW(WCHAR
*path
)
2540 TRACE("%s\n", wine_dbgstr_w(path
));
2542 if (path
&& StrChrW(path
, ' '))
2544 int len
= lstrlenW(path
) + 1;
2546 if (len
+ 2 < MAX_PATH
)
2548 memmove(path
+ 1, path
, len
* sizeof(WCHAR
));
2551 path
[len
+ 1] = '\0';
2556 BOOL WINAPI
PathIsSameRootA(const char *path1
, const char *path2
)
2561 TRACE("%s, %s\n", wine_dbgstr_a(path1
), wine_dbgstr_a(path2
));
2563 if (!path1
|| !path2
|| !(start
= PathSkipRootA(path1
)))
2566 len
= PathCommonPrefixA(path1
, path2
, NULL
) + 1;
2567 return start
- path1
<= len
;
2570 BOOL WINAPI
PathIsSameRootW(const WCHAR
*path1
, const WCHAR
*path2
)
2575 TRACE("%s, %s\n", wine_dbgstr_w(path1
), wine_dbgstr_w(path2
));
2577 if (!path1
|| !path2
|| !(start
= PathSkipRootW(path1
)))
2580 len
= PathCommonPrefixW(path1
, path2
, NULL
) + 1;
2581 return start
- path1
<= len
;
2584 BOOL WINAPI
PathFileExistsA(const char *path
)
2589 TRACE("%s\n", wine_dbgstr_a(path
));
2594 /* Prevent a dialog box if path is on a disk that has been ejected. */
2595 prev_mode
= SetErrorMode(SEM_FAILCRITICALERRORS
);
2596 attrs
= GetFileAttributesA(path
);
2597 SetErrorMode(prev_mode
);
2598 return attrs
!= INVALID_FILE_ATTRIBUTES
;
2601 BOOL WINAPI
PathFileExistsW(const WCHAR
*path
)
2606 TRACE("%s\n", wine_dbgstr_w(path
));
2611 prev_mode
= SetErrorMode(SEM_FAILCRITICALERRORS
);
2612 attrs
= GetFileAttributesW(path
);
2613 SetErrorMode(prev_mode
);
2614 return attrs
!= INVALID_FILE_ATTRIBUTES
;
2617 int WINAPI
PathParseIconLocationA(char *path
)
2622 TRACE("%s\n", debugstr_a(path
));
2627 if ((comma
= strchr(path
, ',')))
2630 ret
= StrToIntA(comma
);
2632 PathUnquoteSpacesA(path
);
2633 PathRemoveBlanksA(path
);
2638 int WINAPI
PathParseIconLocationW(WCHAR
*path
)
2643 TRACE("%s\n", debugstr_w(path
));
2648 if ((comma
= StrChrW(path
, ',')))
2651 ret
= StrToIntW(comma
);
2653 PathUnquoteSpacesW(path
);
2654 PathRemoveBlanksW(path
);
2659 BOOL WINAPI
PathUnExpandEnvStringsA(const char *path
, char *buffer
, UINT buf_len
)
2661 WCHAR bufferW
[MAX_PATH
], *pathW
;
2665 TRACE("%s, %p, %d\n", debugstr_a(path
), buffer
, buf_len
);
2667 pathW
= heap_strdupAtoW(path
);
2668 if (!pathW
) return FALSE
;
2670 ret
= PathUnExpandEnvStringsW(pathW
, bufferW
, MAX_PATH
);
2671 HeapFree(GetProcessHeap(), 0, pathW
);
2672 if (!ret
) return FALSE
;
2674 len
= WideCharToMultiByte(CP_ACP
, 0, bufferW
, -1, NULL
, 0, NULL
, NULL
);
2675 if (buf_len
< len
+ 1) return FALSE
;
2677 WideCharToMultiByte(CP_ACP
, 0, bufferW
, -1, buffer
, buf_len
, NULL
, NULL
);
2684 WCHAR path
[MAX_PATH
];
2688 static void init_envvars_map(struct envvars_map
*map
)
2692 map
->len
= ExpandEnvironmentStringsW(map
->var
, map
->path
, ARRAY_SIZE(map
->path
));
2693 /* exclude null from length */
2694 if (map
->len
) map
->len
--;
2699 BOOL WINAPI
PathUnExpandEnvStringsW(const WCHAR
*path
, WCHAR
*buffer
, UINT buf_len
)
2701 static struct envvars_map null_var
= {L
"", {0}, 0};
2702 struct envvars_map
*match
= &null_var
, *cur
;
2703 struct envvars_map envvars
[] =
2705 { L
"%ALLUSERSPROFILE%" },
2707 { L
"%ProgramFiles%" },
2708 { L
"%SystemRoot%" },
2709 { L
"%SystemDrive%" },
2710 { L
"%USERPROFILE%" },
2716 TRACE("%s, %p, %d\n", debugstr_w(path
), buffer
, buf_len
);
2718 pathlen
= lstrlenW(path
);
2719 init_envvars_map(envvars
);
2723 /* path can't contain expanded value or value wasn't retrieved */
2724 if (cur
->len
== 0 || cur
->len
> pathlen
||
2725 CompareStringOrdinal( cur
->path
, cur
->len
, path
, cur
->len
, TRUE
) != CSTR_EQUAL
)
2731 if (cur
->len
> match
->len
)
2736 needed
= lstrlenW(match
->var
) + 1 + pathlen
- match
->len
;
2737 if (match
->len
== 0 || needed
> buf_len
) return FALSE
;
2739 lstrcpyW(buffer
, match
->var
);
2740 lstrcatW(buffer
, &path
[match
->len
]);
2741 TRACE("ret %s\n", debugstr_w(buffer
));
2748 URL_SCHEME scheme_number
;
2749 const WCHAR
*scheme_name
;
2753 { URL_SCHEME_FTP
, L
"ftp"},
2754 { URL_SCHEME_HTTP
, L
"http"},
2755 { URL_SCHEME_GOPHER
, L
"gopher"},
2756 { URL_SCHEME_MAILTO
, L
"mailto"},
2757 { URL_SCHEME_NEWS
, L
"news"},
2758 { URL_SCHEME_NNTP
, L
"nntp"},
2759 { URL_SCHEME_TELNET
, L
"telnet"},
2760 { URL_SCHEME_WAIS
, L
"wais"},
2761 { URL_SCHEME_FILE
, L
"file"},
2762 { URL_SCHEME_MK
, L
"mk"},
2763 { URL_SCHEME_HTTPS
, L
"https"},
2764 { URL_SCHEME_SHELL
, L
"shell"},
2765 { URL_SCHEME_SNEWS
, L
"snews"},
2766 { URL_SCHEME_LOCAL
, L
"local"},
2767 { URL_SCHEME_JAVASCRIPT
, L
"javascript"},
2768 { URL_SCHEME_VBSCRIPT
, L
"vbscript"},
2769 { URL_SCHEME_ABOUT
, L
"about"},
2770 { URL_SCHEME_RES
, L
"res"},
2773 static DWORD
get_scheme_code(const WCHAR
*scheme
, DWORD scheme_len
)
2777 for (i
= 0; i
< ARRAY_SIZE(url_schemes
); ++i
)
2779 if (scheme_len
== lstrlenW(url_schemes
[i
].scheme_name
)
2780 && !wcsnicmp(scheme
, url_schemes
[i
].scheme_name
, scheme_len
))
2781 return url_schemes
[i
].scheme_number
;
2784 return URL_SCHEME_UNKNOWN
;
2787 HRESULT WINAPI
ParseURLA(const char *url
, PARSEDURLA
*result
)
2789 WCHAR scheme
[INTERNET_MAX_SCHEME_LENGTH
];
2790 const char *ptr
= url
;
2793 TRACE("%s, %p\n", wine_dbgstr_a(url
), result
);
2795 if (result
->cbSize
!= sizeof(*result
))
2796 return E_INVALIDARG
;
2798 while (*ptr
&& (isalnum( *ptr
) || *ptr
== '-' || *ptr
== '+' || *ptr
== '.'))
2801 if (*ptr
!= ':' || ptr
<= url
+ 1)
2803 result
->pszProtocol
= NULL
;
2804 return URL_E_INVALID_SYNTAX
;
2807 result
->pszProtocol
= url
;
2808 result
->cchProtocol
= ptr
- url
;
2809 result
->pszSuffix
= ptr
+ 1;
2810 result
->cchSuffix
= strlen(result
->pszSuffix
);
2812 len
= MultiByteToWideChar(CP_ACP
, 0, url
, ptr
- url
, scheme
, ARRAY_SIZE(scheme
));
2813 result
->nScheme
= get_scheme_code(scheme
, len
);
2818 HRESULT WINAPI
ParseURLW(const WCHAR
*url
, PARSEDURLW
*result
)
2820 const WCHAR
*ptr
= url
;
2822 TRACE("%s, %p\n", wine_dbgstr_w(url
), result
);
2824 if (result
->cbSize
!= sizeof(*result
))
2825 return E_INVALIDARG
;
2827 while (*ptr
&& (isalnum(*ptr
) || *ptr
== '-' || *ptr
== '+' || *ptr
== '.'))
2830 if (*ptr
!= ':' || ptr
<= url
+ 1)
2832 result
->pszProtocol
= NULL
;
2833 return URL_E_INVALID_SYNTAX
;
2836 result
->pszProtocol
= url
;
2837 result
->cchProtocol
= ptr
- url
;
2838 result
->pszSuffix
= ptr
+ 1;
2839 result
->cchSuffix
= lstrlenW(result
->pszSuffix
);
2840 result
->nScheme
= get_scheme_code(url
, ptr
- url
);
2845 HRESULT WINAPI
UrlUnescapeA(char *url
, char *unescaped
, DWORD
*unescaped_len
, DWORD flags
)
2847 BOOL stop_unescaping
= FALSE
;
2853 TRACE("%s, %p, %p, %#lx\n", wine_dbgstr_a(url
), unescaped
, unescaped_len
, flags
);
2856 return E_INVALIDARG
;
2858 if (flags
& URL_UNESCAPE_INPLACE
)
2862 if (!unescaped
|| !unescaped_len
) return E_INVALIDARG
;
2866 for (src
= url
, needed
= 0; *src
; src
++, needed
++)
2868 if (flags
& URL_DONT_UNESCAPE_EXTRA_INFO
&& (*src
== '#' || *src
== '?'))
2870 stop_unescaping
= TRUE
;
2873 else if (*src
== '%' && isxdigit(*(src
+ 1)) && isxdigit(*(src
+ 2)) && !stop_unescaping
)
2877 memcpy(buf
, src
+ 1, 2);
2879 ih
= strtol(buf
, NULL
, 16);
2881 src
+= 2; /* Advance to end of escape */
2886 if (flags
& URL_UNESCAPE_INPLACE
|| needed
< *unescaped_len
)
2890 if (flags
& URL_UNESCAPE_INPLACE
|| needed
< *unescaped_len
)
2897 needed
++; /* add one for the '\0' */
2901 if (!(flags
& URL_UNESCAPE_INPLACE
))
2902 *unescaped_len
= needed
;
2905 TRACE("result %s\n", flags
& URL_UNESCAPE_INPLACE
? wine_dbgstr_a(url
) : wine_dbgstr_a(unescaped
));
2910 static int get_utf8_len(unsigned char code
)
2914 else if ((code
& 0xe0) == 0xc0)
2916 else if ((code
& 0xf0) == 0xe0)
2918 else if ((code
& 0xf8) == 0xf0)
2923 HRESULT WINAPI
UrlUnescapeW(WCHAR
*url
, WCHAR
*unescaped
, DWORD
*unescaped_len
, DWORD flags
)
2925 WCHAR
*dst
, next
, utf16_buf
[4];
2926 BOOL stop_unescaping
= FALSE
;
2927 int utf8_len
, utf16_len
, i
;
2933 TRACE("%s, %p, %p, %#lx\n", wine_dbgstr_w(url
), unescaped
, unescaped_len
, flags
);
2936 return E_INVALIDARG
;
2938 if (flags
& URL_UNESCAPE_INPLACE
)
2942 if (!unescaped
|| !unescaped_len
) return E_INVALIDARG
;
2946 for (src
= url
, needed
= 0; *src
; src
++, needed
++)
2949 if (flags
& URL_DONT_UNESCAPE_EXTRA_INFO
&& (*src
== '#' || *src
== '?'))
2951 stop_unescaping
= TRUE
;
2954 else if (*src
== '%' && isxdigit(*(src
+ 1)) && isxdigit(*(src
+ 2)) && !stop_unescaping
)
2957 WCHAR buf
[5] = L
"0x";
2959 memcpy(buf
+ 2, src
+ 1, 2*sizeof(WCHAR
));
2961 StrToIntExW(buf
, STIF_SUPPORT_HEX
, &ih
);
2962 src
+= 2; /* Advance to end of escape */
2964 if (flags
& URL_UNESCAPE_AS_UTF8
)
2967 utf8_len
= get_utf8_len(ih
);
2968 for (i
= 1; i
< utf8_len
&& *(src
+ 1) == '%' && *(src
+ 2) && *(src
+ 3); i
++)
2970 memcpy(buf
+ 2, src
+ 2, 2 * sizeof(WCHAR
));
2971 StrToIntExW(buf
, STIF_SUPPORT_HEX
, &ih
);
2972 /* Check if it is a valid continuation byte. */
2973 if ((ih
& 0xc0) == 0x80)
2982 utf16_len
= MultiByteToWideChar(CP_UTF8
, MB_ERR_INVALID_CHARS
,
2983 utf8_buf
, i
, utf16_buf
, ARRAYSIZE(utf16_buf
));
2985 needed
+= utf16_len
- 1;
2995 if (flags
& URL_UNESCAPE_INPLACE
|| needed
< *unescaped_len
)
2999 memcpy(dst
, utf16_buf
, utf16_len
* sizeof(*utf16_buf
));
3007 if (flags
& URL_UNESCAPE_INPLACE
|| needed
< *unescaped_len
)
3014 needed
++; /* add one for the '\0' */
3018 if (!(flags
& URL_UNESCAPE_INPLACE
))
3019 *unescaped_len
= needed
;
3022 TRACE("result %s\n", flags
& URL_UNESCAPE_INPLACE
? wine_dbgstr_w(url
) : wine_dbgstr_w(unescaped
));
3027 HRESULT WINAPI
PathCreateFromUrlA(const char *pszUrl
, char *pszPath
, DWORD
*pcchPath
, DWORD dwReserved
)
3029 WCHAR bufW
[MAX_PATH
];
3030 WCHAR
*pathW
= bufW
;
3031 UNICODE_STRING urlW
;
3033 DWORD lenW
= ARRAY_SIZE(bufW
), lenA
;
3035 if (!pszUrl
|| !pszPath
|| !pcchPath
|| !*pcchPath
)
3036 return E_INVALIDARG
;
3038 if(!RtlCreateUnicodeStringFromAsciiz(&urlW
, pszUrl
))
3039 return E_INVALIDARG
;
3040 if((ret
= PathCreateFromUrlW(urlW
.Buffer
, pathW
, &lenW
, dwReserved
)) == E_POINTER
) {
3041 pathW
= HeapAlloc(GetProcessHeap(), 0, lenW
* sizeof(WCHAR
));
3042 ret
= PathCreateFromUrlW(urlW
.Buffer
, pathW
, &lenW
, dwReserved
);
3045 RtlUnicodeToMultiByteSize(&lenA
, pathW
, lenW
* sizeof(WCHAR
));
3046 if(*pcchPath
> lenA
) {
3047 RtlUnicodeToMultiByteN(pszPath
, *pcchPath
- 1, &lenA
, pathW
, lenW
* sizeof(WCHAR
));
3051 *pcchPath
= lenA
+ 1;
3055 if(pathW
!= bufW
) HeapFree(GetProcessHeap(), 0, pathW
);
3056 RtlFreeUnicodeString(&urlW
);
3060 HRESULT WINAPI
PathCreateFromUrlW(const WCHAR
*url
, WCHAR
*path
, DWORD
*pcchPath
, DWORD dwReserved
)
3062 DWORD nslashes
, unescape
, len
;
3067 TRACE("%s, %p, %p, %#lx\n", wine_dbgstr_w(url
), path
, pcchPath
, dwReserved
);
3069 if (!url
|| !path
|| !pcchPath
|| !*pcchPath
)
3070 return E_INVALIDARG
;
3072 if (wcsnicmp( url
, L
"file:", 5))
3073 return E_INVALIDARG
;
3079 while (*src
== '/' || *src
== '\\')
3085 /* We need a temporary buffer so we can compute what size to ask for.
3086 * We know that the final string won't be longer than the current pszUrl
3087 * plus at most two backslashes. All the other transformations make it
3090 len
= 2 + lstrlenW(url
) + 1;
3091 if (*pcchPath
< len
)
3092 tpath
= heap_alloc(len
* sizeof(WCHAR
));
3102 /* 'file:' + escaped DOS path */
3105 /* 'file:/' + escaped DOS path */
3108 /* 'file:///' (implied localhost) + escaped DOS path */
3109 if (!is_escaped_drive_spec( src
))
3113 if (lstrlenW(src
) >= 10 && !wcsnicmp( src
, L
"localhost", 9) && (src
[9] == '/' || src
[9] == '\\'))
3115 /* 'file://localhost/' + escaped DOS path */
3118 else if (is_escaped_drive_spec( src
))
3120 /* 'file://' + unescaped DOS path */
3125 /* 'file://hostname:port/path' (where path is escaped)
3126 * or 'file:' + escaped UNC path (\\server\share\path)
3127 * The second form is clearly specific to Windows and it might
3128 * even be doing a network lookup to try to figure it out.
3130 while (*src
&& *src
!= '/' && *src
!= '\\')
3133 StrCpyNW(dst
, url
, len
+ 1);
3135 if (*src
&& is_escaped_drive_spec( src
+ 1 ))
3137 /* 'Forget' to add a trailing '/', just like Windows */
3143 /* 'file://' + unescaped UNC path (\\server\share\path) */
3145 if (is_escaped_drive_spec( src
))
3149 /* 'file:/...' + escaped UNC path (\\server\share\path) */
3153 /* Copy the remainder of the path */
3154 len
+= lstrlenW(src
);
3157 /* First do the Windows-specific path conversions */
3158 for (dst
= tpath
; *dst
; dst
++)
3159 if (*dst
== '/') *dst
= '\\';
3160 if (is_escaped_drive_spec( tpath
))
3161 tpath
[1] = ':'; /* c| -> c: */
3163 /* And only then unescape the path (i.e. escaped slashes are left as is) */
3166 hr
= UrlUnescapeW(tpath
, NULL
, &len
, URL_UNESCAPE_INPLACE
);
3169 /* When working in-place UrlUnescapeW() does not set len */
3170 len
= lstrlenW(tpath
);
3174 if (*pcchPath
< len
+ 1)
3177 *pcchPath
= len
+ 1;
3183 lstrcpyW(path
, tpath
);
3188 TRACE("Returning (%lu) %s\n", *pcchPath
, wine_dbgstr_w(path
));
3192 HRESULT WINAPI
PathCreateFromUrlAlloc(const WCHAR
*url
, WCHAR
**path
, DWORD reserved
)
3194 WCHAR pathW
[MAX_PATH
];
3199 hr
= PathCreateFromUrlW(url
, pathW
, &size
, reserved
);
3202 /* Yes, this is supposed to crash if 'path' is NULL */
3203 *path
= StrDupW(pathW
);
3209 BOOL WINAPI
PathIsURLA(const char *path
)
3214 TRACE("%s\n", wine_dbgstr_a(path
));
3216 if (!path
|| !*path
)
3220 base
.cbSize
= sizeof(base
);
3221 hr
= ParseURLA(path
, &base
);
3222 return hr
== S_OK
&& (base
.nScheme
!= URL_SCHEME_INVALID
);
3225 BOOL WINAPI
PathIsURLW(const WCHAR
*path
)
3230 TRACE("%s\n", wine_dbgstr_w(path
));
3232 if (!path
|| !*path
)
3236 base
.cbSize
= sizeof(base
);
3237 hr
= ParseURLW(path
, &base
);
3238 return hr
== S_OK
&& (base
.nScheme
!= URL_SCHEME_INVALID
);
3241 #define WINE_URL_BASH_AS_SLASH 0x01
3242 #define WINE_URL_COLLAPSE_SLASHES 0x02
3243 #define WINE_URL_ESCAPE_SLASH 0x04
3244 #define WINE_URL_ESCAPE_HASH 0x08
3245 #define WINE_URL_ESCAPE_QUESTION 0x10
3246 #define WINE_URL_STOP_ON_HASH 0x20
3247 #define WINE_URL_STOP_ON_QUESTION 0x40
3249 static BOOL
url_needs_escape(WCHAR ch
, DWORD flags
, DWORD int_flags
)
3251 if (flags
& URL_ESCAPE_SPACES_ONLY
)
3254 if ((flags
& URL_ESCAPE_PERCENT
) && (ch
== '%'))
3257 if ((flags
& URL_ESCAPE_AS_UTF8
) && (ch
>= 0x80))
3260 if (ch
<= 31 || (ch
>= 127 && ch
<= 255) )
3282 return !!(int_flags
& WINE_URL_ESCAPE_SLASH
);
3284 return !!(int_flags
& WINE_URL_ESCAPE_QUESTION
);
3286 return !!(int_flags
& WINE_URL_ESCAPE_HASH
);
3292 HRESULT WINAPI
UrlEscapeA(const char *url
, char *escaped
, DWORD
*escaped_len
, DWORD flags
)
3294 WCHAR bufW
[INTERNET_MAX_URL_LENGTH
];
3295 WCHAR
*escapedW
= bufW
;
3296 UNICODE_STRING urlW
;
3298 DWORD lenW
= ARRAY_SIZE(bufW
), lenA
;
3300 if (!escaped
|| !escaped_len
|| !*escaped_len
)
3301 return E_INVALIDARG
;
3303 if (!RtlCreateUnicodeStringFromAsciiz(&urlW
, url
))
3304 return E_INVALIDARG
;
3306 if (flags
& URL_ESCAPE_AS_UTF8
)
3308 RtlFreeUnicodeString(&urlW
);
3312 if ((hr
= UrlEscapeW(urlW
.Buffer
, escapedW
, &lenW
, flags
)) == E_POINTER
)
3314 escapedW
= heap_alloc(lenW
* sizeof(WCHAR
));
3315 hr
= UrlEscapeW(urlW
.Buffer
, escapedW
, &lenW
, flags
);
3320 RtlUnicodeToMultiByteSize(&lenA
, escapedW
, lenW
* sizeof(WCHAR
));
3321 if (*escaped_len
> lenA
)
3323 RtlUnicodeToMultiByteN(escaped
, *escaped_len
- 1, &lenA
, escapedW
, lenW
* sizeof(WCHAR
));
3325 *escaped_len
= lenA
;
3329 *escaped_len
= lenA
+ 1;
3333 if (escapedW
!= bufW
)
3334 heap_free(escapedW
);
3335 RtlFreeUnicodeString(&urlW
);
3339 HRESULT WINAPI
UrlEscapeW(const WCHAR
*url
, WCHAR
*escaped
, DWORD
*escaped_len
, DWORD flags
)
3341 DWORD needed
= 0, slashes
= 0, int_flags
;
3342 WCHAR next
[12], *dst
, *dst_ptr
;
3343 BOOL stop_escaping
= FALSE
;
3344 PARSEDURLW parsed_url
;
3349 TRACE("%p, %s, %p, %p, %#lx\n", url
, wine_dbgstr_w(url
), escaped
, escaped_len
, flags
);
3351 if (!url
|| !escaped_len
|| !escaped
|| *escaped_len
== 0)
3352 return E_INVALIDARG
;
3354 if (flags
& ~(URL_ESCAPE_SPACES_ONLY
| URL_ESCAPE_SEGMENT_ONLY
| URL_DONT_ESCAPE_EXTRA_INFO
|
3355 URL_ESCAPE_PERCENT
| URL_ESCAPE_AS_UTF8
))
3357 FIXME("Unimplemented flags: %08lx\n", flags
);
3360 dst_ptr
= dst
= heap_alloc(*escaped_len
* sizeof(WCHAR
));
3362 return E_OUTOFMEMORY
;
3365 if (flags
& URL_ESCAPE_SPACES_ONLY
)
3366 /* if SPACES_ONLY specified, reset the other controls */
3367 flags
&= ~(URL_DONT_ESCAPE_EXTRA_INFO
| URL_ESCAPE_PERCENT
| URL_ESCAPE_SEGMENT_ONLY
);
3369 /* if SPACES_ONLY *not* specified the assume DONT_ESCAPE_EXTRA_INFO */
3370 flags
|= URL_DONT_ESCAPE_EXTRA_INFO
;
3373 if (flags
& URL_ESCAPE_SEGMENT_ONLY
)
3374 int_flags
= WINE_URL_ESCAPE_QUESTION
| WINE_URL_ESCAPE_HASH
| WINE_URL_ESCAPE_SLASH
;
3377 parsed_url
.cbSize
= sizeof(parsed_url
);
3378 if (ParseURLW(url
, &parsed_url
) != S_OK
)
3379 parsed_url
.nScheme
= URL_SCHEME_INVALID
;
3381 TRACE("scheme = %d (%s)\n", parsed_url
.nScheme
, debugstr_wn(parsed_url
.pszProtocol
, parsed_url
.cchProtocol
));
3383 if (flags
& URL_DONT_ESCAPE_EXTRA_INFO
)
3384 int_flags
= WINE_URL_STOP_ON_HASH
| WINE_URL_STOP_ON_QUESTION
;
3386 switch(parsed_url
.nScheme
) {
3387 case URL_SCHEME_FILE
:
3388 int_flags
|= WINE_URL_BASH_AS_SLASH
| WINE_URL_COLLAPSE_SLASHES
| WINE_URL_ESCAPE_HASH
;
3389 int_flags
&= ~WINE_URL_STOP_ON_HASH
;
3392 case URL_SCHEME_HTTP
:
3393 case URL_SCHEME_HTTPS
:
3394 int_flags
|= WINE_URL_BASH_AS_SLASH
;
3395 if(parsed_url
.pszSuffix
[0] != '/' && parsed_url
.pszSuffix
[0] != '\\')
3396 int_flags
|= WINE_URL_ESCAPE_SLASH
;
3399 case URL_SCHEME_MAILTO
:
3400 int_flags
|= WINE_URL_ESCAPE_SLASH
| WINE_URL_ESCAPE_QUESTION
| WINE_URL_ESCAPE_HASH
;
3401 int_flags
&= ~(WINE_URL_STOP_ON_QUESTION
| WINE_URL_STOP_ON_HASH
);
3404 case URL_SCHEME_INVALID
:
3407 case URL_SCHEME_FTP
:
3409 if(parsed_url
.pszSuffix
[0] != '/')
3410 int_flags
|= WINE_URL_ESCAPE_SLASH
;
3415 for (src
= url
; *src
; )
3420 if ((int_flags
& WINE_URL_COLLAPSE_SLASHES
) && src
== url
+ parsed_url
.cchProtocol
+ 1)
3422 while (cur
== '/' || cur
== '\\')
3427 if (slashes
== 2 && !wcsnicmp(src
, L
"localhost", 9)) { /* file://localhost/ -> file:/// */
3428 if(src
[9] == '/' || src
[9] == '\\') src
+= 10;
3436 next
[0] = next
[1] = next
[2] = '/';
3443 next
[0] = next
[1] = '/';
3450 if (cur
== '#' && (int_flags
& WINE_URL_STOP_ON_HASH
))
3451 stop_escaping
= TRUE
;
3453 if (cur
== '?' && (int_flags
& WINE_URL_STOP_ON_QUESTION
))
3454 stop_escaping
= TRUE
;
3456 if (cur
== '\\' && (int_flags
& WINE_URL_BASH_AS_SLASH
) && !stop_escaping
) cur
= '/';
3458 if (url_needs_escape(cur
, flags
, int_flags
) && !stop_escaping
)
3460 if (flags
& URL_ESCAPE_AS_UTF8
)
3464 if ((cur
>= 0xd800 && cur
<= 0xdfff) && (src
[1] >= 0xdc00 && src
[1] <= 0xdfff))
3466 len
= WideCharToMultiByte(CP_UTF8
, WC_ERR_INVALID_CHARS
, src
, 2, utf
, sizeof(utf
), NULL
, NULL
);
3470 len
= WideCharToMultiByte(CP_UTF8
, WC_ERR_INVALID_CHARS
, &cur
, 1, utf
, sizeof(utf
), NULL
, NULL
);
3480 for (i
= 0; i
< len
; ++i
)
3483 next
[i
*3+1] = hexDigits
[(utf
[i
] >> 4) & 0xf];
3484 next
[i
*3+2] = hexDigits
[utf
[i
] & 0xf];
3491 next
[1] = hexDigits
[(cur
>> 4) & 0xf];
3492 next
[2] = hexDigits
[cur
& 0xf];
3504 if (needed
+ len
<= *escaped_len
)
3506 memcpy(dst
, next
, len
*sizeof(WCHAR
));
3512 if (needed
< *escaped_len
)
3515 memcpy(escaped
, dst_ptr
, (needed
+1)*sizeof(WCHAR
));
3520 needed
++; /* add one for the '\0' */
3523 *escaped_len
= needed
;
3529 HRESULT WINAPI
UrlCanonicalizeA(const char *src_url
, char *canonicalized
, DWORD
*canonicalized_len
, DWORD flags
)
3531 LPWSTR url
, canonical
;
3534 TRACE("%s, %p, %p, %#lx\n", wine_dbgstr_a(src_url
), canonicalized
, canonicalized_len
, flags
);
3536 if (!src_url
|| !canonicalized
|| !canonicalized_len
|| !*canonicalized_len
)
3537 return E_INVALIDARG
;
3539 url
= heap_strdupAtoW(src_url
);
3540 canonical
= heap_alloc(*canonicalized_len
* sizeof(WCHAR
));
3541 if (!url
|| !canonical
)
3544 heap_free(canonical
);
3545 return E_OUTOFMEMORY
;
3548 hr
= UrlCanonicalizeW(url
, canonical
, canonicalized_len
, flags
);
3550 WideCharToMultiByte(CP_ACP
, 0, canonical
, -1, canonicalized
, *canonicalized_len
+ 1, NULL
, NULL
);
3553 heap_free(canonical
);
3557 HRESULT WINAPI
UrlCanonicalizeW(const WCHAR
*src_url
, WCHAR
*canonicalized
, DWORD
*canonicalized_len
, DWORD flags
)
3559 WCHAR
*url_copy
, *url
, *wk2
, *mp
, *mp2
;
3560 DWORD nByteLen
, nLen
, nWkLen
;
3561 const WCHAR
*wk1
, *root
;
3568 TRACE("%s, %p, %p, %#lx\n", wine_dbgstr_w(src_url
), canonicalized
, canonicalized_len
, flags
);
3570 if (!src_url
|| !canonicalized
|| !canonicalized_len
|| !*canonicalized_len
)
3571 return E_INVALIDARG
;
3579 /* Remove '\t' characters from URL */
3580 nByteLen
= (lstrlenW(src_url
) + 1) * sizeof(WCHAR
); /* length in bytes */
3581 url
= HeapAlloc(GetProcessHeap(), 0, nByteLen
);
3583 return E_OUTOFMEMORY
;
3594 /* Allocate memory for simplified URL (before escaping) */
3595 nByteLen
= (wk2
-url
)*sizeof(WCHAR
);
3596 url_copy
= heap_alloc(nByteLen
+ sizeof(L
"file:///"));
3600 return E_OUTOFMEMORY
;
3603 is_file_url
= !wcsncmp(url
, L
"file:", 5);
3605 if ((nByteLen
>= 5*sizeof(WCHAR
) && !wcsncmp(url
, L
"http:", 5)) || is_file_url
)
3608 if ((flags
& (URL_FILE_USE_PATHURL
| URL_WININET_COMPATIBILITY
)) && is_file_url
)
3611 if (nByteLen
>= 4*sizeof(WCHAR
) && !wcsncmp(url
, L
"res:", 4))
3613 flags
&= ~URL_FILE_USE_PATHURL
;
3620 * 1 have 2[+] alnum 2,3
3621 * 2 have scheme (found :) 4,6,3
3622 * 3 failed (no location)
3624 * 5 have 1[+] alnum 6,3
3625 * 6 have location (found /) save root location
3635 lstrcpyW(wk2
, L
"file:///");
3636 wk2
+= lstrlenW(wk2
);
3637 if (flags
& (URL_FILE_USE_PATHURL
| URL_WININET_COMPATIBILITY
))
3643 flags
|= URL_ESCAPE_UNSAFE
;
3647 else if (url
[0] == '/')
3658 if (!isalnum(*wk1
)) {state
= 3; break;}
3660 if (!isalnum(*wk1
)) {state
= 3; break;}
3666 if (*wk1
++ == ':') state
= 2;
3670 if (*wk1
!= '/') {state
= 6; break;}
3672 if ((flags
& URL_FILE_USE_PATHURL
) && nByteLen
>= 9*sizeof(WCHAR
) && is_file_url
3673 && !wcsncmp(wk1
, L
"localhost", 9))
3676 while (*wk1
== '\\' && (flags
& URL_FILE_USE_PATHURL
))
3680 if (*wk1
== '/' && (flags
& URL_FILE_USE_PATHURL
))
3682 else if (is_file_url
)
3684 const WCHAR
*body
= wk1
;
3686 while (*body
== '/')
3689 if (is_drive_spec( body
))
3691 if (!(flags
& (URL_WININET_COMPATIBILITY
| URL_FILE_USE_PATHURL
)))
3701 if (flags
& URL_WININET_COMPATIBILITY
)
3703 if (*wk1
== '/' && *(wk1
+ 1) != '/')
3715 if (*wk1
== '/' && *(wk1
+1) != '/')
3729 nWkLen
= lstrlenW(wk1
);
3730 memcpy(wk2
, wk1
, (nWkLen
+ 1) * sizeof(WCHAR
));
3739 if (*mp
== '/' || *mp
== '\\')
3746 if (!isalnum(*wk1
) && (*wk1
!= '-') && (*wk1
!= '.') && (*wk1
!= ':'))
3751 while (isalnum(*wk1
) || (*wk1
== '-') || (*wk1
== '.') || (*wk1
== ':'))
3763 if (*wk1
!= '/' && *wk1
!= '\\')
3768 while (*wk1
== '/' || *wk1
== '\\')
3779 if (flags
& URL_DONT_SIMPLIFY
)
3785 /* Now at root location, cannot back up any more. */
3786 /* "root" will point at the '/' */
3791 mp
= wcschr(wk1
, '/');
3792 mp2
= wcschr(wk1
, '\\');
3793 if (mp2
&& (!mp
|| mp2
< mp
))
3797 nWkLen
= lstrlenW(wk1
);
3798 memcpy(wk2
, wk1
, (nWkLen
+ 1) * sizeof(WCHAR
));
3806 memcpy(wk2
, wk1
, nLen
* sizeof(WCHAR
));
3818 TRACE("found '/.'\n");
3819 if (wk1
[1] == '/' || wk1
[1] == '\\')
3821 /* case of /./ -> skip the ./ */
3824 else if (wk1
[1] == '.' && (wk1
[2] == '/' || wk1
[2] == '\\' || wk1
[2] == '?'
3825 || wk1
[2] == '#' || !wk1
[2]))
3827 /* case /../ -> need to backup wk2 */
3828 TRACE("found '/../'\n");
3829 *(wk2
-1) = '\0'; /* set end of string */
3830 mp
= wcsrchr(root
, '/');
3831 mp2
= wcsrchr(root
, '\\');
3832 if (mp2
&& (!mp
|| mp2
< mp
))
3834 if (mp
&& (mp
>= root
))
3836 /* found valid backup point */
3838 if(wk1
[2] != '/' && wk1
[2] != '\\')
3845 /* did not find point, restore '/' */
3857 FIXME("how did we get here - state=%d\n", state
);
3858 heap_free(url_copy
);
3860 return E_INVALIDARG
;
3863 TRACE("Simplified, orig <%s>, simple <%s>\n", wine_dbgstr_w(src_url
), wine_dbgstr_w(url_copy
));
3865 nLen
= lstrlenW(url_copy
);
3866 while ((nLen
> 0) && ((url_copy
[nLen
-1] <= ' ')))
3869 if ((flags
& URL_UNESCAPE
) ||
3870 ((flags
& URL_FILE_USE_PATHURL
) && nByteLen
>= 5*sizeof(WCHAR
) && !wcsncmp(url
, L
"file:", 5)))
3872 UrlUnescapeW(url_copy
, NULL
, &nLen
, URL_UNESCAPE_INPLACE
);
3875 escape_flags
= flags
& (URL_ESCAPE_UNSAFE
| URL_ESCAPE_SPACES_ONLY
| URL_ESCAPE_PERCENT
|
3876 URL_DONT_ESCAPE_EXTRA_INFO
| URL_ESCAPE_SEGMENT_ONLY
);
3880 escape_flags
&= ~URL_ESCAPE_UNSAFE
;
3881 hr
= UrlEscapeW(url_copy
, canonicalized
, canonicalized_len
, escape_flags
);
3885 /* No escaping needed, just copy the string */
3886 nLen
= lstrlenW(url_copy
);
3887 if (nLen
< *canonicalized_len
)
3888 memcpy(canonicalized
, url_copy
, (nLen
+ 1)*sizeof(WCHAR
));
3894 *canonicalized_len
= nLen
;
3897 heap_free(url_copy
);
3901 TRACE("result %s\n", wine_dbgstr_w(canonicalized
));
3906 HRESULT WINAPI
UrlApplySchemeA(const char *url
, char *out
, DWORD
*out_len
, DWORD flags
)
3912 TRACE("%s, %p, %p:out size %ld, %#lx\n", wine_dbgstr_a(url
), out
, out_len
, out_len
? *out_len
: 0, flags
);
3914 if (!url
|| !out
|| !out_len
)
3915 return E_INVALIDARG
;
3917 inW
= heap_alloc(2 * INTERNET_MAX_URL_LENGTH
* sizeof(WCHAR
));
3918 outW
= inW
+ INTERNET_MAX_URL_LENGTH
;
3920 MultiByteToWideChar(CP_ACP
, 0, url
, -1, inW
, INTERNET_MAX_URL_LENGTH
);
3921 len
= INTERNET_MAX_URL_LENGTH
;
3923 hr
= UrlApplySchemeW(inW
, outW
, &len
, flags
);
3930 len
= WideCharToMultiByte(CP_ACP
, 0, outW
, -1, NULL
, 0, NULL
, NULL
);
3937 WideCharToMultiByte(CP_ACP
, 0, outW
, -1, out
, *out_len
, NULL
, NULL
);
3946 static HRESULT
url_guess_scheme(const WCHAR
*url
, WCHAR
*out
, DWORD
*out_len
)
3948 WCHAR reg_path
[MAX_PATH
], value
[MAX_PATH
], data
[MAX_PATH
];
3949 DWORD value_len
, data_len
, dwType
, i
;
3955 MultiByteToWideChar(CP_ACP
, 0,
3956 "Software\\Microsoft\\Windows\\CurrentVersion\\URL\\Prefixes", -1, reg_path
, MAX_PATH
);
3957 RegOpenKeyExW(HKEY_LOCAL_MACHINE
, reg_path
, 0, 1, &newkey
);
3959 while (value_len
= data_len
= MAX_PATH
,
3960 RegEnumValueW(newkey
, index
, value
, &value_len
, 0, &dwType
, (LPVOID
)data
, &data_len
) == 0)
3962 TRACE("guess %d %s is %s\n", index
, wine_dbgstr_w(value
), wine_dbgstr_w(data
));
3965 for (i
= 0; i
< value_len
; ++i
)
3969 /* remember that TRUE is not-equal */
3970 j
= ChrCmpIW(Wxx
, Wyy
);
3973 if ((i
== value_len
) && !j
)
3975 if (lstrlenW(data
) + lstrlenW(url
) + 1 > *out_len
)
3977 *out_len
= lstrlenW(data
) + lstrlenW(url
) + 1;
3978 RegCloseKey(newkey
);
3981 lstrcpyW(out
, data
);
3983 *out_len
= lstrlenW(out
);
3984 TRACE("matched and set to %s\n", wine_dbgstr_w(out
));
3985 RegCloseKey(newkey
);
3990 RegCloseKey(newkey
);
3994 static HRESULT
url_create_from_path(const WCHAR
*path
, WCHAR
*url
, DWORD
*url_len
)
3996 PARSEDURLW parsed_url
;
4001 parsed_url
.cbSize
= sizeof(parsed_url
);
4002 if (ParseURLW(path
, &parsed_url
) == S_OK
)
4004 if (parsed_url
.nScheme
!= URL_SCHEME_INVALID
&& parsed_url
.cchProtocol
> 1)
4006 needed
= lstrlenW(path
);
4007 if (needed
>= *url_len
)
4009 *url_len
= needed
+ 1;
4020 new_url
= heap_alloc((lstrlenW(path
) + 9) * sizeof(WCHAR
)); /* "file:///" + path length + 1 */
4021 lstrcpyW(new_url
, L
"file:");
4022 if (is_drive_spec( path
)) lstrcatW(new_url
, L
"///");
4023 lstrcatW(new_url
, path
);
4024 hr
= UrlEscapeW(new_url
, url
, url_len
, URL_ESCAPE_PERCENT
);
4029 static HRESULT
url_apply_default_scheme(const WCHAR
*url
, WCHAR
*out
, DWORD
*length
)
4031 DWORD data_len
, dwType
;
4032 WCHAR data
[MAX_PATH
];
4035 /* get and prepend default */
4036 RegOpenKeyExW(HKEY_LOCAL_MACHINE
, L
"Software\\Microsoft\\Windows\\CurrentVersion\\URL\\DefaultPrefix",
4038 data_len
= sizeof(data
);
4039 RegQueryValueExW(newkey
, NULL
, 0, &dwType
, (BYTE
*)data
, &data_len
);
4040 RegCloseKey(newkey
);
4041 if (lstrlenW(data
) + lstrlenW(url
) + 1 > *length
)
4043 *length
= lstrlenW(data
) + lstrlenW(url
) + 1;
4046 lstrcpyW(out
, data
);
4048 *length
= lstrlenW(out
);
4049 TRACE("used default %s\n", wine_dbgstr_w(out
));
4053 HRESULT WINAPI
UrlApplySchemeW(const WCHAR
*url
, WCHAR
*out
, DWORD
*length
, DWORD flags
)
4055 PARSEDURLW in_scheme
;
4059 TRACE("%s, %p, %p:out size %ld, %#lx\n", wine_dbgstr_w(url
), out
, length
, length
? *length
: 0, flags
);
4061 if (!url
|| !out
|| !length
)
4062 return E_INVALIDARG
;
4064 if (flags
& URL_APPLY_GUESSFILE
)
4066 if ((*length
> 1 && ':' == url
[1]) || PathIsUNCW(url
))
4069 hr
= url_create_from_path(url
, out
, &res1
);
4070 if (hr
== S_OK
|| hr
== E_POINTER
)
4075 else if (hr
== S_FALSE
)
4082 in_scheme
.cbSize
= sizeof(in_scheme
);
4083 /* See if the base has a scheme */
4084 res1
= ParseURLW(url
, &in_scheme
);
4087 /* no scheme in input, need to see if we need to guess */
4088 if (flags
& URL_APPLY_GUESSSCHEME
)
4090 if ((hr
= url_guess_scheme(url
, out
, length
)) != E_FAIL
)
4095 /* If we are here, then either invalid scheme,
4096 * or no scheme and can't/failed guess.
4098 if ((((res1
== 0) && (flags
& URL_APPLY_FORCEAPPLY
)) || ((res1
!= 0)) ) && (flags
& URL_APPLY_DEFAULT
))
4099 return url_apply_default_scheme(url
, out
, length
);
4104 INT WINAPI
UrlCompareA(const char *url1
, const char *url2
, BOOL ignore_slash
)
4106 INT ret
, len
, len1
, len2
;
4109 return strcmp(url1
, url2
);
4110 len1
= strlen(url1
);
4111 if (url1
[len1
-1] == '/') len1
--;
4112 len2
= strlen(url2
);
4113 if (url2
[len2
-1] == '/') len2
--;
4115 return strncmp(url1
, url2
, len1
);
4116 len
= min(len1
, len2
);
4117 ret
= strncmp(url1
, url2
, len
);
4118 if (ret
) return ret
;
4119 if (len1
> len2
) return 1;
4123 INT WINAPI
UrlCompareW(const WCHAR
*url1
, const WCHAR
*url2
, BOOL ignore_slash
)
4125 size_t len
, len1
, len2
;
4129 return lstrcmpW(url1
, url2
);
4130 len1
= lstrlenW(url1
);
4131 if (url1
[len1
-1] == '/') len1
--;
4132 len2
= lstrlenW(url2
);
4133 if (url2
[len2
-1] == '/') len2
--;
4135 return wcsncmp(url1
, url2
, len1
);
4136 len
= min(len1
, len2
);
4137 ret
= wcsncmp(url1
, url2
, len
);
4138 if (ret
) return ret
;
4139 if (len1
> len2
) return 1;
4143 HRESULT WINAPI
UrlFixupW(const WCHAR
*url
, WCHAR
*translatedUrl
, DWORD maxChars
)
4147 FIXME("%s, %p, %ld stub\n", wine_dbgstr_w(url
), translatedUrl
, maxChars
);
4152 srcLen
= lstrlenW(url
) + 1;
4154 /* For now just copy the URL directly */
4155 lstrcpynW(translatedUrl
, url
, (maxChars
< srcLen
) ? maxChars
: srcLen
);
4160 const char * WINAPI
UrlGetLocationA(const char *url
)
4164 base
.cbSize
= sizeof(base
);
4165 if (ParseURLA(url
, &base
) != S_OK
) return NULL
; /* invalid scheme */
4167 /* if scheme is file: then never return pointer */
4168 if (!strncmp(base
.pszProtocol
, "file", min(4, base
.cchProtocol
)))
4171 /* Look for '#' and return its addr */
4172 return strchr(base
.pszSuffix
, '#');
4175 const WCHAR
* WINAPI
UrlGetLocationW(const WCHAR
*url
)
4179 base
.cbSize
= sizeof(base
);
4180 if (ParseURLW(url
, &base
) != S_OK
) return NULL
; /* invalid scheme */
4182 /* if scheme is file: then never return pointer */
4183 if (!wcsncmp(base
.pszProtocol
, L
"file", min(4, base
.cchProtocol
)))
4186 /* Look for '#' and return its addr */
4187 return wcschr(base
.pszSuffix
, '#');
4190 HRESULT WINAPI
UrlGetPartA(const char *url
, char *out
, DWORD
*out_len
, DWORD part
, DWORD flags
)
4196 if (!url
|| !out
|| !out_len
|| !*out_len
)
4197 return E_INVALIDARG
;
4199 inW
= heap_alloc(2 * INTERNET_MAX_URL_LENGTH
* sizeof(WCHAR
));
4200 outW
= inW
+ INTERNET_MAX_URL_LENGTH
;
4202 MultiByteToWideChar(CP_ACP
, 0, url
, -1, inW
, INTERNET_MAX_URL_LENGTH
);
4204 len
= INTERNET_MAX_URL_LENGTH
;
4205 hr
= UrlGetPartW(inW
, outW
, &len
, part
, flags
);
4212 len2
= WideCharToMultiByte(CP_ACP
, 0, outW
, len
+ 1, NULL
, 0, NULL
, NULL
);
4213 if (len2
> *out_len
)
4219 len2
= WideCharToMultiByte(CP_ACP
, 0, outW
, len
+ 1, out
, *out_len
, NULL
, NULL
);
4220 *out_len
= len2
- 1;
4222 if (hr
== S_OK
&& !*out_len
) hr
= S_FALSE
;
4226 static const WCHAR
*parse_scheme( const WCHAR
*p
)
4228 while (isalnum( *p
) || *p
== '+' || *p
== '-' || *p
== '.')
4233 static const WCHAR
*parse_url_element( const WCHAR
*url
, const WCHAR
*separators
)
4237 if ((p
= wcspbrk( url
, separators
)))
4239 return url
+ wcslen( url
);
4242 static BOOL
is_slash( char c
)
4244 return c
== '/' || c
== '\\';
4247 static void parse_url( const WCHAR
*url
, struct parsed_url
*pl
)
4251 memset(pl
, 0, sizeof(*pl
));
4253 work
= parse_scheme( pl
->scheme
);
4254 if (work
< url
+ 2 || *work
!= ':') return;
4255 pl
->scheme_len
= work
- pl
->scheme
;
4257 pl
->scheme_number
= get_scheme_code(pl
->scheme
, pl
->scheme_len
);
4258 if (!is_slash( work
[0] ) || !is_slash( work
[1] ))
4260 if (pl
->scheme_number
!= URL_SCHEME_FILE
)
4261 pl
->scheme_number
= URL_SCHEME_UNKNOWN
;
4266 if (pl
->scheme_number
!= URL_SCHEME_FILE
)
4268 pl
->username
= work
;
4269 work
= parse_url_element( pl
->username
, L
":@/\\?#" );
4270 pl
->username_len
= work
- pl
->username
;
4273 pl
->password
= work
+ 1;
4274 work
= parse_url_element( pl
->password
, L
"@/\\?#" );
4275 pl
->password_len
= work
- pl
->password
;
4282 /* what we just parsed must be the hostname and port
4283 * so reset pointers and clear then let it parse */
4284 pl
->username_len
= pl
->password_len
= 0;
4285 work
= pl
->username
;
4286 pl
->username
= pl
->password
= 0;
4289 else if (*work
== '@')
4292 pl
->password_len
= 0;
4298 /* what was parsed was hostname, so reset pointers and let it parse */
4299 pl
->username_len
= pl
->password_len
= 0;
4300 work
= pl
->username
;
4301 pl
->username
= pl
->password
= 0;
4305 pl
->hostname
= work
;
4306 if (pl
->scheme_number
== URL_SCHEME_FILE
)
4308 work
= parse_url_element( pl
->hostname
, L
"/\\?#" );
4309 pl
->hostname_len
= work
- pl
->hostname
;
4310 if (pl
->hostname_len
>= 2 && pl
->hostname
[1] == ':')
4311 pl
->hostname_len
= 0;
4315 work
= parse_url_element( pl
->hostname
, L
":/\\?#" );
4316 pl
->hostname_len
= work
- pl
->hostname
;
4320 pl
->port
= work
+ 1;
4321 work
= parse_url_element( pl
->port
, L
"/\\?#" );
4322 pl
->port_len
= work
- pl
->port
;
4326 if ((pl
->query
= wcschr( work
, '?' )))
4329 pl
->query_len
= lstrlenW(pl
->query
);
4333 HRESULT WINAPI
UrlGetPartW(const WCHAR
*url
, WCHAR
*out
, DWORD
*out_len
, DWORD part
, DWORD flags
)
4335 LPCWSTR addr
, schaddr
;
4336 struct parsed_url pl
;
4337 DWORD size
, schsize
;
4339 TRACE("%s, %p, %p(%ld), %#lx, %#lx\n", wine_dbgstr_w(url
), out
, out_len
, *out_len
, part
, flags
);
4341 if (!url
|| !out
|| !out_len
|| !*out_len
)
4342 return E_INVALIDARG
;
4344 parse_url(url
, &pl
);
4346 switch (pl
.scheme_number
)
4348 case URL_SCHEME_FTP
:
4349 case URL_SCHEME_GOPHER
:
4350 case URL_SCHEME_HTTP
:
4351 case URL_SCHEME_HTTPS
:
4352 case URL_SCHEME_TELNET
:
4353 case URL_SCHEME_NEWS
:
4354 case URL_SCHEME_NNTP
:
4355 case URL_SCHEME_SNEWS
:
4358 case URL_SCHEME_FILE
:
4359 if (part
!= URL_PART_SCHEME
&& part
!= URL_PART_QUERY
&& part
!= URL_PART_HOSTNAME
)
4364 if (part
!= URL_PART_SCHEME
&& part
!= URL_PART_QUERY
)
4370 case URL_PART_SCHEME
:
4371 flags
&= ~URL_PARTFLAG_KEEPSCHEME
;
4373 size
= pl
.scheme_len
;
4376 case URL_PART_HOSTNAME
:
4378 size
= pl
.hostname_len
;
4381 case URL_PART_USERNAME
:
4383 return E_INVALIDARG
;
4385 size
= pl
.username_len
;
4388 case URL_PART_PASSWORD
:
4390 return E_INVALIDARG
;
4392 size
= pl
.password_len
;
4397 return E_INVALIDARG
;
4402 case URL_PART_QUERY
:
4403 flags
&= ~URL_PARTFLAG_KEEPSCHEME
;
4405 size
= pl
.query_len
;
4409 return E_INVALIDARG
;
4412 if (flags
== URL_PARTFLAG_KEEPSCHEME
&& pl
.scheme_number
!= URL_SCHEME_FILE
)
4414 if (!pl
.scheme
|| !pl
.scheme_len
)
4416 schaddr
= pl
.scheme
;
4417 schsize
= pl
.scheme_len
;
4418 if (*out_len
< schsize
+ size
+ 2)
4420 *out_len
= schsize
+ size
+ 2;
4423 memcpy(out
, schaddr
, schsize
*sizeof(WCHAR
));
4425 memcpy(out
+ schsize
+1, addr
, size
*sizeof(WCHAR
));
4426 out
[schsize
+1+size
] = 0;
4427 *out_len
= schsize
+ 1 + size
;
4431 if (*out_len
< size
+ 1)
4433 *out_len
= size
+ 1;
4437 if (part
== URL_PART_SCHEME
)
4441 for (i
= 0; i
< size
; ++i
)
4442 out
[i
] = tolower( addr
[i
] );
4446 memcpy( out
, addr
, size
* sizeof(WCHAR
) );
4451 TRACE("len=%ld %s\n", *out_len
, wine_dbgstr_w(out
));
4456 BOOL WINAPI
UrlIsA(const char *url
, URLIS Urlis
)
4461 TRACE("%s, %d\n", debugstr_a(url
), Urlis
);
4469 base
.cbSize
= sizeof(base
);
4470 if (ParseURLA(url
, &base
) != S_OK
) return FALSE
; /* invalid scheme */
4471 switch (base
.nScheme
)
4473 case URL_SCHEME_MAILTO
:
4474 case URL_SCHEME_SHELL
:
4475 case URL_SCHEME_JAVASCRIPT
:
4476 case URL_SCHEME_VBSCRIPT
:
4477 case URL_SCHEME_ABOUT
:
4483 return (CompareStringA(LOCALE_INVARIANT
, NORM_IGNORECASE
, url
, 5, "file:", 5) == CSTR_EQUAL
);
4485 case URLIS_DIRECTORY
:
4486 last
= url
+ strlen(url
) - 1;
4487 return (last
>= url
&& (*last
== '/' || *last
== '\\' ));
4490 return PathIsURLA(url
);
4492 case URLIS_NOHISTORY
:
4493 case URLIS_APPLIABLE
:
4494 case URLIS_HASQUERY
:
4496 FIXME("(%s %d): stub\n", debugstr_a(url
), Urlis
);
4502 BOOL WINAPI
UrlIsW(const WCHAR
*url
, URLIS Urlis
)
4507 TRACE("%s, %d\n", debugstr_w(url
), Urlis
);
4515 base
.cbSize
= sizeof(base
);
4516 if (ParseURLW(url
, &base
) != S_OK
) return FALSE
; /* invalid scheme */
4517 switch (base
.nScheme
)
4519 case URL_SCHEME_MAILTO
:
4520 case URL_SCHEME_SHELL
:
4521 case URL_SCHEME_JAVASCRIPT
:
4522 case URL_SCHEME_VBSCRIPT
:
4523 case URL_SCHEME_ABOUT
:
4529 return !wcsnicmp( url
, L
"file:", 5 );
4531 case URLIS_DIRECTORY
:
4532 last
= url
+ lstrlenW(url
) - 1;
4533 return (last
>= url
&& (*last
== '/' || *last
== '\\'));
4536 return PathIsURLW(url
);
4538 case URLIS_NOHISTORY
:
4539 case URLIS_APPLIABLE
:
4540 case URLIS_HASQUERY
:
4542 FIXME("(%s %d): stub\n", debugstr_w(url
), Urlis
);
4548 BOOL WINAPI
UrlIsOpaqueA(const char *url
)
4550 return UrlIsA(url
, URLIS_OPAQUE
);
4553 BOOL WINAPI
UrlIsOpaqueW(const WCHAR
*url
)
4555 return UrlIsW(url
, URLIS_OPAQUE
);
4558 BOOL WINAPI
UrlIsNoHistoryA(const char *url
)
4560 return UrlIsA(url
, URLIS_NOHISTORY
);
4563 BOOL WINAPI
UrlIsNoHistoryW(const WCHAR
*url
)
4565 return UrlIsW(url
, URLIS_NOHISTORY
);
4568 HRESULT WINAPI
UrlCreateFromPathA(const char *path
, char *url
, DWORD
*url_len
, DWORD reserved
)
4570 WCHAR bufW
[INTERNET_MAX_URL_LENGTH
];
4571 DWORD lenW
= ARRAY_SIZE(bufW
), lenA
;
4572 UNICODE_STRING pathW
;
4576 if (!RtlCreateUnicodeStringFromAsciiz(&pathW
, path
))
4577 return E_INVALIDARG
;
4579 if ((hr
= UrlCreateFromPathW(pathW
.Buffer
, urlW
, &lenW
, reserved
)) == E_POINTER
)
4581 urlW
= heap_alloc(lenW
* sizeof(WCHAR
));
4582 hr
= UrlCreateFromPathW(pathW
.Buffer
, urlW
, &lenW
, reserved
);
4587 RtlUnicodeToMultiByteSize(&lenA
, urlW
, lenW
* sizeof(WCHAR
));
4588 if (*url_len
> lenA
)
4590 RtlUnicodeToMultiByteN(url
, *url_len
- 1, &lenA
, urlW
, lenW
* sizeof(WCHAR
));
4596 *url_len
= lenA
+ 1;
4602 RtlFreeUnicodeString(&pathW
);
4606 HRESULT WINAPI
UrlCreateFromPathW(const WCHAR
*path
, WCHAR
*url
, DWORD
*url_len
, DWORD reserved
)
4610 TRACE("%s, %p, %p, %#lx\n", debugstr_w(path
), url
, url_len
, reserved
);
4612 if (reserved
|| !url
|| !url_len
)
4613 return E_INVALIDARG
;
4615 hr
= url_create_from_path(path
, url
, url_len
);
4617 lstrcpyW(url
, path
);
4622 HRESULT WINAPI
UrlCombineA(const char *base
, const char *relative
, char *combined
, DWORD
*combined_len
, DWORD flags
)
4624 WCHAR
*baseW
, *relativeW
, *combinedW
;
4628 TRACE("%s, %s, %ld, %#lx\n", debugstr_a(base
), debugstr_a(relative
), combined_len
? *combined_len
: 0, flags
);
4630 if (!base
|| !relative
|| !combined_len
)
4631 return E_INVALIDARG
;
4633 baseW
= heap_alloc(3 * INTERNET_MAX_URL_LENGTH
* sizeof(WCHAR
));
4634 relativeW
= baseW
+ INTERNET_MAX_URL_LENGTH
;
4635 combinedW
= relativeW
+ INTERNET_MAX_URL_LENGTH
;
4637 MultiByteToWideChar(CP_ACP
, 0, base
, -1, baseW
, INTERNET_MAX_URL_LENGTH
);
4638 MultiByteToWideChar(CP_ACP
, 0, relative
, -1, relativeW
, INTERNET_MAX_URL_LENGTH
);
4639 len
= *combined_len
;
4641 hr
= UrlCombineW(baseW
, relativeW
, combined
? combinedW
: NULL
, &len
, flags
);
4644 *combined_len
= len
;
4649 len2
= WideCharToMultiByte(CP_ACP
, 0, combinedW
, len
, NULL
, 0, NULL
, NULL
);
4650 if (len2
> *combined_len
)
4652 *combined_len
= len2
;
4656 WideCharToMultiByte(CP_ACP
, 0, combinedW
, len
+1, combined
, *combined_len
+ 1, NULL
, NULL
);
4657 *combined_len
= len2
;
4662 HRESULT WINAPI
UrlCombineW(const WCHAR
*baseW
, const WCHAR
*relativeW
, WCHAR
*combined
, DWORD
*combined_len
, DWORD flags
)
4664 DWORD i
, len
, process_case
= 0, myflags
, sizeloc
= 0;
4665 LPWSTR work
, preliminary
, mbase
, mrelative
;
4666 PARSEDURLW base
, relative
;
4669 TRACE("%s, %s, %ld, %#lx\n", debugstr_w(baseW
), debugstr_w(relativeW
), combined_len
? *combined_len
: 0, flags
);
4671 if (!baseW
|| !relativeW
|| !combined_len
)
4672 return E_INVALIDARG
;
4674 base
.cbSize
= sizeof(base
);
4675 relative
.cbSize
= sizeof(relative
);
4677 /* Get space for duplicates of the input and the output */
4678 preliminary
= heap_alloc(3 * INTERNET_MAX_URL_LENGTH
* sizeof(WCHAR
));
4679 mbase
= preliminary
+ INTERNET_MAX_URL_LENGTH
;
4680 mrelative
= mbase
+ INTERNET_MAX_URL_LENGTH
;
4681 *preliminary
= '\0';
4683 /* Canonicalize the base input prior to looking for the scheme */
4684 myflags
= flags
& (URL_DONT_SIMPLIFY
| URL_UNESCAPE
);
4685 len
= INTERNET_MAX_URL_LENGTH
;
4686 UrlCanonicalizeW(baseW
, mbase
, &len
, myflags
);
4688 /* Canonicalize the relative input prior to looking for the scheme */
4689 len
= INTERNET_MAX_URL_LENGTH
;
4690 UrlCanonicalizeW(relativeW
, mrelative
, &len
, myflags
);
4692 /* See if the base has a scheme */
4693 if (ParseURLW(mbase
, &base
) != S_OK
)
4695 /* If base has no scheme return relative. */
4696 TRACE("no scheme detected in Base\n");
4701 BOOL manual_search
= FALSE
;
4703 work
= (LPWSTR
)base
.pszProtocol
;
4704 for (i
= 0; i
< base
.cchProtocol
; ++i
)
4705 work
[i
] = RtlDowncaseUnicodeChar(work
[i
]);
4707 /* mk is a special case */
4708 if (base
.nScheme
== URL_SCHEME_MK
)
4710 WCHAR
*ptr
= wcsstr(base
.pszSuffix
, L
"::");
4716 delta
= ptr
-base
.pszSuffix
;
4717 base
.cchProtocol
+= delta
;
4718 base
.pszSuffix
+= delta
;
4719 base
.cchSuffix
-= delta
;
4724 /* get size of location field (if it exists) */
4725 work
= (LPWSTR
)base
.pszSuffix
;
4731 /* At this point have start of location and
4732 * it ends at next '/' or end of string.
4734 while (*work
&& (*work
!= '/')) work
++;
4735 sizeloc
= (DWORD
)(work
- base
.pszSuffix
);
4740 /* If there is a '?', then the remaining part can only contain a
4741 * query string or fragment, so start looking for the last leaf
4742 * from the '?'. Otherwise, if there is a '#' and the characters
4743 * immediately preceding it are ".htm[l]", then begin looking for
4744 * the last leaf starting from the '#'. Otherwise the '#' is not
4745 * meaningful and just start looking from the end. */
4746 if ((work
= wcspbrk(base
.pszSuffix
+ sizeloc
, L
"#?")))
4748 if (*work
== '?' || base
.nScheme
== URL_SCHEME_HTTP
|| base
.nScheme
== URL_SCHEME_HTTPS
)
4749 manual_search
= TRUE
;
4750 else if (work
- base
.pszSuffix
> 4)
4752 if (!wcsnicmp(work
- 4, L
".htm", 4)) manual_search
= TRUE
;
4755 if (!manual_search
&& work
- base
.pszSuffix
> 5)
4757 if (!wcsnicmp(work
- 5, L
".html", 5)) manual_search
= TRUE
;
4763 /* search backwards starting from the current position */
4764 while (*work
!= '/' && work
> base
.pszSuffix
+ sizeloc
)
4766 base
.cchSuffix
= work
- base
.pszSuffix
+ 1;
4770 /* search backwards starting from the end of the string */
4771 work
= wcsrchr((base
.pszSuffix
+sizeloc
), '/');
4774 len
= (DWORD
)(work
- base
.pszSuffix
+ 1);
4775 base
.cchSuffix
= len
;
4778 base
.cchSuffix
= sizeloc
;
4783 * .pszSuffix points to location (starting with '//')
4784 * .cchSuffix length of location (above) and rest less the last
4786 * sizeloc length of location (above) up to but not including
4790 if (ParseURLW(mrelative
, &relative
) != S_OK
)
4792 /* No scheme in relative */
4793 TRACE("no scheme detected in Relative\n");
4794 relative
.pszSuffix
= mrelative
; /* case 3,4,5 depends on this */
4795 relative
.cchSuffix
= lstrlenW(mrelative
);
4796 if (*relativeW
== ':')
4798 /* Case that is either left alone or uses base. */
4799 if (flags
& URL_PLUGGABLE_PROTOCOL
)
4807 if (is_drive_spec( mrelative
))
4809 /* case that becomes "file:///" */
4810 lstrcpyW(preliminary
, L
"file:///");
4814 if (*mrelative
== '/' && *(mrelative
+1) == '/')
4816 /* Relative has location and the rest. */
4820 if (*mrelative
== '/')
4822 /* Relative is root to location. */
4826 if (*mrelative
== '#')
4828 if (!(work
= wcschr(base
.pszSuffix
+base
.cchSuffix
, '#')))
4829 work
= (LPWSTR
)base
.pszSuffix
+ lstrlenW(base
.pszSuffix
);
4831 memcpy(preliminary
, base
.pszProtocol
, (work
-base
.pszProtocol
)*sizeof(WCHAR
));
4832 preliminary
[work
-base
.pszProtocol
] = '\0';
4836 process_case
= (*base
.pszSuffix
== '/' || base
.nScheme
== URL_SCHEME_MK
) ? 5 : 3;
4841 work
= (LPWSTR
)relative
.pszProtocol
;
4842 for (i
= 0; i
< relative
.cchProtocol
; ++i
)
4843 work
[i
] = RtlDowncaseUnicodeChar(work
[i
]);
4846 /* Handle cases where relative has scheme. */
4847 if ((base
.cchProtocol
== relative
.cchProtocol
) && !wcsncmp(base
.pszProtocol
, relative
.pszProtocol
, base
.cchProtocol
))
4849 /* since the schemes are the same */
4850 if (*relative
.pszSuffix
== '/' && *(relative
.pszSuffix
+1) == '/')
4852 /* Relative replaces location and what follows. */
4856 if (*relative
.pszSuffix
== '/')
4858 /* Relative is root to location */
4862 /* replace either just location if base's location starts with a
4863 * slash or otherwise everything */
4864 process_case
= (*base
.pszSuffix
== '/') ? 5 : 1;
4868 if (*relative
.pszSuffix
== '/' && *(relative
.pszSuffix
+1) == '/')
4870 /* Relative replaces scheme, location, and following and handles PLUGGABLE */
4876 } while (FALSE
); /* a little trick to allow easy exit from nested if's */
4879 switch (process_case
)
4882 /* Return relative appended to whatever is in combined (which may the string "file:///" */
4883 lstrcatW(preliminary
, mrelative
);
4887 /* Relative replaces scheme and location */
4888 lstrcpyW(preliminary
, mrelative
);
4892 /* Return the base scheme with relative. Basically keeps the scheme and replaces the domain and following. */
4893 memcpy(preliminary
, base
.pszProtocol
, (base
.cchProtocol
+ 1)*sizeof(WCHAR
));
4894 work
= preliminary
+ base
.cchProtocol
+ 1;
4895 lstrcpyW(work
, relative
.pszSuffix
);
4899 /* Return the base scheme and location but everything after the location is relative. (Replace document from root on.) */
4900 memcpy(preliminary
, base
.pszProtocol
, (base
.cchProtocol
+1+sizeloc
)*sizeof(WCHAR
));
4901 work
= preliminary
+ base
.cchProtocol
+ 1 + sizeloc
;
4902 if (flags
& URL_PLUGGABLE_PROTOCOL
)
4904 lstrcpyW(work
, relative
.pszSuffix
);
4908 /* Return the base without its document (if any) and append relative after its scheme. */
4909 memcpy(preliminary
, base
.pszProtocol
, (base
.cchProtocol
+ 1 + base
.cchSuffix
)*sizeof(WCHAR
));
4910 work
= preliminary
+ base
.cchProtocol
+ 1 + base
.cchSuffix
- 1;
4913 lstrcpyW(work
, relative
.pszSuffix
);
4917 FIXME("Unexpected case %ld.\n", process_case
);
4923 /* Reuse mrelative as temp storage as it's already allocated and not needed anymore */
4924 if (*combined_len
== 0)
4926 hr
= UrlCanonicalizeW(preliminary
, mrelative
, combined_len
, flags
& ~URL_FILE_USE_PATHURL
);
4927 if (SUCCEEDED(hr
) && combined
)
4928 lstrcpyW(combined
, mrelative
);
4930 TRACE("return-%ld len=%ld, %s\n", process_case
, *combined_len
, debugstr_w(combined
));
4933 heap_free(preliminary
);
4937 HRESULT WINAPI
HashData(const unsigned char *src
, DWORD src_len
, unsigned char *dest
, DWORD dest_len
)
4939 INT src_count
= src_len
- 1, dest_count
= dest_len
- 1;
4942 return E_INVALIDARG
;
4944 while (dest_count
>= 0)
4946 dest
[dest_count
] = (dest_count
& 0xff);
4950 while (src_count
>= 0)
4952 dest_count
= dest_len
- 1;
4953 while (dest_count
>= 0)
4955 dest
[dest_count
] = hashdata_lookup
[src
[src_count
] ^ dest
[dest_count
]];
4964 HRESULT WINAPI
UrlHashA(const char *url
, unsigned char *dest
, DWORD dest_len
)
4968 HashData((const BYTE
*)url
, (int)strlen(url
), dest
, dest_len
);
4972 return E_INVALIDARG
;
4978 HRESULT WINAPI
UrlHashW(const WCHAR
*url
, unsigned char *dest
, DWORD dest_len
)
4980 char urlA
[MAX_PATH
];
4982 TRACE("%s, %p, %ld\n", debugstr_w(url
), dest
, dest_len
);
4986 WideCharToMultiByte(CP_ACP
, 0, url
, -1, urlA
, MAX_PATH
, NULL
, NULL
);
4987 HashData((const BYTE
*)urlA
, (int)strlen(urlA
), dest
, dest_len
);
4991 return E_INVALIDARG
;
4997 BOOL WINAPI
IsInternetESCEnabled(void)