Reactos

[SDK][KERNELBASE][PATHCCH] Compile a collection of PathCch libraries (#8085)

CORE-12686

This collection of libraries consist in:

- a down-level `pathcch_static` PathCch library, to be used for programs
that need to run on Windows 7 and below;

- Windows 8+ compatible libraries importing from either kernelbase.dll
(`pathcch_kernelbase`), or from the corresponding APISET
api-ms-win-core-path-l1-1-0.dll (`pathcch`, which is Win8+ PSDK
compatible).

The down-level static library is compiled by reusing the newly-introduced
kernelbase's path.c, instead of using a specific pathcch.c.
Unrelated functions are excluded from compilation by putting them into
`#ifndef STATIC_PATHCCH ..... #endif` blocks.

+91 -1100
+59
dll/win32/kernelbase/wine/path.c
··· 23 23 #include <string.h> 24 24 #include <wchar.h> 25 25 26 + #ifndef STATIC_PATHCCH 27 + 26 28 #include "windef.h" 27 29 #include "winbase.h" 28 30 #include "pathcch.h" ··· 38 40 #include "wine/heap.h" 39 41 40 42 WINE_DEFAULT_DEBUG_CHANNEL(path); 43 + 44 + #else // STATIC_PATHCCH 45 + #ifdef __REACTOS__ 46 + /* This is the static implementation of the PathCch library */ 47 + 48 + #include <windef.h> 49 + #include <winbase.h> 50 + 51 + /* The PathCch functions use size_t, but Wine's implementation uses SIZE_T, 52 + * so temporarily change the define'd SIZE_T type to the compatible one... */ 53 + #undef SIZE_T 54 + #define SIZE_T size_t 55 + 56 + #include <pathcch.h> 57 + #include <strsafe.h> 58 + 59 + #define TRACE(...) 60 + 61 + #if (_WIN32_WINNT < _WIN32_WINNT_VISTA) || (DLL_EXPORT_VERSION < _WIN32_WINNT_VISTA) 62 + /* wcsnlen is an NT6+ function. To cover all cases, use a private implementation */ 63 + static inline size_t compat_wcsnlen(const wchar_t* str, size_t size) 64 + { 65 + StringCchLengthW(str, size, &size); 66 + return size; 67 + } 68 + #define wcsnlen compat_wcsnlen 69 + #endif /* _WIN32_WINNT || DLL_EXPORT_VERSION */ 70 + 71 + // Implementation from string.c copied here in order not 72 + // to depend on the whole file just for the PathCch library. 73 + WCHAR * WINAPI StrRChrW(const WCHAR *str, const WCHAR *end, WORD ch) 74 + { 75 + WCHAR *ret = NULL; 76 + 77 + if (!str) return NULL; 78 + if (!end) end = str + lstrlenW(str); 79 + while (str < end) 80 + { 81 + if (*str == ch) ret = (WCHAR *)str; 82 + str++; 83 + } 84 + return ret; 85 + } 86 + 87 + #endif /* __REACTOS__ */ 88 + #endif // STATIC_PATHCCH 89 + 90 + #ifndef STATIC_PATHCCH 41 91 42 92 static const char hexDigits[] = "0123456789ABCDEF"; 43 93 ··· 131 181 return isalpha( str[0] ) && str[1] == ':'; 132 182 } 133 183 184 + #endif // !STATIC_PATHCCH 185 + 134 186 static BOOL is_drive_spec( const WCHAR *str ) 135 187 { 136 188 return isalpha( str[0] ) && str[1] == ':'; 137 189 } 138 190 191 + #ifndef STATIC_PATHCCH 139 192 static BOOL is_escaped_drive_spec( const WCHAR *str ) 140 193 { 141 194 return isalpha( str[0] ) && (str[1] == ':' || str[1] == '|'); 142 195 } 196 + #endif // !STATIC_PATHCCH 143 197 144 198 static BOOL is_prefixed_unc(const WCHAR *string) 145 199 { ··· 945 999 if (server) *server = result; 946 1000 return !!result; 947 1001 } 1002 + 1003 + #ifndef STATIC_PATHCCH 1004 + /* Other functions not part of the PathCch library */ 948 1005 949 1006 BOOL WINAPI PathIsUNCA(const char *path) 950 1007 { ··· 5248 5305 FIXME(": stub\n"); 5249 5306 return FALSE; 5250 5307 } 5308 + 5309 + #endif // !STATIC_PATHCCH
+10 -2
sdk/lib/pathcch/CMakeLists.txt
··· 1 1 2 - add_library(pathcch pathcch.c) 3 - add_dependencies(pathcch xdk) 2 + # Down-level static PathCch library, to be used for 3 + # programs that need to run on Windows 7 and below. 4 + add_library(pathcch_static STATIC ${REACTOS_SOURCE_DIR}/dll/win32/kernelbase/wine/path.c) 5 + add_dependencies(pathcch_static xdk) 6 + target_compile_definitions(pathcch_static PRIVATE wcsnicmp=_wcsnicmp STATIC_PATHCCH) 7 + 8 + # Windows 8+ compatible libraries, importing from either 9 + # kernelbase.dll or api-ms-win-core-path-l1-1-0.dll 10 + generate_import_lib(pathcch_kernelbase kernelbase.dll pathcch.spec "--version=${DLL_EXPORT_VERSION}" "") 11 + generate_import_lib(pathcch api-ms-win-core-path-l1-1-0.dll pathcch.spec "--version=${DLL_EXPORT_VERSION}" "")
-1098
sdk/lib/pathcch/pathcch.c
··· 1 - /* 2 - * Copyright 2018 Nikolay Sivov 3 - * Copyright 2018 Zhiyi Zhang 4 - * 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. 9 - * 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. 14 - * 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 18 - */ 19 - 20 - #include <stdarg.h> 21 - #include <string.h> 22 - 23 - /* Wine code is still stuck in the past... */ 24 - #ifdef __REACTOS__ 25 - #define wcsnicmp _wcsnicmp 26 - #endif 27 - 28 - #include <windef.h> 29 - #include <winbase.h> 30 - 31 - /* The PathCch functions use size_t, but Wine's implementation uses SIZE_T, 32 - * so temporarily change the define'd SIZE_T type to the compatible one... */ 33 - #ifdef __REACTOS__ 34 - #undef SIZE_T 35 - #define SIZE_T size_t 36 - #endif 37 - 38 - /* This is the static implementation of the PathCch functions */ 39 - #define STATIC_PATHCCH 40 - #ifdef __GNUC__ // GCC doesn't support #pragma deprecated() 41 - #undef DEPRECATE_SUPPORTED 42 - #endif 43 - #include <pathcch.h> 44 - 45 - #include <strsafe.h> 46 - 47 - #include "wine/debug.h" 48 - 49 - WINE_DEFAULT_DEBUG_CHANNEL(path); 50 - 51 - #ifdef __REACTOS__ 52 - #if (_WIN32_WINNT < _WIN32_WINNT_VISTA) || (DLL_EXPORT_VERSION < _WIN32_WINNT_VISTA) 53 - /* wcsnlen is an NT6+ function. To cover all cases, use a private implementation */ 54 - static inline size_t hacked_wcsnlen(const wchar_t* str, size_t size) 55 - { 56 - StringCchLengthW(str, size, &size); 57 - return size; 58 - } 59 - #define wcsnlen hacked_wcsnlen 60 - #endif 61 - #endif /* __REACTOS__ */ 62 - 63 - static BOOL is_drive_spec( const WCHAR *str ) 64 - { 65 - return isalpha( str[0] ) && str[1] == ':'; 66 - } 67 - 68 - #if 0 69 - static BOOL is_escaped_drive_spec( const WCHAR *str ) 70 - { 71 - return isalpha( str[0] ) && (str[1] == ':' || str[1] == '|'); 72 - } 73 - #endif 74 - 75 - static BOOL is_prefixed_unc(const WCHAR *string) 76 - { 77 - return !wcsnicmp(string, L"\\\\?\\UNC\\", 8 ); 78 - } 79 - 80 - static BOOL is_prefixed_disk(const WCHAR *string) 81 - { 82 - return !wcsncmp(string, L"\\\\?\\", 4) && is_drive_spec( string + 4 ); 83 - } 84 - 85 - static BOOL is_prefixed_volume(const WCHAR *string) 86 - { 87 - const WCHAR *guid; 88 - INT i = 0; 89 - 90 - if (wcsnicmp( string, L"\\\\?\\Volume", 10 )) return FALSE; 91 - 92 - guid = string + 10; 93 - 94 - while (i <= 37) 95 - { 96 - switch (i) 97 - { 98 - case 0: 99 - if (guid[i] != '{') return FALSE; 100 - break; 101 - case 9: 102 - case 14: 103 - case 19: 104 - case 24: 105 - if (guid[i] != '-') return FALSE; 106 - break; 107 - case 37: 108 - if (guid[i] != '}') return FALSE; 109 - break; 110 - default: 111 - if (!isxdigit(guid[i])) return FALSE; 112 - break; 113 - } 114 - i++; 115 - } 116 - 117 - return TRUE; 118 - } 119 - 120 - /* Get the next character beyond end of the segment. 121 - Return TRUE if the last segment ends with a backslash */ 122 - static BOOL get_next_segment(const WCHAR *next, const WCHAR **next_segment) 123 - { 124 - while (*next && *next != '\\') next++; 125 - if (*next == '\\') 126 - { 127 - *next_segment = next + 1; 128 - return TRUE; 129 - } 130 - else 131 - { 132 - *next_segment = next; 133 - return FALSE; 134 - } 135 - } 136 - 137 - /* Find the last character of the root in a path, if there is one, without any segments */ 138 - static const WCHAR *get_root_end(const WCHAR *path) 139 - { 140 - /* Find path root */ 141 - if (is_prefixed_volume(path)) 142 - return path[48] == '\\' ? path + 48 : path + 47; 143 - else if (is_prefixed_unc(path)) 144 - return path + 7; 145 - else if (is_prefixed_disk(path)) 146 - return path[6] == '\\' ? path + 6 : path + 5; 147 - /* \\ */ 148 - else if (path[0] == '\\' && path[1] == '\\') 149 - return path + 1; 150 - /* \ */ 151 - else if (path[0] == '\\') 152 - return path; 153 - /* X:\ */ 154 - else if (is_drive_spec( path )) 155 - return path[2] == '\\' ? path + 2 : path + 1; 156 - else 157 - return NULL; 158 - } 159 - 160 - #ifdef __REACTOS__ 161 - HRESULT 162 - APIENTRY 163 - PathAllocCanonicalize( 164 - _In_ PCWSTR path_in, 165 - _In_ /* PATHCCH_OPTIONS */ ULONG flags, 166 - _Outptr_ PWSTR* path_out) 167 - #else 168 - HRESULT WINAPI PathAllocCanonicalize(const WCHAR *path_in, DWORD flags, WCHAR **path_out) 169 - #endif 170 - { 171 - WCHAR *buffer, *dst; 172 - const WCHAR *src; 173 - const WCHAR *root_end; 174 - SIZE_T buffer_size, length; 175 - 176 - TRACE("%s %#lx %p\n", debugstr_w(path_in), flags, path_out); 177 - 178 - if (!path_in || !path_out 179 - || ((flags & PATHCCH_FORCE_ENABLE_LONG_NAME_PROCESS) && (flags & PATHCCH_FORCE_DISABLE_LONG_NAME_PROCESS)) 180 - || (flags & (PATHCCH_FORCE_ENABLE_LONG_NAME_PROCESS | PATHCCH_FORCE_DISABLE_LONG_NAME_PROCESS) 181 - && !(flags & PATHCCH_ALLOW_LONG_PATHS)) 182 - || ((flags & PATHCCH_ENSURE_IS_EXTENDED_LENGTH_PATH) && (flags & PATHCCH_ALLOW_LONG_PATHS))) 183 - { 184 - if (path_out) *path_out = NULL; 185 - return E_INVALIDARG; 186 - } 187 - 188 - length = lstrlenW(path_in); 189 - if ((length + 1 > MAX_PATH && !(flags & (PATHCCH_ALLOW_LONG_PATHS | PATHCCH_ENSURE_IS_EXTENDED_LENGTH_PATH))) 190 - || (length + 1 > PATHCCH_MAX_CCH)) 191 - { 192 - *path_out = NULL; 193 - return HRESULT_FROM_WIN32(ERROR_FILENAME_EXCED_RANGE); 194 - } 195 - 196 - /* PATHCCH_ENSURE_IS_EXTENDED_LENGTH_PATH implies PATHCCH_DO_NOT_NORMALIZE_SEGMENTS */ 197 - if (flags & PATHCCH_ENSURE_IS_EXTENDED_LENGTH_PATH) flags |= PATHCCH_DO_NOT_NORMALIZE_SEGMENTS; 198 - 199 - /* path length + possible \\?\ addition + possible \ addition + NUL */ 200 - buffer_size = (length + 6) * sizeof(WCHAR); 201 - buffer = LocalAlloc(LMEM_ZEROINIT, buffer_size); 202 - if (!buffer) 203 - { 204 - *path_out = NULL; 205 - return E_OUTOFMEMORY; 206 - } 207 - 208 - src = path_in; 209 - dst = buffer; 210 - 211 - root_end = get_root_end(path_in); 212 - if (root_end) root_end = buffer + (root_end - path_in); 213 - 214 - /* Copy path root */ 215 - if (root_end) 216 - { 217 - memcpy(dst, src, (root_end - buffer + 1) * sizeof(WCHAR)); 218 - src += root_end - buffer + 1; 219 - if(PathCchStripPrefix(dst, length + 6) == S_OK) 220 - { 221 - /* Fill in \ in X:\ if the \ is missing */ 222 - if (is_drive_spec( dst ) && dst[2]!= '\\') 223 - { 224 - dst[2] = '\\'; 225 - dst[3] = 0; 226 - } 227 - dst = buffer + lstrlenW(buffer); 228 - root_end = dst; 229 - } 230 - else 231 - dst += root_end - buffer + 1; 232 - } 233 - 234 - while (*src) 235 - { 236 - if (src[0] == '.') 237 - { 238 - if (src[1] == '.') 239 - { 240 - /* Keep one . after * */ 241 - if (dst > buffer && dst[-1] == '*') 242 - { 243 - *dst++ = *src++; 244 - continue; 245 - } 246 - 247 - /* Keep the .. if not surrounded by \ */ 248 - if ((src[2] != '\\' && src[2]) || (dst > buffer && dst[-1] != '\\')) 249 - { 250 - *dst++ = *src++; 251 - *dst++ = *src++; 252 - continue; 253 - } 254 - 255 - /* Remove the \ before .. if the \ is not part of root */ 256 - if (dst > buffer && dst[-1] == '\\' && (!root_end || dst - 1 > root_end)) 257 - { 258 - *--dst = '\0'; 259 - /* Remove characters until a \ is encountered */ 260 - while (dst > buffer) 261 - { 262 - if (dst[-1] == '\\') 263 - { 264 - *--dst = 0; 265 - break; 266 - } 267 - else 268 - *--dst = 0; 269 - } 270 - } 271 - /* Remove the extra \ after .. if the \ before .. wasn't deleted */ 272 - else if (src[2] == '\\') 273 - src++; 274 - 275 - src += 2; 276 - } 277 - else 278 - { 279 - /* Keep the . if not surrounded by \ */ 280 - if ((src[1] != '\\' && src[1]) || (dst > buffer && dst[-1] != '\\')) 281 - { 282 - *dst++ = *src++; 283 - continue; 284 - } 285 - 286 - /* Remove the \ before . if the \ is not part of root */ 287 - if (dst > buffer && dst[-1] == '\\' && (!root_end || dst - 1 > root_end)) dst--; 288 - /* Remove the extra \ after . if the \ before . wasn't deleted */ 289 - else if (src[1] == '\\') 290 - src++; 291 - 292 - src++; 293 - } 294 - 295 - /* If X:\ is not complete, then complete it */ 296 - if (is_drive_spec( buffer ) && buffer[2] != '\\') 297 - { 298 - root_end = buffer + 2; 299 - dst = buffer + 3; 300 - buffer[2] = '\\'; 301 - /* If next character is \, use the \ to fill in */ 302 - if (src[0] == '\\') src++; 303 - } 304 - } 305 - /* Copy over */ 306 - else 307 - *dst++ = *src++; 308 - } 309 - /* End the path */ 310 - *dst = 0; 311 - 312 - /* Strip multiple trailing . */ 313 - if (!(flags & PATHCCH_DO_NOT_NORMALIZE_SEGMENTS)) 314 - { 315 - while (dst > buffer && dst[-1] == '.') 316 - { 317 - /* Keep a . after * */ 318 - if (dst - 1 > buffer && dst[-2] == '*') 319 - break; 320 - /* If . follow a : at the second character, remove the . and add a \ */ 321 - else if (dst - 1 > buffer && dst[-2] == ':' && dst - 2 == buffer + 1) 322 - *--dst = '\\'; 323 - else 324 - *--dst = 0; 325 - } 326 - } 327 - 328 - /* If result path is empty, fill in \ */ 329 - if (!*buffer) 330 - { 331 - buffer[0] = '\\'; 332 - buffer[1] = 0; 333 - } 334 - 335 - /* Extend the path if needed */ 336 - length = lstrlenW(buffer); 337 - if (((length + 1 > MAX_PATH && is_drive_spec( buffer )) 338 - || (is_drive_spec( buffer ) && flags & PATHCCH_ENSURE_IS_EXTENDED_LENGTH_PATH)) 339 - && !(flags & PATHCCH_FORCE_ENABLE_LONG_NAME_PROCESS)) 340 - { 341 - memmove(buffer + 4, buffer, (length + 1) * sizeof(WCHAR)); 342 - buffer[0] = '\\'; 343 - buffer[1] = '\\'; 344 - buffer[2] = '?'; 345 - buffer[3] = '\\'; 346 - } 347 - 348 - /* Add a trailing backslash to the path if needed */ 349 - if (flags & PATHCCH_ENSURE_TRAILING_SLASH) 350 - PathCchAddBackslash(buffer, buffer_size); 351 - 352 - *path_out = buffer; 353 - return S_OK; 354 - } 355 - 356 - #ifdef __REACTOS__ 357 - HRESULT 358 - APIENTRY 359 - PathAllocCombine( 360 - _In_opt_ PCWSTR path1, 361 - _In_opt_ PCWSTR path2, 362 - _In_ /* PATHCCH_OPTIONS */ ULONG flags, 363 - _Outptr_ PWSTR* out) 364 - #else 365 - HRESULT WINAPI PathAllocCombine(const WCHAR *path1, const WCHAR *path2, DWORD flags, WCHAR **out) 366 - #endif 367 - { 368 - SIZE_T combined_length, length2; 369 - WCHAR *combined_path; 370 - BOOL add_backslash = FALSE; 371 - HRESULT hr; 372 - 373 - TRACE("%s %s %#lx %p\n", wine_dbgstr_w(path1), wine_dbgstr_w(path2), flags, out); 374 - 375 - if ((!path1 && !path2) || !out) 376 - { 377 - if (out) *out = NULL; 378 - return E_INVALIDARG; 379 - } 380 - 381 - if (!path1 || !path2) return PathAllocCanonicalize(path1 ? path1 : path2, flags, out); 382 - 383 - /* If path2 is fully qualified, use path2 only */ 384 - if (is_drive_spec( path2 ) || (path2[0] == '\\' && path2[1] == '\\')) 385 - { 386 - path1 = path2; 387 - path2 = NULL; 388 - add_backslash = (is_drive_spec(path1) && !path1[2]) 389 - || (is_prefixed_disk(path1) && !path1[6]); 390 - } 391 - 392 - length2 = path2 ? lstrlenW(path2) : 0; 393 - /* path1 length + path2 length + possible backslash + NULL */ 394 - combined_length = lstrlenW(path1) + length2 + 2; 395 - 396 - combined_path = HeapAlloc(GetProcessHeap(), 0, combined_length * sizeof(WCHAR)); 397 - if (!combined_path) 398 - { 399 - *out = NULL; 400 - return E_OUTOFMEMORY; 401 - } 402 - 403 - lstrcpyW(combined_path, path1); 404 - PathCchStripPrefix(combined_path, combined_length); 405 - if (add_backslash) PathCchAddBackslashEx(combined_path, combined_length, NULL, NULL); 406 - 407 - if (path2 && path2[0]) 408 - { 409 - if (path2[0] == '\\' && path2[1] != '\\') 410 - { 411 - PathCchStripToRoot(combined_path, combined_length); 412 - path2++; 413 - } 414 - 415 - PathCchAddBackslashEx(combined_path, combined_length, NULL, NULL); 416 - lstrcatW(combined_path, path2); 417 - } 418 - 419 - hr = PathAllocCanonicalize(combined_path, flags, out); 420 - HeapFree(GetProcessHeap(), 0, combined_path); 421 - return hr; 422 - } 423 - 424 - #ifdef __REACTOS__ 425 - HRESULT 426 - APIENTRY 427 - PathCchAddBackslash( 428 - _Inout_updates_(size) PWSTR path, 429 - _In_ size_t size) 430 - #else 431 - HRESULT WINAPI PathCchAddBackslash(WCHAR *path, SIZE_T size) 432 - #endif 433 - { 434 - return PathCchAddBackslashEx(path, size, NULL, NULL); 435 - } 436 - 437 - #ifdef __REACTOS__ 438 - HRESULT 439 - APIENTRY 440 - PathCchAddBackslashEx( 441 - _Inout_updates_(size) PWSTR path, 442 - _In_ size_t size, 443 - _Outptr_opt_result_buffer_(*remaining) PWSTR* endptr, 444 - _Out_opt_ size_t* remaining) 445 - #else 446 - HRESULT WINAPI PathCchAddBackslashEx(WCHAR *path, SIZE_T size, WCHAR **endptr, SIZE_T *remaining) 447 - #endif 448 - { 449 - BOOL needs_termination; 450 - SIZE_T length; 451 - 452 - TRACE("%s, %Iu, %p, %p\n", debugstr_w(path), size, endptr, remaining); 453 - 454 - length = lstrlenW(path); 455 - needs_termination = size && length && path[length - 1] != '\\'; 456 - 457 - if (length >= (needs_termination ? size - 1 : size)) 458 - { 459 - if (endptr) *endptr = NULL; 460 - if (remaining) *remaining = 0; 461 - return STRSAFE_E_INSUFFICIENT_BUFFER; 462 - } 463 - 464 - if (!needs_termination) 465 - { 466 - if (endptr) *endptr = path + length; 467 - if (remaining) *remaining = size - length; 468 - return S_FALSE; 469 - } 470 - 471 - path[length++] = '\\'; 472 - path[length] = 0; 473 - 474 - if (endptr) *endptr = path + length; 475 - if (remaining) *remaining = size - length; 476 - 477 - return S_OK; 478 - } 479 - 480 - #ifdef __REACTOS__ 481 - HRESULT 482 - APIENTRY 483 - PathCchAddExtension( 484 - _Inout_updates_(size) PWSTR path, 485 - _In_ size_t size, 486 - _In_ PCWSTR extension) 487 - #else 488 - HRESULT WINAPI PathCchAddExtension(WCHAR *path, SIZE_T size, const WCHAR *extension) 489 - #endif 490 - { 491 - const WCHAR *existing_extension, *next; 492 - SIZE_T path_length, extension_length, dot_length; 493 - BOOL has_dot; 494 - HRESULT hr; 495 - 496 - TRACE("%s %Iu %s\n", wine_dbgstr_w(path), size, wine_dbgstr_w(extension)); 497 - 498 - if (!path || !size || size > PATHCCH_MAX_CCH || !extension) return E_INVALIDARG; 499 - 500 - next = extension; 501 - while (*next) 502 - { 503 - if ((*next == '.' && next > extension) || *next == ' ' || *next == '\\') return E_INVALIDARG; 504 - next++; 505 - } 506 - 507 - has_dot = extension[0] == '.'; 508 - 509 - hr = PathCchFindExtension(path, size, &existing_extension); 510 - if (FAILED(hr)) return hr; 511 - if (*existing_extension) return S_FALSE; 512 - 513 - path_length = wcsnlen(path, size); 514 - dot_length = has_dot ? 0 : 1; 515 - extension_length = lstrlenW(extension); 516 - 517 - if (path_length + dot_length + extension_length + 1 > size) return STRSAFE_E_INSUFFICIENT_BUFFER; 518 - 519 - /* If extension is empty or only dot, return S_OK with path unchanged */ 520 - if (!extension[0] || (extension[0] == '.' && !extension[1])) return S_OK; 521 - 522 - if (!has_dot) 523 - { 524 - path[path_length] = '.'; 525 - path_length++; 526 - } 527 - 528 - lstrcpyW(path + path_length, extension); 529 - return S_OK; 530 - } 531 - 532 - #ifdef __REACTOS__ 533 - HRESULT 534 - APIENTRY 535 - PathCchAppend( 536 - _Inout_updates_(size) PWSTR path1, 537 - _In_ size_t size, 538 - _In_opt_ PCWSTR path2) 539 - #else 540 - HRESULT WINAPI PathCchAppend(WCHAR *path1, SIZE_T size, const WCHAR *path2) 541 - #endif 542 - { 543 - TRACE("%s %Iu %s\n", wine_dbgstr_w(path1), size, wine_dbgstr_w(path2)); 544 - 545 - return PathCchAppendEx(path1, size, path2, PATHCCH_NONE); 546 - } 547 - 548 - #ifdef __REACTOS__ 549 - HRESULT 550 - APIENTRY 551 - PathCchAppendEx( 552 - _Inout_updates_(size) PWSTR path1, 553 - _In_ size_t size, 554 - _In_opt_ PCWSTR path2, 555 - _In_ /* PATHCCH_OPTIONS */ ULONG flags) 556 - #else 557 - HRESULT WINAPI PathCchAppendEx(WCHAR *path1, SIZE_T size, const WCHAR *path2, DWORD flags) 558 - #endif 559 - { 560 - HRESULT hr; 561 - WCHAR *result; 562 - 563 - TRACE("%s %Iu %s %#lx\n", wine_dbgstr_w(path1), size, wine_dbgstr_w(path2), flags); 564 - 565 - if (!path1 || !size) return E_INVALIDARG; 566 - 567 - /* Create a temporary buffer for result because we need to keep path1 unchanged if error occurs. 568 - * And PathCchCombineEx writes empty result if there is error so we can't just use path1 as output 569 - * buffer for PathCchCombineEx */ 570 - result = HeapAlloc(GetProcessHeap(), 0, size * sizeof(WCHAR)); 571 - if (!result) return E_OUTOFMEMORY; 572 - 573 - /* Avoid the single backslash behavior with PathCchCombineEx when appending */ 574 - if (path2 && path2[0] == '\\' && path2[1] != '\\') path2++; 575 - 576 - hr = PathCchCombineEx(result, size, path1, path2, flags); 577 - if (SUCCEEDED(hr)) memcpy(path1, result, size * sizeof(WCHAR)); 578 - 579 - HeapFree(GetProcessHeap(), 0, result); 580 - return hr; 581 - } 582 - 583 - #ifdef __REACTOS__ 584 - HRESULT 585 - APIENTRY 586 - PathCchCanonicalize( 587 - _Out_writes_(size) PWSTR out, 588 - _In_ size_t size, 589 - _In_ PCWSTR in) 590 - #else 591 - HRESULT WINAPI PathCchCanonicalize(WCHAR *out, SIZE_T size, const WCHAR *in) 592 - #endif 593 - { 594 - TRACE("%p %Iu %s\n", out, size, wine_dbgstr_w(in)); 595 - 596 - /* Not X:\ and path > MAX_PATH - 4, return HRESULT_FROM_WIN32(ERROR_FILENAME_EXCED_RANGE) */ 597 - if (lstrlenW(in) > MAX_PATH - 4 && !(is_drive_spec( in ) && in[2] == '\\')) 598 - return HRESULT_FROM_WIN32(ERROR_FILENAME_EXCED_RANGE); 599 - 600 - return PathCchCanonicalizeEx(out, size, in, PATHCCH_NONE); 601 - } 602 - 603 - #ifdef __REACTOS__ 604 - HRESULT 605 - APIENTRY 606 - PathCchCanonicalizeEx( 607 - _Out_writes_(size) PWSTR out, 608 - _In_ size_t size, 609 - _In_ PCWSTR in, 610 - _In_ /* PATHCCH_OPTIONS */ ULONG flags) 611 - #else 612 - HRESULT WINAPI PathCchCanonicalizeEx(WCHAR *out, SIZE_T size, const WCHAR *in, DWORD flags) 613 - #endif 614 - { 615 - WCHAR *buffer; 616 - SIZE_T length; 617 - HRESULT hr; 618 - 619 - TRACE("%p %Iu %s %#lx\n", out, size, wine_dbgstr_w(in), flags); 620 - 621 - if (!size) return E_INVALIDARG; 622 - 623 - hr = PathAllocCanonicalize(in, flags, &buffer); 624 - if (FAILED(hr)) return hr; 625 - 626 - length = lstrlenW(buffer); 627 - if (size < length + 1) 628 - { 629 - /* No root and path > MAX_PATH - 4, return HRESULT_FROM_WIN32(ERROR_FILENAME_EXCED_RANGE) */ 630 - if (length > MAX_PATH - 4 && !(in[0] == '\\' || (is_drive_spec( in ) && in[2] == '\\'))) 631 - hr = HRESULT_FROM_WIN32(ERROR_FILENAME_EXCED_RANGE); 632 - else 633 - hr = STRSAFE_E_INSUFFICIENT_BUFFER; 634 - } 635 - 636 - if (SUCCEEDED(hr)) 637 - { 638 - memcpy(out, buffer, (length + 1) * sizeof(WCHAR)); 639 - 640 - /* Fill a backslash at the end of X: */ 641 - if (is_drive_spec( out ) && !out[2] && size > 3) 642 - { 643 - out[2] = '\\'; 644 - out[3] = 0; 645 - } 646 - } 647 - 648 - LocalFree(buffer); 649 - return hr; 650 - } 651 - 652 - #ifdef __REACTOS__ 653 - HRESULT 654 - APIENTRY 655 - PathCchCombine( 656 - _Out_writes_(size) PWSTR out, 657 - _In_ size_t size, 658 - _In_opt_ PCWSTR path1, 659 - _In_opt_ PCWSTR path2) 660 - #else 661 - HRESULT WINAPI PathCchCombine(WCHAR *out, SIZE_T size, const WCHAR *path1, const WCHAR *path2) 662 - #endif 663 - { 664 - TRACE("%p %s %s\n", out, wine_dbgstr_w(path1), wine_dbgstr_w(path2)); 665 - 666 - return PathCchCombineEx(out, size, path1, path2, PATHCCH_NONE); 667 - } 668 - 669 - #ifdef __REACTOS__ 670 - HRESULT 671 - APIENTRY 672 - PathCchCombineEx( 673 - _Out_writes_(size) PWSTR out, 674 - _In_ size_t size, 675 - _In_opt_ PCWSTR path1, 676 - _In_opt_ PCWSTR path2, 677 - _In_ /* PATHCCH_OPTIONS */ ULONG flags) 678 - #else 679 - HRESULT WINAPI PathCchCombineEx(WCHAR *out, SIZE_T size, const WCHAR *path1, const WCHAR *path2, DWORD flags) 680 - #endif 681 - { 682 - HRESULT hr; 683 - WCHAR *buffer; 684 - SIZE_T length; 685 - 686 - TRACE("%p %s %s %#lx\n", out, wine_dbgstr_w(path1), wine_dbgstr_w(path2), flags); 687 - 688 - if (!out || !size || size > PATHCCH_MAX_CCH) return E_INVALIDARG; 689 - 690 - hr = PathAllocCombine(path1, path2, flags, &buffer); 691 - if (FAILED(hr)) 692 - { 693 - out[0] = 0; 694 - return hr; 695 - } 696 - 697 - length = lstrlenW(buffer); 698 - if (length + 1 > size) 699 - { 700 - out[0] = 0; 701 - LocalFree(buffer); 702 - return STRSAFE_E_INSUFFICIENT_BUFFER; 703 - } 704 - else 705 - { 706 - memcpy(out, buffer, (length + 1) * sizeof(WCHAR)); 707 - LocalFree(buffer); 708 - return S_OK; 709 - } 710 - } 711 - 712 - #ifdef __REACTOS__ 713 - HRESULT 714 - APIENTRY 715 - PathCchFindExtension( 716 - _In_reads_(size) PCWSTR path, 717 - _In_ size_t size, 718 - _Outptr_ PCWSTR* extension) 719 - #else 720 - HRESULT WINAPI PathCchFindExtension(const WCHAR *path, SIZE_T size, const WCHAR **extension) 721 - #endif 722 - { 723 - const WCHAR *lastpoint = NULL; 724 - SIZE_T counter = 0; 725 - 726 - TRACE("%s %Iu %p\n", wine_dbgstr_w(path), size, extension); 727 - 728 - if (!path || !size || size > PATHCCH_MAX_CCH) 729 - { 730 - *extension = NULL; 731 - return E_INVALIDARG; 732 - } 733 - 734 - while (*path) 735 - { 736 - if (*path == '\\' || *path == ' ') 737 - lastpoint = NULL; 738 - else if (*path == '.') 739 - lastpoint = path; 740 - 741 - path++; 742 - counter++; 743 - if (counter == size || counter == PATHCCH_MAX_CCH) 744 - { 745 - *extension = NULL; 746 - return E_INVALIDARG; 747 - } 748 - } 749 - 750 - *extension = lastpoint ? lastpoint : path; 751 - return S_OK; 752 - } 753 - 754 - #ifdef __REACTOS__ 755 - BOOL 756 - APIENTRY 757 - PathCchIsRoot( 758 - _In_opt_ PCWSTR path) 759 - #else 760 - BOOL WINAPI PathCchIsRoot(const WCHAR *path) 761 - #endif 762 - { 763 - const WCHAR *root_end; 764 - const WCHAR *next; 765 - BOOL is_unc; 766 - 767 - TRACE("%s\n", wine_dbgstr_w(path)); 768 - 769 - if (!path || !*path) return FALSE; 770 - 771 - root_end = get_root_end(path); 772 - if (!root_end) return FALSE; 773 - 774 - if ((is_unc = is_prefixed_unc(path)) || (path[0] == '\\' && path[1] == '\\' && path[2] != '?')) 775 - { 776 - next = root_end + 1; 777 - /* No extra segments */ 778 - if ((is_unc && !*next) || (!is_unc && !*next)) return TRUE; 779 - 780 - /* Has first segment with an ending backslash but no remaining characters */ 781 - if (get_next_segment(next, &next) && !*next) return FALSE; 782 - /* Has first segment with no ending backslash */ 783 - else if (!*next) 784 - return TRUE; 785 - /* Has first segment with an ending backslash and has remaining characters*/ 786 - else 787 - { 788 - next++; 789 - /* Second segment must have no backslash and no remaining characters */ 790 - return !get_next_segment(next, &next) && !*next; 791 - } 792 - } 793 - else if (*root_end == '\\' && !root_end[1]) 794 - return TRUE; 795 - else 796 - return FALSE; 797 - } 798 - 799 - #ifdef __REACTOS__ 800 - HRESULT 801 - APIENTRY 802 - PathCchRemoveBackslash( 803 - _Inout_updates_(path_size) PWSTR path, 804 - _In_ size_t path_size) 805 - #else 806 - HRESULT WINAPI PathCchRemoveBackslash(WCHAR *path, SIZE_T path_size) 807 - #endif 808 - { 809 - WCHAR *path_end; 810 - SIZE_T free_size; 811 - 812 - TRACE("%s %Iu\n", debugstr_w(path), path_size); 813 - 814 - return PathCchRemoveBackslashEx(path, path_size, &path_end, &free_size); 815 - } 816 - 817 - #ifdef __REACTOS__ 818 - HRESULT 819 - APIENTRY 820 - PathCchRemoveBackslashEx( 821 - _Inout_updates_(path_size) PWSTR path, 822 - _In_ size_t path_size, 823 - _Outptr_opt_result_buffer_(*free_size) PWSTR* path_end, 824 - _Out_opt_ size_t* free_size) 825 - #else 826 - HRESULT WINAPI PathCchRemoveBackslashEx(WCHAR *path, SIZE_T path_size, WCHAR **path_end, SIZE_T *free_size) 827 - #endif 828 - { 829 - const WCHAR *root_end; 830 - SIZE_T path_length; 831 - 832 - TRACE("%s %Iu %p %p\n", debugstr_w(path), path_size, path_end, free_size); 833 - 834 - if (!path_size || !path_end || !free_size) 835 - { 836 - if (path_end) *path_end = NULL; 837 - if (free_size) *free_size = 0; 838 - return E_INVALIDARG; 839 - } 840 - 841 - path_length = wcsnlen(path, path_size); 842 - if (path_length == path_size && !path[path_length]) return E_INVALIDARG; 843 - 844 - root_end = get_root_end(path); 845 - if (path_length > 0 && path[path_length - 1] == '\\') 846 - { 847 - *path_end = path + path_length - 1; 848 - *free_size = path_size - path_length + 1; 849 - /* If the last character is beyond end of root */ 850 - if (!root_end || path + path_length - 1 > root_end) 851 - { 852 - path[path_length - 1] = 0; 853 - return S_OK; 854 - } 855 - else 856 - return S_FALSE; 857 - } 858 - else 859 - { 860 - *path_end = path + path_length; 861 - *free_size = path_size - path_length; 862 - return S_FALSE; 863 - } 864 - } 865 - 866 - #ifdef __REACTOS__ 867 - HRESULT 868 - APIENTRY 869 - PathCchRemoveExtension( 870 - _Inout_updates_(size) PWSTR path, 871 - _In_ size_t size) 872 - #else 873 - HRESULT WINAPI PathCchRemoveExtension(WCHAR *path, SIZE_T size) 874 - #endif 875 - { 876 - const WCHAR *extension; 877 - WCHAR *next; 878 - HRESULT hr; 879 - 880 - TRACE("%s %Iu\n", wine_dbgstr_w(path), size); 881 - 882 - if (!path || !size || size > PATHCCH_MAX_CCH) return E_INVALIDARG; 883 - 884 - hr = PathCchFindExtension(path, size, &extension); 885 - if (FAILED(hr)) return hr; 886 - 887 - next = path + (extension - path); 888 - while (next - path < size && *next) *next++ = 0; 889 - 890 - return next == extension ? S_FALSE : S_OK; 891 - } 892 - 893 - #ifdef __REACTOS__ 894 - HRESULT 895 - APIENTRY 896 - PathCchRemoveFileSpec( 897 - _Inout_updates_(size) PWSTR path, 898 - _In_ size_t size) 899 - #else 900 - HRESULT WINAPI PathCchRemoveFileSpec(WCHAR *path, SIZE_T size) 901 - #endif 902 - { 903 - const WCHAR *root_end = NULL; 904 - SIZE_T length; 905 - WCHAR *last; 906 - 907 - TRACE("%s %Iu\n", wine_dbgstr_w(path), size); 908 - 909 - if (!path || !size || size > PATHCCH_MAX_CCH) return E_INVALIDARG; 910 - 911 - if (PathCchIsRoot(path)) return S_FALSE; 912 - 913 - PathCchSkipRoot(path, &root_end); 914 - 915 - /* The backslash at the end of UNC and \\* are not considered part of root in this case */ 916 - if (root_end && root_end > path && root_end[-1] == '\\' 917 - && (is_prefixed_unc(path) || (path[0] == '\\' && path[1] == '\\' && path[2] != '?'))) 918 - root_end--; 919 - 920 - length = lstrlenW(path); 921 - last = path + length - 1; 922 - while (last >= path && (!root_end || last >= root_end)) 923 - { 924 - if (last - path >= size) return E_INVALIDARG; 925 - 926 - if (*last == '\\') 927 - { 928 - *last-- = 0; 929 - break; 930 - } 931 - 932 - *last-- = 0; 933 - } 934 - 935 - return last != path + length - 1 ? S_OK : S_FALSE; 936 - } 937 - 938 - #ifdef __REACTOS__ 939 - HRESULT 940 - APIENTRY 941 - PathCchRenameExtension( 942 - _Inout_updates_(size) PWSTR path, 943 - _In_ size_t size, 944 - _In_ PCWSTR extension) 945 - #else 946 - HRESULT WINAPI PathCchRenameExtension(WCHAR *path, SIZE_T size, const WCHAR *extension) 947 - #endif 948 - { 949 - HRESULT hr; 950 - 951 - TRACE("%s %Iu %s\n", wine_dbgstr_w(path), size, wine_dbgstr_w(extension)); 952 - 953 - hr = PathCchRemoveExtension(path, size); 954 - if (FAILED(hr)) return hr; 955 - 956 - hr = PathCchAddExtension(path, size, extension); 957 - return FAILED(hr) ? hr : S_OK; 958 - } 959 - 960 - #ifdef __REACTOS__ 961 - HRESULT 962 - APIENTRY 963 - PathCchSkipRoot( 964 - _In_ PCWSTR path, 965 - _Outptr_ PCWSTR* root_end) 966 - #else 967 - HRESULT WINAPI PathCchSkipRoot(const WCHAR *path, const WCHAR **root_end) 968 - #endif 969 - { 970 - TRACE("%s %p\n", debugstr_w(path), root_end); 971 - 972 - if (!path || !path[0] || !root_end 973 - || (!wcsnicmp(path, L"\\\\?", 3) && !is_prefixed_volume(path) && !is_prefixed_unc(path) 974 - && !is_prefixed_disk(path))) 975 - return E_INVALIDARG; 976 - 977 - *root_end = get_root_end(path); 978 - if (*root_end) 979 - { 980 - (*root_end)++; 981 - if (is_prefixed_unc(path)) 982 - { 983 - get_next_segment(*root_end, root_end); 984 - get_next_segment(*root_end, root_end); 985 - } 986 - else if (path[0] == '\\' && path[1] == '\\' && path[2] != '?') 987 - { 988 - /* Skip share server */ 989 - get_next_segment(*root_end, root_end); 990 - /* If mount point is empty, don't skip over mount point */ 991 - if (**root_end != '\\') get_next_segment(*root_end, root_end); 992 - } 993 - } 994 - 995 - return *root_end ? S_OK : E_INVALIDARG; 996 - } 997 - 998 - #ifdef __REACTOS__ 999 - HRESULT 1000 - APIENTRY 1001 - PathCchStripPrefix( 1002 - _Inout_updates_(size) PWSTR path, 1003 - _In_ size_t size) 1004 - #else 1005 - HRESULT WINAPI PathCchStripPrefix(WCHAR *path, SIZE_T size) 1006 - #endif 1007 - { 1008 - TRACE("%s %Iu\n", wine_dbgstr_w(path), size); 1009 - 1010 - if (!path || !size || size > PATHCCH_MAX_CCH) return E_INVALIDARG; 1011 - 1012 - if (is_prefixed_unc(path)) 1013 - { 1014 - /* \\?\UNC\a -> \\a */ 1015 - if (size < lstrlenW(path + 8) + 3) return E_INVALIDARG; 1016 - lstrcpyW(path + 2, path + 8); 1017 - return S_OK; 1018 - } 1019 - else if (is_prefixed_disk(path)) 1020 - { 1021 - /* \\?\C:\ -> C:\ */ 1022 - if (size < lstrlenW(path + 4) + 1) return E_INVALIDARG; 1023 - lstrcpyW(path, path + 4); 1024 - return S_OK; 1025 - } 1026 - else 1027 - return S_FALSE; 1028 - } 1029 - 1030 - #ifdef __REACTOS__ 1031 - HRESULT 1032 - APIENTRY 1033 - PathCchStripToRoot( 1034 - _Inout_updates_(size) PWSTR path, 1035 - _In_ size_t size) 1036 - #else 1037 - HRESULT WINAPI PathCchStripToRoot(WCHAR *path, SIZE_T size) 1038 - #endif 1039 - { 1040 - const WCHAR *root_end; 1041 - WCHAR *segment_end; 1042 - BOOL is_unc; 1043 - 1044 - TRACE("%s %Iu\n", wine_dbgstr_w(path), size); 1045 - 1046 - if (!path || !*path || !size || size > PATHCCH_MAX_CCH) return E_INVALIDARG; 1047 - 1048 - /* \\\\?\\UNC\\* and \\\\* have to have at least two extra segments to be striped, 1049 - * e.g. \\\\?\\UNC\\a\\b\\c -> \\\\?\\UNC\\a\\b 1050 - * \\\\a\\b\\c -> \\\\a\\b */ 1051 - if ((is_unc = is_prefixed_unc(path)) || (path[0] == '\\' && path[1] == '\\' && path[2] != '?')) 1052 - { 1053 - root_end = is_unc ? path + 8 : path + 3; 1054 - if (!get_next_segment(root_end, &root_end)) return S_FALSE; 1055 - if (!get_next_segment(root_end, &root_end)) return S_FALSE; 1056 - 1057 - if (root_end - path >= size) return E_INVALIDARG; 1058 - 1059 - segment_end = path + (root_end - path) - 1; 1060 - *segment_end = 0; 1061 - return S_OK; 1062 - } 1063 - else if (PathCchSkipRoot(path, &root_end) == S_OK) 1064 - { 1065 - if (root_end - path >= size) return E_INVALIDARG; 1066 - 1067 - segment_end = path + (root_end - path); 1068 - if (!*segment_end) return S_FALSE; 1069 - 1070 - *segment_end = 0; 1071 - return S_OK; 1072 - } 1073 - else 1074 - return E_INVALIDARG; 1075 - } 1076 - 1077 - #ifdef __REACTOS__ 1078 - BOOL 1079 - APIENTRY 1080 - PathIsUNCEx( 1081 - _In_ PCWSTR path, 1082 - _Outptr_opt_ PCWSTR* server) 1083 - #else 1084 - BOOL WINAPI PathIsUNCEx(const WCHAR *path, const WCHAR **server) 1085 - #endif 1086 - { 1087 - const WCHAR *result = NULL; 1088 - 1089 - TRACE("%s %p\n", wine_dbgstr_w(path), server); 1090 - 1091 - if (is_prefixed_unc(path)) 1092 - result = path + 8; 1093 - else if (path[0] == '\\' && path[1] == '\\' && path[2] != '?') 1094 - result = path + 2; 1095 - 1096 - if (server) *server = result; 1097 - return !!result; 1098 - }
+22
sdk/lib/pathcch/pathcch.spec
··· 1 + @ stdcall PathAllocCanonicalize(wstr long ptr) 2 + @ stdcall PathAllocCombine(wstr wstr long ptr) 3 + @ stdcall PathCchAddBackslash(wstr long) 4 + @ stdcall PathCchAddBackslashEx(wstr long ptr ptr) 5 + @ stdcall PathCchAddExtension(wstr long wstr) 6 + @ stdcall PathCchAppend(wstr long wstr) 7 + @ stdcall PathCchAppendEx(wstr long wstr long) 8 + @ stdcall PathCchCanonicalize(ptr long wstr) 9 + @ stdcall PathCchCanonicalizeEx(ptr long wstr long) 10 + @ stdcall PathCchCombine(ptr long wstr wstr) 11 + @ stdcall PathCchCombineEx(ptr long wstr wstr long) 12 + @ stdcall PathCchFindExtension(wstr long ptr) 13 + @ stdcall PathCchIsRoot(wstr) 14 + @ stdcall PathCchRemoveBackslash(wstr long) 15 + @ stdcall PathCchRemoveBackslashEx(wstr long ptr ptr) 16 + @ stdcall PathCchRemoveExtension(wstr long) 17 + @ stdcall PathCchRemoveFileSpec(wstr long) 18 + @ stdcall PathCchRenameExtension(wstr long wstr) 19 + @ stdcall PathCchSkipRoot(wstr ptr) 20 + @ stdcall PathCchStripPrefix(wstr long) 21 + @ stdcall PathCchStripToRoot(wstr long) 22 + @ stdcall PathIsUNCEx(wstr ptr)