1 /* 2 * Path Functions 3 * 4 * Copyright 1999, 2000 Juergen Schmied 5 * Copyright 2001, 2002 Jon Griffiths 6 * 7 * This library is free software; you can redistribute it and/or 8 * modify it under the terms of the GNU Lesser General Public 9 * License as published by the Free Software Foundation; either 10 * version 2.1 of the License, or (at your option) any later version. 11 * 12 * This library is distributed in the hope that it will be useful, 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 * Lesser General Public License for more details. 16 * 17 * You should have received a copy of the GNU Lesser General Public 18 * License along with this library; if not, write to the Free Software 19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA 20 */ 21 22 #include "config.h" 23 #include "wine/port.h" 24 25 #include <stdarg.h> 26 #include <string.h> 27 #include <stdlib.h> 28 29 #include "wine/unicode.h" 30 #include "windef.h" 31 #include "winbase.h" 32 #include "wingdi.h" 33 #include "winuser.h" 34 #include "winreg.h" 35 #include "winternl.h" 36 #define NO_SHLWAPI_STREAM 37 #include "shlwapi.h" 38 #include "wine/debug.h" 39 40 WINE_DEFAULT_DEBUG_CHANNEL(shell); 41 42 #ifdef __REACTOS__ 43 44 #include <shlobj.h> 45 #include <shlwapi_undoc.h> 46 47 int WINAPI IsNetDrive(int drive); 48 49 #else 50 51 /* Get a function pointer from a DLL handle */ 52 #define GET_FUNC(func, module, name, fail) \ 53 do { \ 54 if (!func) { \ 55 if (!SHLWAPI_h##module && !(SHLWAPI_h##module = LoadLibraryA(#module ".dll"))) return fail; \ 56 func = (fn##func)GetProcAddress(SHLWAPI_h##module, name); \ 57 if (!func) return fail; \ 58 } \ 59 } while (0) 60 61 /* DLL handles for late bound calls */ 62 static HMODULE SHLWAPI_hshell32; 63 64 /* Function pointers for GET_FUNC macro; these need to be global because of gcc bug */ 65 typedef BOOL (WINAPI *fnpIsNetDrive)(int); 66 static fnpIsNetDrive pIsNetDrive; 67 68 #endif /* __REACTOS__ */ 69 70 71 HRESULT WINAPI SHGetWebFolderFilePathW(LPCWSTR,LPWSTR,DWORD); 72 73 static inline WCHAR* heap_strdupAtoW(LPCSTR str) 74 { 75 WCHAR *ret = NULL; 76 77 if (str) 78 { 79 DWORD len = MultiByteToWideChar(CP_ACP, 0, str, -1, NULL, 0); 80 ret = HeapAlloc(GetProcessHeap(), 0, len*sizeof(WCHAR)); 81 if (ret) 82 MultiByteToWideChar(CP_ACP, 0, str, -1, ret, len); 83 } 84 85 return ret; 86 } 87 88 /************************************************************************* 89 * PathAppendA [SHLWAPI.@] 90 * 91 * Append one path to another. 92 * 93 * PARAMS 94 * lpszPath [I/O] Initial part of path, and destination for output 95 * lpszAppend [I] Path to append 96 * 97 * RETURNS 98 * Success: TRUE. lpszPath contains the newly created path. 99 * Failure: FALSE, if either path is NULL, or PathCombineA() fails. 100 * 101 * NOTES 102 * lpszAppend must contain at least one backslash ('\') if not NULL. 103 * Because PathCombineA() is used to join the paths, the resulting 104 * path is also canonicalized. 105 */ 106 BOOL WINAPI PathAppendA (LPSTR lpszPath, LPCSTR lpszAppend) 107 { 108 TRACE("(%s,%s)\n",debugstr_a(lpszPath), debugstr_a(lpszAppend)); 109 110 if (lpszPath && lpszAppend) 111 { 112 if (!PathIsUNCA(lpszAppend)) 113 while (*lpszAppend == '\\') 114 lpszAppend++; 115 if (PathCombineA(lpszPath, lpszPath, lpszAppend)) 116 return TRUE; 117 } 118 return FALSE; 119 } 120 121 /************************************************************************* 122 * PathAppendW [SHLWAPI.@] 123 * 124 * See PathAppendA. 125 */ 126 BOOL WINAPI PathAppendW(LPWSTR lpszPath, LPCWSTR lpszAppend) 127 { 128 TRACE("(%s,%s)\n",debugstr_w(lpszPath), debugstr_w(lpszAppend)); 129 130 if (lpszPath && lpszAppend) 131 { 132 if (!PathIsUNCW(lpszAppend)) 133 while (*lpszAppend == '\\') 134 lpszAppend++; 135 if (PathCombineW(lpszPath, lpszPath, lpszAppend)) 136 return TRUE; 137 } 138 return FALSE; 139 } 140 141 /************************************************************************* 142 * PathCombineA [SHLWAPI.@] 143 * 144 * Combine two paths together. 145 * 146 * PARAMS 147 * lpszDest [O] Destination for combined path 148 * lpszDir [I] Directory path 149 * lpszFile [I] File path 150 * 151 * RETURNS 152 * Success: The output path 153 * Failure: NULL, if inputs are invalid. 154 * 155 * NOTES 156 * lpszDest should be at least MAX_PATH in size, and may point to the same 157 * memory location as lpszDir. The combined path is canonicalised. 158 */ 159 LPSTR WINAPI PathCombineA(LPSTR lpszDest, LPCSTR lpszDir, LPCSTR lpszFile) 160 { 161 WCHAR szDest[MAX_PATH]; 162 WCHAR szDir[MAX_PATH]; 163 WCHAR szFile[MAX_PATH]; 164 TRACE("(%p,%s,%s)\n", lpszDest, debugstr_a(lpszDir), debugstr_a(lpszFile)); 165 166 /* Invalid parameters */ 167 if (!lpszDest) 168 return NULL; 169 if (!lpszDir && !lpszFile) 170 goto fail; 171 172 if (lpszDir) 173 if (!MultiByteToWideChar(CP_ACP,0,lpszDir,-1,szDir,MAX_PATH)) 174 goto fail; 175 176 if (lpszFile) 177 if (!MultiByteToWideChar(CP_ACP,0,lpszFile,-1,szFile,MAX_PATH)) 178 goto fail; 179 180 if (PathCombineW(szDest, lpszDir ? szDir : NULL, lpszFile ? szFile : NULL)) 181 if (WideCharToMultiByte(CP_ACP,0,szDest,-1,lpszDest,MAX_PATH,0,0)) 182 return lpszDest; 183 184 fail: 185 lpszDest[0] = 0; 186 return NULL; 187 } 188 189 /************************************************************************* 190 * PathCombineW [SHLWAPI.@] 191 * 192 * See PathCombineA. 193 */ 194 LPWSTR WINAPI PathCombineW(LPWSTR lpszDest, LPCWSTR lpszDir, LPCWSTR lpszFile) 195 { 196 WCHAR szTemp[MAX_PATH]; 197 BOOL bUseBoth = FALSE, bStrip = FALSE; 198 199 TRACE("(%p,%s,%s)\n", lpszDest, debugstr_w(lpszDir), debugstr_w(lpszFile)); 200 201 /* Invalid parameters */ 202 if (!lpszDest) 203 return NULL; 204 if (!lpszDir && !lpszFile) 205 { 206 lpszDest[0] = 0; 207 return NULL; 208 } 209 210 if ((!lpszFile || !*lpszFile) && lpszDir) 211 { 212 /* Use dir only */ 213 lstrcpynW(szTemp, lpszDir, MAX_PATH); 214 } 215 else if (!lpszDir || !*lpszDir || !PathIsRelativeW(lpszFile)) 216 { 217 if (!lpszDir || !*lpszDir || *lpszFile != '\\' || PathIsUNCW(lpszFile)) 218 { 219 /* Use file only */ 220 lstrcpynW(szTemp, lpszFile, MAX_PATH); 221 } 222 else 223 { 224 bUseBoth = TRUE; 225 bStrip = TRUE; 226 } 227 } 228 else 229 bUseBoth = TRUE; 230 231 if (bUseBoth) 232 { 233 lstrcpynW(szTemp, lpszDir, MAX_PATH); 234 if (bStrip) 235 { 236 PathStripToRootW(szTemp); 237 lpszFile++; /* Skip '\' */ 238 } 239 if (!PathAddBackslashW(szTemp) || strlenW(szTemp) + strlenW(lpszFile) >= MAX_PATH) 240 { 241 lpszDest[0] = 0; 242 return NULL; 243 } 244 strcatW(szTemp, lpszFile); 245 } 246 247 PathCanonicalizeW(lpszDest, szTemp); 248 return lpszDest; 249 } 250 251 /************************************************************************* 252 * PathAddBackslashA [SHLWAPI.@] 253 * 254 * Append a backslash ('\') to a path if one doesn't exist. 255 * 256 * PARAMS 257 * lpszPath [I/O] The path to append a backslash to. 258 * 259 * RETURNS 260 * Success: The position of the last backslash in the path. 261 * Failure: NULL, if lpszPath is NULL or the path is too large. 262 */ 263 LPSTR WINAPI PathAddBackslashA(LPSTR lpszPath) 264 { 265 size_t iLen; 266 LPSTR prev = lpszPath; 267 268 TRACE("(%s)\n",debugstr_a(lpszPath)); 269 270 if (!lpszPath || (iLen = strlen(lpszPath)) >= MAX_PATH) 271 return NULL; 272 273 if (iLen) 274 { 275 do { 276 lpszPath = CharNextA(prev); 277 if (*lpszPath) 278 prev = lpszPath; 279 } while (*lpszPath); 280 if (*prev != '\\') 281 { 282 *lpszPath++ = '\\'; 283 *lpszPath = '\0'; 284 } 285 } 286 return lpszPath; 287 } 288 289 /************************************************************************* 290 * PathAddBackslashW [SHLWAPI.@] 291 * 292 * See PathAddBackslashA. 293 */ 294 LPWSTR WINAPI PathAddBackslashW( LPWSTR lpszPath ) 295 { 296 size_t iLen; 297 298 TRACE("(%s)\n",debugstr_w(lpszPath)); 299 300 if (!lpszPath || (iLen = strlenW(lpszPath)) >= MAX_PATH) 301 return NULL; 302 303 if (iLen) 304 { 305 lpszPath += iLen; 306 if (lpszPath[-1] != '\\') 307 { 308 *lpszPath++ = '\\'; 309 *lpszPath = '\0'; 310 } 311 } 312 return lpszPath; 313 } 314 315 /************************************************************************* 316 * PathBuildRootA [SHLWAPI.@] 317 * 318 * Create a root drive string (e.g. "A:\") from a drive number. 319 * 320 * PARAMS 321 * lpszPath [O] Destination for the drive string 322 * 323 * RETURNS 324 * lpszPath 325 * 326 * NOTES 327 * If lpszPath is NULL or drive is invalid, nothing is written to lpszPath. 328 */ 329 LPSTR WINAPI PathBuildRootA(LPSTR lpszPath, int drive) 330 { 331 TRACE("(%p,%d)\n", lpszPath, drive); 332 333 if (lpszPath && drive >= 0 && drive < 26) 334 { 335 lpszPath[0] = 'A' + drive; 336 lpszPath[1] = ':'; 337 lpszPath[2] = '\\'; 338 lpszPath[3] = '\0'; 339 } 340 return lpszPath; 341 } 342 343 /************************************************************************* 344 * PathBuildRootW [SHLWAPI.@] 345 * 346 * See PathBuildRootA. 347 */ 348 LPWSTR WINAPI PathBuildRootW(LPWSTR lpszPath, int drive) 349 { 350 TRACE("(%p,%d)\n", lpszPath, drive); 351 352 if (lpszPath && drive >= 0 && drive < 26) 353 { 354 lpszPath[0] = 'A' + drive; 355 lpszPath[1] = ':'; 356 lpszPath[2] = '\\'; 357 lpszPath[3] = '\0'; 358 } 359 return lpszPath; 360 } 361 362 /************************************************************************* 363 * PathFindFileNameA [SHLWAPI.@] 364 * 365 * Locate the start of the file name in a path 366 * 367 * PARAMS 368 * lpszPath [I] Path to search 369 * 370 * RETURNS 371 * A pointer to the first character of the file name 372 */ 373 LPSTR WINAPI PathFindFileNameA(LPCSTR lpszPath) 374 { 375 LPCSTR lastSlash = lpszPath; 376 377 TRACE("(%s)\n",debugstr_a(lpszPath)); 378 379 while (lpszPath && *lpszPath) 380 { 381 if ((*lpszPath == '\\' || *lpszPath == '/' || *lpszPath == ':') && 382 lpszPath[1] && lpszPath[1] != '\\' && lpszPath[1] != '/') 383 lastSlash = lpszPath + 1; 384 lpszPath = CharNextA(lpszPath); 385 } 386 return (LPSTR)lastSlash; 387 } 388 389 /************************************************************************* 390 * PathFindFileNameW [SHLWAPI.@] 391 * 392 * See PathFindFileNameA. 393 */ 394 LPWSTR WINAPI PathFindFileNameW(LPCWSTR lpszPath) 395 { 396 LPCWSTR lastSlash = lpszPath; 397 398 TRACE("(%s)\n",debugstr_w(lpszPath)); 399 400 while (lpszPath && *lpszPath) 401 { 402 if ((*lpszPath == '\\' || *lpszPath == '/' || *lpszPath == ':') && 403 lpszPath[1] && lpszPath[1] != '\\' && lpszPath[1] != '/') 404 lastSlash = lpszPath + 1; 405 lpszPath++; 406 } 407 return (LPWSTR)lastSlash; 408 } 409 410 /************************************************************************* 411 * PathFindExtensionA [SHLWAPI.@] 412 * 413 * Locate the start of the file extension in a path 414 * 415 * PARAMS 416 * lpszPath [I] The path to search 417 * 418 * RETURNS 419 * A pointer to the first character of the extension, the end of 420 * the string if the path has no extension, or NULL If lpszPath is NULL 421 */ 422 LPSTR WINAPI PathFindExtensionA( LPCSTR lpszPath ) 423 { 424 LPCSTR lastpoint = NULL; 425 426 TRACE("(%s)\n", debugstr_a(lpszPath)); 427 428 if (lpszPath) 429 { 430 while (*lpszPath) 431 { 432 if (*lpszPath == '\\' || *lpszPath==' ') 433 lastpoint = NULL; 434 else if (*lpszPath == '.') 435 lastpoint = lpszPath; 436 lpszPath = CharNextA(lpszPath); 437 } 438 } 439 return (LPSTR)(lastpoint ? lastpoint : lpszPath); 440 } 441 442 /************************************************************************* 443 * PathFindExtensionW [SHLWAPI.@] 444 * 445 * See PathFindExtensionA. 446 */ 447 LPWSTR WINAPI PathFindExtensionW( LPCWSTR lpszPath ) 448 { 449 LPCWSTR lastpoint = NULL; 450 451 TRACE("(%s)\n", debugstr_w(lpszPath)); 452 453 if (lpszPath) 454 { 455 while (*lpszPath) 456 { 457 if (*lpszPath == '\\' || *lpszPath==' ') 458 lastpoint = NULL; 459 else if (*lpszPath == '.') 460 lastpoint = lpszPath; 461 lpszPath++; 462 } 463 } 464 return (LPWSTR)(lastpoint ? lastpoint : lpszPath); 465 } 466 467 /************************************************************************* 468 * PathGetArgsA [SHLWAPI.@] 469 * 470 * Find the next argument in a string delimited by spaces. 471 * 472 * PARAMS 473 * lpszPath [I] The string to search for arguments in 474 * 475 * RETURNS 476 * The start of the next argument in lpszPath, or NULL if lpszPath is NULL 477 * 478 * NOTES 479 * Spaces in quoted strings are ignored as delimiters. 480 */ 481 LPSTR WINAPI PathGetArgsA(LPCSTR lpszPath) 482 { 483 BOOL bSeenQuote = FALSE; 484 485 TRACE("(%s)\n",debugstr_a(lpszPath)); 486 487 if (lpszPath) 488 { 489 while (*lpszPath) 490 { 491 if ((*lpszPath==' ') && !bSeenQuote) 492 return (LPSTR)lpszPath + 1; 493 if (*lpszPath == '"') 494 bSeenQuote = !bSeenQuote; 495 lpszPath = CharNextA(lpszPath); 496 } 497 } 498 return (LPSTR)lpszPath; 499 } 500 501 /************************************************************************* 502 * PathGetArgsW [SHLWAPI.@] 503 * 504 * See PathGetArgsA. 505 */ 506 LPWSTR WINAPI PathGetArgsW(LPCWSTR lpszPath) 507 { 508 BOOL bSeenQuote = FALSE; 509 510 TRACE("(%s)\n",debugstr_w(lpszPath)); 511 512 if (lpszPath) 513 { 514 while (*lpszPath) 515 { 516 if ((*lpszPath==' ') && !bSeenQuote) 517 return (LPWSTR)lpszPath + 1; 518 if (*lpszPath == '"') 519 bSeenQuote = !bSeenQuote; 520 lpszPath++; 521 } 522 } 523 return (LPWSTR)lpszPath; 524 } 525 526 /************************************************************************* 527 * PathGetDriveNumberA [SHLWAPI.@] 528 * 529 * Return the drive number from a path 530 * 531 * PARAMS 532 * lpszPath [I] Path to get the drive number from 533 * 534 * RETURNS 535 * Success: The drive number corresponding to the drive in the path 536 * Failure: -1, if lpszPath contains no valid drive 537 */ 538 int WINAPI PathGetDriveNumberA(LPCSTR lpszPath) 539 { 540 TRACE ("(%s)\n",debugstr_a(lpszPath)); 541 542 if (lpszPath && !IsDBCSLeadByte(*lpszPath) && lpszPath[1] == ':' && 543 tolower(*lpszPath) >= 'a' && tolower(*lpszPath) <= 'z') 544 return tolower(*lpszPath) - 'a'; 545 return -1; 546 } 547 548 /************************************************************************* 549 * PathGetDriveNumberW [SHLWAPI.@] 550 * 551 * See PathGetDriveNumberA. 552 */ 553 int WINAPI PathGetDriveNumberW(const WCHAR *path) 554 { 555 WCHAR drive; 556 557 static const WCHAR nt_prefixW[] = {'\\','\\','?','\\'}; 558 559 TRACE("(%s)\n", debugstr_w(path)); 560 561 if (!path) 562 return -1; 563 564 if (!strncmpW(path, nt_prefixW, 4)) 565 path += 4; 566 567 drive = tolowerW(path[0]); 568 if (drive < 'a' || drive > 'z' || path[1] != ':') 569 return -1; 570 571 return drive - 'a'; 572 } 573 574 /************************************************************************* 575 * PathRemoveFileSpecA [SHLWAPI.@] 576 * 577 * Remove the file specification from a path. 578 * 579 * PARAMS 580 * lpszPath [I/O] Path to remove the file spec from 581 * 582 * RETURNS 583 * TRUE If the path was valid and modified 584 * FALSE Otherwise 585 */ 586 BOOL WINAPI PathRemoveFileSpecA(LPSTR lpszPath) 587 { 588 LPSTR lpszFileSpec = lpszPath; 589 BOOL bModified = FALSE; 590 591 TRACE("(%s)\n",debugstr_a(lpszPath)); 592 593 if(lpszPath) 594 { 595 /* Skip directory or UNC path */ 596 if (*lpszPath == '\\') 597 lpszFileSpec = ++lpszPath; 598 if (*lpszPath == '\\') 599 lpszFileSpec = ++lpszPath; 600 601 while (*lpszPath) 602 { 603 if(*lpszPath == '\\') 604 lpszFileSpec = lpszPath; /* Skip dir */ 605 else if(*lpszPath == ':') 606 { 607 lpszFileSpec = ++lpszPath; /* Skip drive */ 608 if (*lpszPath == '\\') 609 lpszFileSpec++; 610 } 611 if (!(lpszPath = CharNextA(lpszPath))) 612 break; 613 } 614 615 if (*lpszFileSpec) 616 { 617 *lpszFileSpec = '\0'; 618 bModified = TRUE; 619 } 620 } 621 return bModified; 622 } 623 624 /************************************************************************* 625 * PathRemoveFileSpecW [SHLWAPI.@] 626 * 627 * See PathRemoveFileSpecA. 628 */ 629 BOOL WINAPI PathRemoveFileSpecW(LPWSTR lpszPath) 630 { 631 LPWSTR lpszFileSpec = lpszPath; 632 BOOL bModified = FALSE; 633 634 TRACE("(%s)\n",debugstr_w(lpszPath)); 635 636 if(lpszPath) 637 { 638 /* Skip directory or UNC path */ 639 if (*lpszPath == '\\') 640 lpszFileSpec = ++lpszPath; 641 if (*lpszPath == '\\') 642 lpszFileSpec = ++lpszPath; 643 644 while (*lpszPath) 645 { 646 if(*lpszPath == '\\') 647 lpszFileSpec = lpszPath; /* Skip dir */ 648 else if(*lpszPath == ':') 649 { 650 lpszFileSpec = ++lpszPath; /* Skip drive */ 651 if (*lpszPath == '\\') 652 lpszFileSpec++; 653 } 654 lpszPath++; 655 } 656 657 if (*lpszFileSpec) 658 { 659 *lpszFileSpec = '\0'; 660 bModified = TRUE; 661 } 662 } 663 return bModified; 664 } 665 666 /************************************************************************* 667 * PathStripPathA [SHLWAPI.@] 668 * 669 * Remove the initial path from the beginning of a filename 670 * 671 * PARAMS 672 * lpszPath [I/O] Path to remove the initial path from 673 * 674 * RETURNS 675 * Nothing. 676 */ 677 void WINAPI PathStripPathA(LPSTR lpszPath) 678 { 679 TRACE("(%s)\n", debugstr_a(lpszPath)); 680 681 if (lpszPath) 682 { 683 LPSTR lpszFileName = PathFindFileNameA(lpszPath); 684 if(lpszFileName != lpszPath) 685 RtlMoveMemory(lpszPath, lpszFileName, strlen(lpszFileName)+1); 686 } 687 } 688 689 /************************************************************************* 690 * PathStripPathW [SHLWAPI.@] 691 * 692 * See PathStripPathA. 693 */ 694 void WINAPI PathStripPathW(LPWSTR lpszPath) 695 { 696 LPWSTR lpszFileName; 697 698 TRACE("(%s)\n", debugstr_w(lpszPath)); 699 lpszFileName = PathFindFileNameW(lpszPath); 700 if(lpszFileName != lpszPath) 701 RtlMoveMemory(lpszPath, lpszFileName, (strlenW(lpszFileName)+1)*sizeof(WCHAR)); 702 } 703 704 /************************************************************************* 705 * PathStripToRootA [SHLWAPI.@] 706 * 707 * Reduce a path to its root. 708 * 709 * PARAMS 710 * lpszPath [I/O] the path to reduce 711 * 712 * RETURNS 713 * Success: TRUE if the stripped path is a root path 714 * Failure: FALSE if the path cannot be stripped or is NULL 715 */ 716 BOOL WINAPI PathStripToRootA(LPSTR lpszPath) 717 { 718 TRACE("(%s)\n", debugstr_a(lpszPath)); 719 720 if (!lpszPath) 721 return FALSE; 722 while(!PathIsRootA(lpszPath)) 723 if (!PathRemoveFileSpecA(lpszPath)) 724 return FALSE; 725 return TRUE; 726 } 727 728 /************************************************************************* 729 * PathStripToRootW [SHLWAPI.@] 730 * 731 * See PathStripToRootA. 732 */ 733 BOOL WINAPI PathStripToRootW(LPWSTR lpszPath) 734 { 735 TRACE("(%s)\n", debugstr_w(lpszPath)); 736 737 if (!lpszPath) 738 return FALSE; 739 while(!PathIsRootW(lpszPath)) 740 if (!PathRemoveFileSpecW(lpszPath)) 741 return FALSE; 742 return TRUE; 743 } 744 745 /************************************************************************* 746 * PathRemoveArgsA [SHLWAPI.@] 747 * 748 * Strip space separated arguments from a path. 749 * 750 * PARAMS 751 * lpszPath [I/O] Path to remove arguments from 752 * 753 * RETURNS 754 * Nothing. 755 */ 756 void WINAPI PathRemoveArgsA(LPSTR lpszPath) 757 { 758 TRACE("(%s)\n",debugstr_a(lpszPath)); 759 760 if(lpszPath) 761 { 762 LPSTR lpszArgs = PathGetArgsA(lpszPath); 763 if (*lpszArgs) 764 lpszArgs[-1] = '\0'; 765 else 766 { 767 LPSTR lpszLastChar = CharPrevA(lpszPath, lpszArgs); 768 if(*lpszLastChar == ' ') 769 *lpszLastChar = '\0'; 770 } 771 } 772 } 773 774 /************************************************************************* 775 * PathRemoveArgsW [SHLWAPI.@] 776 * 777 * See PathRemoveArgsA. 778 */ 779 void WINAPI PathRemoveArgsW(LPWSTR lpszPath) 780 { 781 TRACE("(%s)\n",debugstr_w(lpszPath)); 782 783 if(lpszPath) 784 { 785 LPWSTR lpszArgs = PathGetArgsW(lpszPath); 786 if (*lpszArgs || (lpszArgs > lpszPath && lpszArgs[-1] == ' ')) 787 lpszArgs[-1] = '\0'; 788 } 789 } 790 791 /************************************************************************* 792 * PathRemoveExtensionA [SHLWAPI.@] 793 * 794 * Remove the file extension from a path 795 * 796 * PARAMS 797 * lpszPath [I/O] Path to remove the extension from 798 * 799 * NOTES 800 * The NUL terminator must be written only if extension exists 801 * and if the pointed character is not already NUL. 802 * 803 * RETURNS 804 * Nothing. 805 */ 806 void WINAPI PathRemoveExtensionA(LPSTR lpszPath) 807 { 808 TRACE("(%s)\n", debugstr_a(lpszPath)); 809 810 if (lpszPath) 811 { 812 lpszPath = PathFindExtensionA(lpszPath); 813 if (lpszPath && *lpszPath != '\0') 814 *lpszPath = '\0'; 815 } 816 } 817 818 /************************************************************************* 819 * PathRemoveExtensionW [SHLWAPI.@] 820 * 821 * See PathRemoveExtensionA. 822 */ 823 void WINAPI PathRemoveExtensionW(LPWSTR lpszPath) 824 { 825 TRACE("(%s)\n", debugstr_w(lpszPath)); 826 827 if (lpszPath) 828 { 829 lpszPath = PathFindExtensionW(lpszPath); 830 if (lpszPath && *lpszPath != '\0') 831 *lpszPath = '\0'; 832 } 833 } 834 835 /************************************************************************* 836 * PathRemoveBackslashA [SHLWAPI.@] 837 * 838 * Remove a trailing backslash from a path. 839 * 840 * PARAMS 841 * lpszPath [I/O] Path to remove backslash from 842 * 843 * RETURNS 844 * Success: A pointer to the end of the path 845 * Failure: NULL, if lpszPath is NULL 846 */ 847 LPSTR WINAPI PathRemoveBackslashA( LPSTR lpszPath ) 848 { 849 LPSTR szTemp = NULL; 850 851 TRACE("(%s)\n", debugstr_a(lpszPath)); 852 853 if(lpszPath) 854 { 855 szTemp = CharPrevA(lpszPath, lpszPath + strlen(lpszPath)); 856 if (!PathIsRootA(lpszPath) && *szTemp == '\\') 857 *szTemp = '\0'; 858 } 859 return szTemp; 860 } 861 862 /************************************************************************* 863 * PathRemoveBackslashW [SHLWAPI.@] 864 * 865 * See PathRemoveBackslashA. 866 */ 867 LPWSTR WINAPI PathRemoveBackslashW( LPWSTR lpszPath ) 868 { 869 LPWSTR szTemp = NULL; 870 871 TRACE("(%s)\n", debugstr_w(lpszPath)); 872 873 if(lpszPath) 874 { 875 szTemp = lpszPath + strlenW(lpszPath); 876 if (szTemp > lpszPath) szTemp--; 877 if (!PathIsRootW(lpszPath) && *szTemp == '\\') 878 *szTemp = '\0'; 879 } 880 return szTemp; 881 } 882 883 /************************************************************************* 884 * PathRemoveBlanksA [SHLWAPI.@] 885 * 886 * Remove Spaces from the start and end of a path. 887 * 888 * PARAMS 889 * lpszPath [I/O] Path to strip blanks from 890 * 891 * RETURNS 892 * Nothing. 893 */ 894 VOID WINAPI PathRemoveBlanksA(LPSTR lpszPath) 895 { 896 TRACE("(%s)\n", debugstr_a(lpszPath)); 897 898 if(lpszPath && *lpszPath) 899 { 900 LPSTR start = lpszPath; 901 902 while (*lpszPath == ' ') 903 lpszPath = CharNextA(lpszPath); 904 905 while(*lpszPath) 906 *start++ = *lpszPath++; 907 908 if (start != lpszPath) 909 while (start[-1] == ' ') 910 start--; 911 *start = '\0'; 912 } 913 } 914 915 /************************************************************************* 916 * PathRemoveBlanksW [SHLWAPI.@] 917 * 918 * See PathRemoveBlanksA. 919 */ 920 VOID WINAPI PathRemoveBlanksW(LPWSTR lpszPath) 921 { 922 TRACE("(%s)\n", debugstr_w(lpszPath)); 923 924 if(lpszPath && *lpszPath) 925 { 926 LPWSTR start = lpszPath; 927 928 while (*lpszPath == ' ') 929 lpszPath++; 930 931 while(*lpszPath) 932 *start++ = *lpszPath++; 933 934 if (start != lpszPath) 935 while (start[-1] == ' ') 936 start--; 937 *start = '\0'; 938 } 939 } 940 941 /************************************************************************* 942 * PathQuoteSpacesA [SHLWAPI.@] 943 * 944 * Surround a path containing spaces in quotes. 945 * 946 * PARAMS 947 * lpszPath [I/O] Path to quote 948 * 949 * RETURNS 950 * Nothing. 951 * 952 * NOTES 953 * The path is not changed if it is invalid or has no spaces. 954 */ 955 VOID WINAPI PathQuoteSpacesA(LPSTR lpszPath) 956 { 957 TRACE("(%s)\n", debugstr_a(lpszPath)); 958 959 if(lpszPath && StrChrA(lpszPath,' ')) 960 { 961 size_t iLen = strlen(lpszPath) + 1; 962 963 if (iLen + 2 < MAX_PATH) 964 { 965 memmove(lpszPath + 1, lpszPath, iLen); 966 lpszPath[0] = '"'; 967 lpszPath[iLen] = '"'; 968 lpszPath[iLen + 1] = '\0'; 969 } 970 } 971 } 972 973 /************************************************************************* 974 * PathQuoteSpacesW [SHLWAPI.@] 975 * 976 * See PathQuoteSpacesA. 977 */ 978 VOID WINAPI PathQuoteSpacesW(LPWSTR lpszPath) 979 { 980 TRACE("(%s)\n", debugstr_w(lpszPath)); 981 982 if(lpszPath && StrChrW(lpszPath,' ')) 983 { 984 int iLen = strlenW(lpszPath) + 1; 985 986 if (iLen + 2 < MAX_PATH) 987 { 988 memmove(lpszPath + 1, lpszPath, iLen * sizeof(WCHAR)); 989 lpszPath[0] = '"'; 990 lpszPath[iLen] = '"'; 991 lpszPath[iLen + 1] = '\0'; 992 } 993 } 994 } 995 996 /************************************************************************* 997 * PathUnquoteSpacesA [SHLWAPI.@] 998 * 999 * Remove quotes ("") from around a path, if present. 1000 * 1001 * PARAMS 1002 * lpszPath [I/O] Path to strip quotes from 1003 * 1004 * RETURNS 1005 * Nothing 1006 * 1007 * NOTES 1008 * If the path contains a single quote only, an empty string will result. 1009 * Otherwise quotes are only removed if they appear at the start and end 1010 * of the path. 1011 */ 1012 VOID WINAPI PathUnquoteSpacesA(LPSTR lpszPath) 1013 { 1014 TRACE("(%s)\n", debugstr_a(lpszPath)); 1015 1016 if (lpszPath && *lpszPath == '"') 1017 { 1018 DWORD dwLen = strlen(lpszPath) - 1; 1019 1020 if (lpszPath[dwLen] == '"') 1021 { 1022 lpszPath[dwLen] = '\0'; 1023 for (; *lpszPath; lpszPath++) 1024 *lpszPath = lpszPath[1]; 1025 } 1026 } 1027 } 1028 1029 /************************************************************************* 1030 * PathUnquoteSpacesW [SHLWAPI.@] 1031 * 1032 * See PathUnquoteSpacesA. 1033 */ 1034 VOID WINAPI PathUnquoteSpacesW(LPWSTR lpszPath) 1035 { 1036 TRACE("(%s)\n", debugstr_w(lpszPath)); 1037 1038 if (lpszPath && *lpszPath == '"') 1039 { 1040 DWORD dwLen = strlenW(lpszPath) - 1; 1041 1042 if (lpszPath[dwLen] == '"') 1043 { 1044 lpszPath[dwLen] = '\0'; 1045 for (; *lpszPath; lpszPath++) 1046 *lpszPath = lpszPath[1]; 1047 } 1048 } 1049 } 1050 1051 /************************************************************************* 1052 * PathParseIconLocationA [SHLWAPI.@] 1053 * 1054 * Parse the location of an icon from a path. 1055 * 1056 * PARAMS 1057 * lpszPath [I/O] The path to parse the icon location from. 1058 * 1059 * RETURNS 1060 * Success: The number of the icon 1061 * Failure: 0 if the path does not contain an icon location or is NULL 1062 * 1063 * NOTES 1064 * The path has surrounding quotes and spaces removed regardless 1065 * of whether the call succeeds or not. 1066 */ 1067 int WINAPI PathParseIconLocationA(LPSTR lpszPath) 1068 { 1069 int iRet = 0; 1070 LPSTR lpszComma; 1071 1072 TRACE("(%s)\n", debugstr_a(lpszPath)); 1073 1074 if (lpszPath) 1075 { 1076 if ((lpszComma = strchr(lpszPath, ','))) 1077 { 1078 *lpszComma++ = '\0'; 1079 iRet = StrToIntA(lpszComma); 1080 } 1081 PathUnquoteSpacesA(lpszPath); 1082 PathRemoveBlanksA(lpszPath); 1083 } 1084 return iRet; 1085 } 1086 1087 /************************************************************************* 1088 * PathParseIconLocationW [SHLWAPI.@] 1089 * 1090 * See PathParseIconLocationA. 1091 */ 1092 int WINAPI PathParseIconLocationW(LPWSTR lpszPath) 1093 { 1094 int iRet = 0; 1095 LPWSTR lpszComma; 1096 1097 TRACE("(%s)\n", debugstr_w(lpszPath)); 1098 1099 if (lpszPath) 1100 { 1101 if ((lpszComma = StrChrW(lpszPath, ','))) 1102 { 1103 *lpszComma++ = '\0'; 1104 iRet = StrToIntW(lpszComma); 1105 } 1106 PathUnquoteSpacesW(lpszPath); 1107 PathRemoveBlanksW(lpszPath); 1108 } 1109 return iRet; 1110 } 1111 1112 /************************************************************************* 1113 * @ [SHLWAPI.4] 1114 * 1115 * Unicode version of PathFileExistsDefExtA. 1116 */ 1117 BOOL WINAPI PathFileExistsDefExtW(LPWSTR lpszPath,DWORD dwWhich) 1118 { 1119 static const WCHAR pszExts[][5] = { { '.', 'p', 'i', 'f', 0}, 1120 { '.', 'c', 'o', 'm', 0}, 1121 { '.', 'e', 'x', 'e', 0}, 1122 { '.', 'b', 'a', 't', 0}, 1123 { '.', 'l', 'n', 'k', 0}, 1124 { '.', 'c', 'm', 'd', 0}, 1125 { 0, 0, 0, 0, 0} }; 1126 1127 TRACE("(%s,%d)\n", debugstr_w(lpszPath), dwWhich); 1128 1129 if (!lpszPath || PathIsUNCServerW(lpszPath) || PathIsUNCServerShareW(lpszPath)) 1130 return FALSE; 1131 1132 if (dwWhich) 1133 { 1134 LPCWSTR szExt = PathFindExtensionW(lpszPath); 1135 #ifndef __REACTOS__ 1136 if (!*szExt || dwWhich & 0x40) 1137 #else 1138 if (!*szExt || dwWhich & WHICH_OPTIONAL) 1139 #endif 1140 { 1141 size_t iChoose = 0; 1142 int iLen = lstrlenW(lpszPath); 1143 if (iLen > (MAX_PATH - 5)) 1144 return FALSE; 1145 #ifndef __REACTOS__ 1146 while ( (dwWhich & 0x1) && pszExts[iChoose][0] ) 1147 #else 1148 while (pszExts[iChoose][0]) 1149 #endif 1150 { 1151 #ifdef __REACTOS__ 1152 if (dwWhich & 0x1) 1153 { 1154 if (GetFileAttributes(lpszPath) != FILE_ATTRIBUTE_DIRECTORY) 1155 #endif 1156 lstrcpyW(lpszPath + iLen, pszExts[iChoose]); 1157 if (PathFileExistsW(lpszPath)) 1158 return TRUE; 1159 #ifdef __REACTOS__ 1160 } 1161 #endif 1162 iChoose++; 1163 dwWhich >>= 1; 1164 } 1165 *(lpszPath + iLen) = (WCHAR)'\0'; 1166 return FALSE; 1167 } 1168 } 1169 return PathFileExistsW(lpszPath); 1170 } 1171 1172 /************************************************************************* 1173 * @ [SHLWAPI.3] 1174 * 1175 * Determine if a file exists locally and is of an executable type. 1176 * 1177 * PARAMS 1178 * lpszPath [I/O] File to search for 1179 * dwWhich [I] Type of executable to search for 1180 * 1181 * RETURNS 1182 * TRUE If the file was found. lpszPath contains the file name. 1183 * FALSE Otherwise. 1184 * 1185 * NOTES 1186 * lpszPath is modified in place and must be at least MAX_PATH in length. 1187 * If the function returns FALSE, the path is modified to its original state. 1188 * If the given path contains an extension or dwWhich is 0, executable 1189 * extensions are not checked. 1190 * 1191 * Ordinals 3-6 are a classic case of MS exposing limited functionality to 1192 * users (here through PathFindOnPathA()) and keeping advanced functionality for 1193 * their own developers exclusive use. Monopoly, anyone? 1194 */ 1195 BOOL WINAPI PathFileExistsDefExtA(LPSTR lpszPath,DWORD dwWhich) 1196 { 1197 BOOL bRet = FALSE; 1198 1199 TRACE("(%s,%d)\n", debugstr_a(lpszPath), dwWhich); 1200 1201 if (lpszPath) 1202 { 1203 WCHAR szPath[MAX_PATH]; 1204 MultiByteToWideChar(CP_ACP,0,lpszPath,-1,szPath,MAX_PATH); 1205 bRet = PathFileExistsDefExtW(szPath, dwWhich); 1206 if (bRet) 1207 WideCharToMultiByte(CP_ACP,0,szPath,-1,lpszPath,MAX_PATH,0,0); 1208 } 1209 return bRet; 1210 } 1211 1212 /************************************************************************* 1213 * SHLWAPI_PathFindInOtherDirs 1214 * 1215 * Internal helper for SHLWAPI_PathFindOnPathExA/W. 1216 */ 1217 static BOOL SHLWAPI_PathFindInOtherDirs(LPWSTR lpszFile, DWORD dwWhich) 1218 { 1219 static const WCHAR szSystem[] = { 'S','y','s','t','e','m','\0'}; 1220 static const WCHAR szPath[] = { 'P','A','T','H','\0'}; 1221 DWORD dwLenPATH; 1222 LPCWSTR lpszCurr; 1223 WCHAR *lpszPATH; 1224 WCHAR buff[MAX_PATH]; 1225 1226 TRACE("(%s,%08x)\n", debugstr_w(lpszFile), dwWhich); 1227 1228 /* Try system directories */ 1229 GetSystemDirectoryW(buff, MAX_PATH); 1230 if (!PathAppendW(buff, lpszFile)) 1231 return FALSE; 1232 if (PathFileExistsDefExtW(buff, dwWhich)) 1233 { 1234 strcpyW(lpszFile, buff); 1235 return TRUE; 1236 } 1237 GetWindowsDirectoryW(buff, MAX_PATH); 1238 if (!PathAppendW(buff, szSystem ) || !PathAppendW(buff, lpszFile)) 1239 return FALSE; 1240 if (PathFileExistsDefExtW(buff, dwWhich)) 1241 { 1242 strcpyW(lpszFile, buff); 1243 return TRUE; 1244 } 1245 GetWindowsDirectoryW(buff, MAX_PATH); 1246 if (!PathAppendW(buff, lpszFile)) 1247 return FALSE; 1248 if (PathFileExistsDefExtW(buff, dwWhich)) 1249 { 1250 strcpyW(lpszFile, buff); 1251 return TRUE; 1252 } 1253 /* Try dirs listed in %PATH% */ 1254 dwLenPATH = GetEnvironmentVariableW(szPath, buff, MAX_PATH); 1255 1256 if (!dwLenPATH || !(lpszPATH = HeapAlloc(GetProcessHeap(), 0, (dwLenPATH + 1) * sizeof (WCHAR)))) 1257 return FALSE; 1258 1259 GetEnvironmentVariableW(szPath, lpszPATH, dwLenPATH + 1); 1260 lpszCurr = lpszPATH; 1261 while (lpszCurr) 1262 { 1263 LPCWSTR lpszEnd = lpszCurr; 1264 LPWSTR pBuff = buff; 1265 1266 while (*lpszEnd == ' ') 1267 lpszEnd++; 1268 while (*lpszEnd && *lpszEnd != ';') 1269 *pBuff++ = *lpszEnd++; 1270 *pBuff = '\0'; 1271 1272 if (*lpszEnd) 1273 lpszCurr = lpszEnd + 1; 1274 else 1275 lpszCurr = NULL; /* Last Path, terminate after this */ 1276 1277 if (!PathAppendW(buff, lpszFile)) 1278 { 1279 HeapFree(GetProcessHeap(), 0, lpszPATH); 1280 return FALSE; 1281 } 1282 if (PathFileExistsDefExtW(buff, dwWhich)) 1283 { 1284 strcpyW(lpszFile, buff); 1285 HeapFree(GetProcessHeap(), 0, lpszPATH); 1286 return TRUE; 1287 } 1288 } 1289 HeapFree(GetProcessHeap(), 0, lpszPATH); 1290 return FALSE; 1291 } 1292 1293 /************************************************************************* 1294 * @ [SHLWAPI.5] 1295 * 1296 * Search a range of paths for a specific type of executable. 1297 * 1298 * PARAMS 1299 * lpszFile [I/O] File to search for 1300 * lppszOtherDirs [I] Other directories to look in 1301 * dwWhich [I] Type of executable to search for 1302 * 1303 * RETURNS 1304 * Success: TRUE. The path to the executable is stored in lpszFile. 1305 * Failure: FALSE. The path to the executable is unchanged. 1306 */ 1307 BOOL WINAPI PathFindOnPathExA(LPSTR lpszFile,LPCSTR *lppszOtherDirs,DWORD dwWhich) 1308 { 1309 WCHAR szFile[MAX_PATH]; 1310 WCHAR buff[MAX_PATH]; 1311 1312 TRACE("(%s,%p,%08x)\n", debugstr_a(lpszFile), lppszOtherDirs, dwWhich); 1313 1314 if (!lpszFile || !PathIsFileSpecA(lpszFile)) 1315 return FALSE; 1316 1317 MultiByteToWideChar(CP_ACP,0,lpszFile,-1,szFile,MAX_PATH); 1318 1319 /* Search provided directories first */ 1320 if (lppszOtherDirs && *lppszOtherDirs) 1321 { 1322 WCHAR szOther[MAX_PATH]; 1323 LPCSTR *lpszOtherPath = lppszOtherDirs; 1324 1325 while (lpszOtherPath && *lpszOtherPath && (*lpszOtherPath)[0]) 1326 { 1327 MultiByteToWideChar(CP_ACP,0,*lpszOtherPath,-1,szOther,MAX_PATH); 1328 PathCombineW(buff, szOther, szFile); 1329 if (PathFileExistsDefExtW(buff, dwWhich)) 1330 { 1331 WideCharToMultiByte(CP_ACP,0,buff,-1,lpszFile,MAX_PATH,0,0); 1332 return TRUE; 1333 } 1334 lpszOtherPath++; 1335 } 1336 } 1337 /* Not found, try system and path dirs */ 1338 if (SHLWAPI_PathFindInOtherDirs(szFile, dwWhich)) 1339 { 1340 WideCharToMultiByte(CP_ACP,0,szFile,-1,lpszFile,MAX_PATH,0,0); 1341 return TRUE; 1342 } 1343 return FALSE; 1344 } 1345 1346 /************************************************************************* 1347 * @ [SHLWAPI.6] 1348 * 1349 * Unicode version of PathFindOnPathExA. 1350 */ 1351 BOOL WINAPI PathFindOnPathExW(LPWSTR lpszFile,LPCWSTR *lppszOtherDirs,DWORD dwWhich) 1352 { 1353 WCHAR buff[MAX_PATH]; 1354 1355 TRACE("(%s,%p,%08x)\n", debugstr_w(lpszFile), lppszOtherDirs, dwWhich); 1356 1357 if (!lpszFile || !PathIsFileSpecW(lpszFile)) 1358 return FALSE; 1359 1360 /* Search provided directories first */ 1361 if (lppszOtherDirs && *lppszOtherDirs) 1362 { 1363 LPCWSTR *lpszOtherPath = lppszOtherDirs; 1364 while (lpszOtherPath && *lpszOtherPath && (*lpszOtherPath)[0]) 1365 { 1366 PathCombineW(buff, *lpszOtherPath, lpszFile); 1367 if (PathFileExistsDefExtW(buff, dwWhich)) 1368 { 1369 strcpyW(lpszFile, buff); 1370 return TRUE; 1371 } 1372 lpszOtherPath++; 1373 } 1374 } 1375 /* Not found, try system and path dirs */ 1376 return SHLWAPI_PathFindInOtherDirs(lpszFile, dwWhich); 1377 } 1378 1379 /************************************************************************* 1380 * PathFindOnPathA [SHLWAPI.@] 1381 * 1382 * Search a range of paths for an executable. 1383 * 1384 * PARAMS 1385 * lpszFile [I/O] File to search for 1386 * lppszOtherDirs [I] Other directories to look in 1387 * 1388 * RETURNS 1389 * Success: TRUE. The path to the executable is stored in lpszFile. 1390 * Failure: FALSE. The path to the executable is unchanged. 1391 */ 1392 BOOL WINAPI PathFindOnPathA(LPSTR lpszFile, LPCSTR *lppszOtherDirs) 1393 { 1394 TRACE("(%s,%p)\n", debugstr_a(lpszFile), lppszOtherDirs); 1395 return PathFindOnPathExA(lpszFile, lppszOtherDirs, 0); 1396 } 1397 1398 /************************************************************************* 1399 * PathFindOnPathW [SHLWAPI.@] 1400 * 1401 * See PathFindOnPathA. 1402 */ 1403 BOOL WINAPI PathFindOnPathW(LPWSTR lpszFile, LPCWSTR *lppszOtherDirs) 1404 { 1405 TRACE("(%s,%p)\n", debugstr_w(lpszFile), lppszOtherDirs); 1406 return PathFindOnPathExW(lpszFile,lppszOtherDirs, 0); 1407 } 1408 1409 /************************************************************************* 1410 * PathCompactPathExA [SHLWAPI.@] 1411 * 1412 * Compact a path into a given number of characters. 1413 * 1414 * PARAMS 1415 * lpszDest [O] Destination for compacted path 1416 * lpszPath [I] Source path 1417 * cchMax [I] Maximum size of compacted path 1418 * dwFlags [I] Reserved 1419 * 1420 * RETURNS 1421 * Success: TRUE. The compacted path is written to lpszDest. 1422 * Failure: FALSE. lpszPath is undefined. 1423 * 1424 * NOTES 1425 * If cchMax is given as 0, lpszDest will still be NUL terminated. 1426 * 1427 * The Win32 version of this function contains a bug: When cchMax == 7, 1428 * 8 bytes will be written to lpszDest. This bug is fixed in the Wine 1429 * implementation. 1430 * 1431 * Some relative paths will be different when cchMax == 5 or 6. This occurs 1432 * because Win32 will insert a "\" in lpszDest, even if one is 1433 * not present in the original path. 1434 */ 1435 BOOL WINAPI PathCompactPathExA(LPSTR lpszDest, LPCSTR lpszPath, 1436 UINT cchMax, DWORD dwFlags) 1437 { 1438 BOOL bRet = FALSE; 1439 1440 TRACE("(%p,%s,%d,0x%08x)\n", lpszDest, debugstr_a(lpszPath), cchMax, dwFlags); 1441 1442 if (lpszPath && lpszDest) 1443 { 1444 WCHAR szPath[MAX_PATH]; 1445 WCHAR szDest[MAX_PATH]; 1446 1447 MultiByteToWideChar(CP_ACP,0,lpszPath,-1,szPath,MAX_PATH); 1448 szDest[0] = '\0'; 1449 bRet = PathCompactPathExW(szDest, szPath, cchMax, dwFlags); 1450 WideCharToMultiByte(CP_ACP,0,szDest,-1,lpszDest,MAX_PATH,0,0); 1451 } 1452 return bRet; 1453 } 1454 1455 /************************************************************************* 1456 * PathCompactPathExW [SHLWAPI.@] 1457 * 1458 * See PathCompactPathExA. 1459 */ 1460 BOOL WINAPI PathCompactPathExW(LPWSTR lpszDest, LPCWSTR lpszPath, 1461 UINT cchMax, DWORD dwFlags) 1462 { 1463 static const WCHAR szEllipses[] = { '.', '.', '.', '\0' }; 1464 LPCWSTR lpszFile; 1465 DWORD dwLen, dwFileLen = 0; 1466 1467 TRACE("(%p,%s,%d,0x%08x)\n", lpszDest, debugstr_w(lpszPath), cchMax, dwFlags); 1468 1469 if (!lpszPath) 1470 return FALSE; 1471 1472 if (!lpszDest) 1473 { 1474 WARN("Invalid lpszDest would crash under Win32!\n"); 1475 return FALSE; 1476 } 1477 1478 *lpszDest = '\0'; 1479 1480 if (cchMax < 2) 1481 return TRUE; 1482 1483 dwLen = strlenW(lpszPath) + 1; 1484 1485 if (dwLen < cchMax) 1486 { 1487 /* Don't need to compact */ 1488 memcpy(lpszDest, lpszPath, dwLen * sizeof(WCHAR)); 1489 return TRUE; 1490 } 1491 1492 /* Path must be compacted to fit into lpszDest */ 1493 lpszFile = PathFindFileNameW(lpszPath); 1494 dwFileLen = lpszPath + dwLen - lpszFile; 1495 1496 if (dwFileLen == dwLen) 1497 { 1498 /* No root in psth */ 1499 if (cchMax <= 4) 1500 { 1501 while (--cchMax > 0) /* No room left for anything but ellipses */ 1502 *lpszDest++ = '.'; 1503 *lpszDest = '\0'; 1504 return TRUE; 1505 } 1506 /* Compact the file name with ellipses at the end */ 1507 cchMax -= 4; 1508 memcpy(lpszDest, lpszFile, cchMax * sizeof(WCHAR)); 1509 strcpyW(lpszDest + cchMax, szEllipses); 1510 return TRUE; 1511 } 1512 /* We have a root in the path */ 1513 lpszFile--; /* Start compacted filename with the path separator */ 1514 dwFileLen++; 1515 1516 if (dwFileLen + 3 > cchMax) 1517 { 1518 /* Compact the file name */ 1519 if (cchMax <= 4) 1520 { 1521 while (--cchMax > 0) /* No room left for anything but ellipses */ 1522 *lpszDest++ = '.'; 1523 *lpszDest = '\0'; 1524 return TRUE; 1525 } 1526 strcpyW(lpszDest, szEllipses); 1527 lpszDest += 3; 1528 cchMax -= 4; 1529 *lpszDest++ = *lpszFile++; 1530 if (cchMax <= 4) 1531 { 1532 while (--cchMax > 0) /* No room left for anything but ellipses */ 1533 *lpszDest++ = '.'; 1534 *lpszDest = '\0'; 1535 return TRUE; 1536 } 1537 cchMax -= 4; 1538 memcpy(lpszDest, lpszFile, cchMax * sizeof(WCHAR)); 1539 strcpyW(lpszDest + cchMax, szEllipses); 1540 return TRUE; 1541 } 1542 1543 /* Only the root needs to be Compacted */ 1544 dwLen = cchMax - dwFileLen - 3; 1545 memcpy(lpszDest, lpszPath, dwLen * sizeof(WCHAR)); 1546 strcpyW(lpszDest + dwLen, szEllipses); 1547 strcpyW(lpszDest + dwLen + 3, lpszFile); 1548 return TRUE; 1549 } 1550 1551 /************************************************************************* 1552 * PathIsRelativeA [SHLWAPI.@] 1553 * 1554 * Determine if a path is a relative path. 1555 * 1556 * PARAMS 1557 * lpszPath [I] Path to check 1558 * 1559 * RETURNS 1560 * TRUE: The path is relative, or is invalid. 1561 * FALSE: The path is not relative. 1562 */ 1563 BOOL WINAPI PathIsRelativeA (LPCSTR lpszPath) 1564 { 1565 TRACE("(%s)\n",debugstr_a(lpszPath)); 1566 1567 if (!lpszPath || !*lpszPath || IsDBCSLeadByte(*lpszPath)) 1568 return TRUE; 1569 if (*lpszPath == '\\' || (*lpszPath && lpszPath[1] == ':')) 1570 return FALSE; 1571 return TRUE; 1572 } 1573 1574 /************************************************************************* 1575 * PathIsRelativeW [SHLWAPI.@] 1576 * 1577 * See PathIsRelativeA. 1578 */ 1579 BOOL WINAPI PathIsRelativeW (LPCWSTR lpszPath) 1580 { 1581 TRACE("(%s)\n",debugstr_w(lpszPath)); 1582 1583 if (!lpszPath || !*lpszPath) 1584 return TRUE; 1585 if (*lpszPath == '\\' || (*lpszPath && lpszPath[1] == ':')) 1586 return FALSE; 1587 return TRUE; 1588 } 1589 1590 /************************************************************************* 1591 * PathIsRootA [SHLWAPI.@] 1592 * 1593 * Determine if a path is a root path. 1594 * 1595 * PARAMS 1596 * lpszPath [I] Path to check 1597 * 1598 * RETURNS 1599 * TRUE If lpszPath is valid and a root path, 1600 * FALSE Otherwise 1601 */ 1602 BOOL WINAPI PathIsRootA(LPCSTR lpszPath) 1603 { 1604 TRACE("(%s)\n", debugstr_a(lpszPath)); 1605 1606 if (lpszPath && *lpszPath) 1607 { 1608 if (*lpszPath == '\\') 1609 { 1610 if (!lpszPath[1]) 1611 return TRUE; /* \ */ 1612 else if (lpszPath[1]=='\\') 1613 { 1614 BOOL bSeenSlash = FALSE; 1615 lpszPath += 2; 1616 1617 /* Check for UNC root path */ 1618 while (*lpszPath) 1619 { 1620 if (*lpszPath == '\\') 1621 { 1622 if (bSeenSlash) 1623 return FALSE; 1624 bSeenSlash = TRUE; 1625 } 1626 lpszPath = CharNextA(lpszPath); 1627 } 1628 return TRUE; 1629 } 1630 } 1631 else if (lpszPath[1] == ':' && lpszPath[2] == '\\' && lpszPath[3] == '\0') 1632 return TRUE; /* X:\ */ 1633 } 1634 return FALSE; 1635 } 1636 1637 /************************************************************************* 1638 * PathIsRootW [SHLWAPI.@] 1639 * 1640 * See PathIsRootA. 1641 */ 1642 BOOL WINAPI PathIsRootW(LPCWSTR lpszPath) 1643 { 1644 TRACE("(%s)\n", debugstr_w(lpszPath)); 1645 1646 if (lpszPath && *lpszPath) 1647 { 1648 if (*lpszPath == '\\') 1649 { 1650 if (!lpszPath[1]) 1651 return TRUE; /* \ */ 1652 else if (lpszPath[1]=='\\') 1653 { 1654 BOOL bSeenSlash = FALSE; 1655 lpszPath += 2; 1656 1657 /* Check for UNC root path */ 1658 while (*lpszPath) 1659 { 1660 if (*lpszPath == '\\') 1661 { 1662 if (bSeenSlash) 1663 return FALSE; 1664 bSeenSlash = TRUE; 1665 } 1666 lpszPath++; 1667 } 1668 return TRUE; 1669 } 1670 } 1671 else if (lpszPath[1] == ':' && lpszPath[2] == '\\' && lpszPath[3] == '\0') 1672 return TRUE; /* X:\ */ 1673 } 1674 return FALSE; 1675 } 1676 1677 /************************************************************************* 1678 * PathIsDirectoryA [SHLWAPI.@] 1679 * 1680 * Determine if a path is a valid directory 1681 * 1682 * PARAMS 1683 * lpszPath [I] Path to check. 1684 * 1685 * RETURNS 1686 * FILE_ATTRIBUTE_DIRECTORY if lpszPath exists and can be read (See Notes) 1687 * FALSE if lpszPath is invalid or not a directory. 1688 * 1689 * NOTES 1690 * Although this function is prototyped as returning a BOOL, it returns 1691 * FILE_ATTRIBUTE_DIRECTORY for success. This means that code such as: 1692 * 1693 *| if (PathIsDirectoryA("c:\\windows\\") == TRUE) 1694 *| ... 1695 * 1696 * will always fail. 1697 */ 1698 BOOL WINAPI PathIsDirectoryA(LPCSTR lpszPath) 1699 { 1700 DWORD dwAttr; 1701 1702 TRACE("(%s)\n", debugstr_a(lpszPath)); 1703 1704 if (!lpszPath || PathIsUNCServerA(lpszPath)) 1705 return FALSE; 1706 1707 if (PathIsUNCServerShareA(lpszPath)) 1708 { 1709 FIXME("UNC Server Share not yet supported - FAILING\n"); 1710 return FALSE; 1711 } 1712 1713 if ((dwAttr = GetFileAttributesA(lpszPath)) == INVALID_FILE_ATTRIBUTES) 1714 return FALSE; 1715 return dwAttr & FILE_ATTRIBUTE_DIRECTORY; 1716 } 1717 1718 /************************************************************************* 1719 * PathIsDirectoryW [SHLWAPI.@] 1720 * 1721 * See PathIsDirectoryA. 1722 */ 1723 BOOL WINAPI PathIsDirectoryW(LPCWSTR lpszPath) 1724 { 1725 DWORD dwAttr; 1726 1727 TRACE("(%s)\n", debugstr_w(lpszPath)); 1728 1729 if (!lpszPath || PathIsUNCServerW(lpszPath)) 1730 return FALSE; 1731 1732 if (PathIsUNCServerShareW(lpszPath)) 1733 { 1734 FIXME("UNC Server Share not yet supported - FAILING\n"); 1735 return FALSE; 1736 } 1737 1738 if ((dwAttr = GetFileAttributesW(lpszPath)) == INVALID_FILE_ATTRIBUTES) 1739 return FALSE; 1740 return dwAttr & FILE_ATTRIBUTE_DIRECTORY; 1741 } 1742 1743 /************************************************************************* 1744 * PathFileExistsA [SHLWAPI.@] 1745 * 1746 * Determine if a file exists. 1747 * 1748 * PARAMS 1749 * lpszPath [I] Path to check 1750 * 1751 * RETURNS 1752 * TRUE If the file exists and is readable 1753 * FALSE Otherwise 1754 */ 1755 BOOL WINAPI PathFileExistsA(LPCSTR lpszPath) 1756 { 1757 UINT iPrevErrMode; 1758 DWORD dwAttr; 1759 1760 TRACE("(%s)\n",debugstr_a(lpszPath)); 1761 1762 if (!lpszPath) 1763 return FALSE; 1764 1765 /* Prevent a dialog box if path is on a disk that has been ejected. */ 1766 iPrevErrMode = SetErrorMode(SEM_FAILCRITICALERRORS); 1767 dwAttr = GetFileAttributesA(lpszPath); 1768 SetErrorMode(iPrevErrMode); 1769 return dwAttr != INVALID_FILE_ATTRIBUTES; 1770 } 1771 1772 /************************************************************************* 1773 * PathFileExistsW [SHLWAPI.@] 1774 * 1775 * See PathFileExistsA. 1776 */ 1777 BOOL WINAPI PathFileExistsW(LPCWSTR lpszPath) 1778 { 1779 UINT iPrevErrMode; 1780 DWORD dwAttr; 1781 1782 TRACE("(%s)\n",debugstr_w(lpszPath)); 1783 1784 if (!lpszPath) 1785 return FALSE; 1786 1787 iPrevErrMode = SetErrorMode(SEM_FAILCRITICALERRORS); 1788 dwAttr = GetFileAttributesW(lpszPath); 1789 SetErrorMode(iPrevErrMode); 1790 return dwAttr != INVALID_FILE_ATTRIBUTES; 1791 } 1792 1793 /************************************************************************* 1794 * PathFileExistsAndAttributesA [SHLWAPI.445] 1795 * 1796 * Determine if a file exists. 1797 * 1798 * PARAMS 1799 * lpszPath [I] Path to check 1800 * dwAttr [O] attributes of file 1801 * 1802 * RETURNS 1803 * TRUE If the file exists and is readable 1804 * FALSE Otherwise 1805 */ 1806 BOOL WINAPI PathFileExistsAndAttributesA(LPCSTR lpszPath, DWORD *dwAttr) 1807 { 1808 UINT iPrevErrMode; 1809 DWORD dwVal = 0; 1810 1811 TRACE("(%s %p)\n", debugstr_a(lpszPath), dwAttr); 1812 1813 if (dwAttr) 1814 *dwAttr = INVALID_FILE_ATTRIBUTES; 1815 1816 if (!lpszPath) 1817 return FALSE; 1818 1819 iPrevErrMode = SetErrorMode(SEM_FAILCRITICALERRORS); 1820 dwVal = GetFileAttributesA(lpszPath); 1821 SetErrorMode(iPrevErrMode); 1822 if (dwAttr) 1823 *dwAttr = dwVal; 1824 return (dwVal != INVALID_FILE_ATTRIBUTES); 1825 } 1826 1827 /************************************************************************* 1828 * PathFileExistsAndAttributesW [SHLWAPI.446] 1829 * 1830 * See PathFileExistsA. 1831 */ 1832 BOOL WINAPI PathFileExistsAndAttributesW(LPCWSTR lpszPath, DWORD *dwAttr) 1833 { 1834 UINT iPrevErrMode; 1835 DWORD dwVal; 1836 1837 TRACE("(%s %p)\n", debugstr_w(lpszPath), dwAttr); 1838 1839 if (!lpszPath) 1840 return FALSE; 1841 1842 iPrevErrMode = SetErrorMode(SEM_FAILCRITICALERRORS); 1843 dwVal = GetFileAttributesW(lpszPath); 1844 SetErrorMode(iPrevErrMode); 1845 if (dwAttr) 1846 *dwAttr = dwVal; 1847 return (dwVal != INVALID_FILE_ATTRIBUTES); 1848 } 1849 1850 /************************************************************************* 1851 * PathMatchSingleMaskA [internal] 1852 */ 1853 static BOOL PathMatchSingleMaskA(LPCSTR name, LPCSTR mask) 1854 { 1855 while (*name && *mask && *mask!=';') 1856 { 1857 if (*mask == '*') 1858 { 1859 do 1860 { 1861 if (PathMatchSingleMaskA(name,mask+1)) 1862 return TRUE; /* try substrings */ 1863 } while (*name++); 1864 return FALSE; 1865 } 1866 1867 if (toupper(*mask) != toupper(*name) && *mask != '?') 1868 return FALSE; 1869 1870 name = CharNextA(name); 1871 mask = CharNextA(mask); 1872 } 1873 1874 if (!*name) 1875 { 1876 while (*mask == '*') 1877 mask++; 1878 if (!*mask || *mask == ';') 1879 return TRUE; 1880 } 1881 return FALSE; 1882 } 1883 1884 /************************************************************************* 1885 * PathMatchSingleMaskW [internal] 1886 */ 1887 static BOOL PathMatchSingleMaskW(LPCWSTR name, LPCWSTR mask) 1888 { 1889 while (*name && *mask && *mask != ';') 1890 { 1891 if (*mask == '*') 1892 { 1893 do 1894 { 1895 if (PathMatchSingleMaskW(name,mask+1)) 1896 return TRUE; /* try substrings */ 1897 } while (*name++); 1898 return FALSE; 1899 } 1900 1901 if (toupperW(*mask) != toupperW(*name) && *mask != '?') 1902 return FALSE; 1903 1904 name++; 1905 mask++; 1906 } 1907 if (!*name) 1908 { 1909 while (*mask == '*') 1910 mask++; 1911 if (!*mask || *mask == ';') 1912 return TRUE; 1913 } 1914 return FALSE; 1915 } 1916 1917 /************************************************************************* 1918 * PathMatchSpecA [SHLWAPI.@] 1919 * 1920 * Determine if a path matches one or more search masks. 1921 * 1922 * PARAMS 1923 * lpszPath [I] Path to check 1924 * lpszMask [I] Search mask(s) 1925 * 1926 * RETURNS 1927 * TRUE If lpszPath is valid and is matched 1928 * FALSE Otherwise 1929 * 1930 * NOTES 1931 * Multiple search masks may be given if they are separated by ";". The 1932 * pattern "*.*" is treated specially in that it matches all paths (for 1933 * backwards compatibility with DOS). 1934 */ 1935 BOOL WINAPI PathMatchSpecA(LPCSTR lpszPath, LPCSTR lpszMask) 1936 { 1937 TRACE("(%s,%s)\n", lpszPath, lpszMask); 1938 1939 if (!lstrcmpA(lpszMask, "*.*")) 1940 return TRUE; /* Matches every path */ 1941 1942 while (*lpszMask) 1943 { 1944 while (*lpszMask == ' ') 1945 lpszMask++; /* Eat leading spaces */ 1946 1947 if (PathMatchSingleMaskA(lpszPath, lpszMask)) 1948 return TRUE; /* Matches the current mask */ 1949 1950 while (*lpszMask && *lpszMask != ';') 1951 lpszMask = CharNextA(lpszMask); /* masks separated by ';' */ 1952 1953 if (*lpszMask == ';') 1954 lpszMask++; 1955 } 1956 return FALSE; 1957 } 1958 1959 /************************************************************************* 1960 * PathMatchSpecW [SHLWAPI.@] 1961 * 1962 * See PathMatchSpecA. 1963 */ 1964 BOOL WINAPI PathMatchSpecW(LPCWSTR lpszPath, LPCWSTR lpszMask) 1965 { 1966 static const WCHAR szStarDotStar[] = { '*', '.', '*', '\0' }; 1967 1968 TRACE("(%s,%s)\n", debugstr_w(lpszPath), debugstr_w(lpszMask)); 1969 1970 if (!lstrcmpW(lpszMask, szStarDotStar)) 1971 return TRUE; /* Matches every path */ 1972 1973 while (*lpszMask) 1974 { 1975 while (*lpszMask == ' ') 1976 lpszMask++; /* Eat leading spaces */ 1977 1978 if (PathMatchSingleMaskW(lpszPath, lpszMask)) 1979 return TRUE; /* Matches the current path */ 1980 1981 while (*lpszMask && *lpszMask != ';') 1982 lpszMask++; /* masks separated by ';' */ 1983 1984 if (*lpszMask == ';') 1985 lpszMask++; 1986 } 1987 return FALSE; 1988 } 1989 1990 /************************************************************************* 1991 * PathIsSameRootA [SHLWAPI.@] 1992 * 1993 * Determine if two paths share the same root. 1994 * 1995 * PARAMS 1996 * lpszPath1 [I] Source path 1997 * lpszPath2 [I] Path to compare with 1998 * 1999 * RETURNS 2000 * TRUE If both paths are valid and share the same root. 2001 * FALSE If either path is invalid or the paths do not share the same root. 2002 */ 2003 BOOL WINAPI PathIsSameRootA(LPCSTR lpszPath1, LPCSTR lpszPath2) 2004 { 2005 LPCSTR lpszStart; 2006 int dwLen; 2007 2008 TRACE("(%s,%s)\n", debugstr_a(lpszPath1), debugstr_a(lpszPath2)); 2009 2010 if (!lpszPath1 || !lpszPath2 || !(lpszStart = PathSkipRootA(lpszPath1))) 2011 return FALSE; 2012 2013 dwLen = PathCommonPrefixA(lpszPath1, lpszPath2, NULL) + 1; 2014 if (lpszStart - lpszPath1 > dwLen) 2015 return FALSE; /* Paths not common up to length of the root */ 2016 return TRUE; 2017 } 2018 2019 /************************************************************************* 2020 * PathIsSameRootW [SHLWAPI.@] 2021 * 2022 * See PathIsSameRootA. 2023 */ 2024 BOOL WINAPI PathIsSameRootW(LPCWSTR lpszPath1, LPCWSTR lpszPath2) 2025 { 2026 LPCWSTR lpszStart; 2027 int dwLen; 2028 2029 TRACE("(%s,%s)\n", debugstr_w(lpszPath1), debugstr_w(lpszPath2)); 2030 2031 if (!lpszPath1 || !lpszPath2 || !(lpszStart = PathSkipRootW(lpszPath1))) 2032 return FALSE; 2033 2034 dwLen = PathCommonPrefixW(lpszPath1, lpszPath2, NULL) + 1; 2035 if (lpszStart - lpszPath1 > dwLen) 2036 return FALSE; /* Paths not common up to length of the root */ 2037 return TRUE; 2038 } 2039 2040 /************************************************************************* 2041 * PathIsContentTypeA [SHLWAPI.@] 2042 * 2043 * Determine if a file is of a given registered content type. 2044 * 2045 * PARAMS 2046 * lpszPath [I] File to check 2047 * lpszContentType [I] Content type to check for 2048 * 2049 * RETURNS 2050 * TRUE If lpszPath is a given registered content type, 2051 * FALSE Otherwise. 2052 * 2053 * NOTES 2054 * This function looks up the registered content type for lpszPath. If 2055 * a content type is registered, it is compared (case insensitively) to 2056 * lpszContentType. Only if this matches does the function succeed. 2057 */ 2058 BOOL WINAPI PathIsContentTypeA(LPCSTR lpszPath, LPCSTR lpszContentType) 2059 { 2060 LPCSTR szExt; 2061 DWORD dwDummy; 2062 char szBuff[MAX_PATH]; 2063 2064 TRACE("(%s,%s)\n", debugstr_a(lpszPath), debugstr_a(lpszContentType)); 2065 2066 if (lpszPath && (szExt = PathFindExtensionA(lpszPath)) && *szExt && 2067 !SHGetValueA(HKEY_CLASSES_ROOT, szExt, "Content Type", 2068 REG_NONE, szBuff, &dwDummy) && 2069 !strcasecmp(lpszContentType, szBuff)) 2070 { 2071 return TRUE; 2072 } 2073 return FALSE; 2074 } 2075 2076 /************************************************************************* 2077 * PathIsContentTypeW [SHLWAPI.@] 2078 * 2079 * See PathIsContentTypeA. 2080 */ 2081 BOOL WINAPI PathIsContentTypeW(LPCWSTR lpszPath, LPCWSTR lpszContentType) 2082 { 2083 static const WCHAR szContentType[] = { 'C','o','n','t','e','n','t',' ','T','y','p','e','\0' }; 2084 LPCWSTR szExt; 2085 DWORD dwDummy; 2086 WCHAR szBuff[MAX_PATH]; 2087 2088 TRACE("(%s,%s)\n", debugstr_w(lpszPath), debugstr_w(lpszContentType)); 2089 2090 if (lpszPath && (szExt = PathFindExtensionW(lpszPath)) && *szExt && 2091 !SHGetValueW(HKEY_CLASSES_ROOT, szExt, szContentType, 2092 REG_NONE, szBuff, &dwDummy) && 2093 !strcmpiW(lpszContentType, szBuff)) 2094 { 2095 return TRUE; 2096 } 2097 return FALSE; 2098 } 2099 2100 /************************************************************************* 2101 * PathIsFileSpecA [SHLWAPI.@] 2102 * 2103 * Determine if a path is a file specification. 2104 * 2105 * PARAMS 2106 * lpszPath [I] Path to check 2107 * 2108 * RETURNS 2109 * TRUE If lpszPath is a file specification (i.e. Contains no directories). 2110 * FALSE Otherwise. 2111 */ 2112 BOOL WINAPI PathIsFileSpecA(LPCSTR lpszPath) 2113 { 2114 TRACE("(%s)\n", debugstr_a(lpszPath)); 2115 2116 if (!lpszPath) 2117 return FALSE; 2118 2119 while (*lpszPath) 2120 { 2121 if (*lpszPath == '\\' || *lpszPath == ':') 2122 return FALSE; 2123 lpszPath = CharNextA(lpszPath); 2124 } 2125 return TRUE; 2126 } 2127 2128 /************************************************************************* 2129 * PathIsFileSpecW [SHLWAPI.@] 2130 * 2131 * See PathIsFileSpecA. 2132 */ 2133 BOOL WINAPI PathIsFileSpecW(LPCWSTR lpszPath) 2134 { 2135 TRACE("(%s)\n", debugstr_w(lpszPath)); 2136 2137 if (!lpszPath) 2138 return FALSE; 2139 2140 while (*lpszPath) 2141 { 2142 if (*lpszPath == '\\' || *lpszPath == ':') 2143 return FALSE; 2144 lpszPath++; 2145 } 2146 return TRUE; 2147 } 2148 2149 /************************************************************************* 2150 * PathIsPrefixA [SHLWAPI.@] 2151 * 2152 * Determine if a path is a prefix of another. 2153 * 2154 * PARAMS 2155 * lpszPrefix [I] Prefix 2156 * lpszPath [I] Path to check 2157 * 2158 * RETURNS 2159 * TRUE If lpszPath has lpszPrefix as its prefix, 2160 * FALSE If either path is NULL or lpszPrefix is not a prefix 2161 */ 2162 BOOL WINAPI PathIsPrefixA (LPCSTR lpszPrefix, LPCSTR lpszPath) 2163 { 2164 TRACE("(%s,%s)\n", debugstr_a(lpszPrefix), debugstr_a(lpszPath)); 2165 2166 if (lpszPrefix && lpszPath && 2167 PathCommonPrefixA(lpszPath, lpszPrefix, NULL) == (int)strlen(lpszPrefix)) 2168 return TRUE; 2169 return FALSE; 2170 } 2171 2172 /************************************************************************* 2173 * PathIsPrefixW [SHLWAPI.@] 2174 * 2175 * See PathIsPrefixA. 2176 */ 2177 BOOL WINAPI PathIsPrefixW(LPCWSTR lpszPrefix, LPCWSTR lpszPath) 2178 { 2179 TRACE("(%s,%s)\n", debugstr_w(lpszPrefix), debugstr_w(lpszPath)); 2180 2181 if (lpszPrefix && lpszPath && 2182 PathCommonPrefixW(lpszPath, lpszPrefix, NULL) == (int)strlenW(lpszPrefix)) 2183 return TRUE; 2184 return FALSE; 2185 } 2186 2187 /************************************************************************* 2188 * PathIsSystemFolderA [SHLWAPI.@] 2189 * 2190 * Determine if a path or file attributes are a system folder. 2191 * 2192 * PARAMS 2193 * lpszPath [I] Path to check. 2194 * dwAttrib [I] Attributes to check, if lpszPath is NULL. 2195 * 2196 * RETURNS 2197 * TRUE If lpszPath or dwAttrib are a system folder. 2198 * FALSE If GetFileAttributesA() fails or neither parameter is a system folder. 2199 */ 2200 BOOL WINAPI PathIsSystemFolderA(LPCSTR lpszPath, DWORD dwAttrib) 2201 { 2202 TRACE("(%s,0x%08x)\n", debugstr_a(lpszPath), dwAttrib); 2203 2204 if (lpszPath && *lpszPath) 2205 dwAttrib = GetFileAttributesA(lpszPath); 2206 2207 if (dwAttrib == INVALID_FILE_ATTRIBUTES || !(dwAttrib & FILE_ATTRIBUTE_DIRECTORY) || 2208 !(dwAttrib & (FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_READONLY))) 2209 return FALSE; 2210 return TRUE; 2211 } 2212 2213 /************************************************************************* 2214 * PathIsSystemFolderW [SHLWAPI.@] 2215 * 2216 * See PathIsSystemFolderA. 2217 */ 2218 BOOL WINAPI PathIsSystemFolderW(LPCWSTR lpszPath, DWORD dwAttrib) 2219 { 2220 TRACE("(%s,0x%08x)\n", debugstr_w(lpszPath), dwAttrib); 2221 2222 if (lpszPath && *lpszPath) 2223 dwAttrib = GetFileAttributesW(lpszPath); 2224 2225 if (dwAttrib == INVALID_FILE_ATTRIBUTES || !(dwAttrib & FILE_ATTRIBUTE_DIRECTORY) || 2226 !(dwAttrib & (FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_READONLY))) 2227 return FALSE; 2228 return TRUE; 2229 } 2230 2231 /************************************************************************* 2232 * PathIsUNCA [SHLWAPI.@] 2233 * 2234 * Determine if a path is in UNC format. 2235 * 2236 * PARAMS 2237 * lpszPath [I] Path to check 2238 * 2239 * RETURNS 2240 * TRUE: The path is UNC. 2241 * FALSE: The path is not UNC or is NULL. 2242 */ 2243 BOOL WINAPI PathIsUNCA(LPCSTR lpszPath) 2244 { 2245 TRACE("(%s)\n",debugstr_a(lpszPath)); 2246 2247 /* 2248 * On Windows 2003, tests show that strings starting with "\\?" are 2249 * considered UNC, while on Windows Vista+ this is not the case anymore. 2250 */ 2251 // #ifdef __REACTOS__ 2252 #if (_WIN32_WINNT >= _WIN32_WINNT_VISTA) 2253 if (lpszPath && (lpszPath[0]=='\\') && (lpszPath[1]=='\\') && (lpszPath[2]!='?')) 2254 #else 2255 if (lpszPath && (lpszPath[0]=='\\') && (lpszPath[1]=='\\')) 2256 #endif 2257 return TRUE; 2258 return FALSE; 2259 } 2260 2261 /************************************************************************* 2262 * PathIsUNCW [SHLWAPI.@] 2263 * 2264 * See PathIsUNCA. 2265 */ 2266 BOOL WINAPI PathIsUNCW(LPCWSTR lpszPath) 2267 { 2268 TRACE("(%s)\n",debugstr_w(lpszPath)); 2269 2270 /* 2271 * On Windows 2003, tests show that strings starting with "\\?" are 2272 * considered UNC, while on Windows Vista+ this is not the case anymore. 2273 */ 2274 // #ifdef __REACTOS__ 2275 #if (_WIN32_WINNT >= _WIN32_WINNT_VISTA) 2276 if (lpszPath && (lpszPath[0]=='\\') && (lpszPath[1]=='\\') && (lpszPath[2]!='?')) 2277 #else 2278 if (lpszPath && (lpszPath[0]=='\\') && (lpszPath[1]=='\\')) 2279 #endif 2280 return TRUE; 2281 return FALSE; 2282 } 2283 2284 /************************************************************************* 2285 * PathIsUNCServerA [SHLWAPI.@] 2286 * 2287 * Determine if a path is a UNC server name ("\\SHARENAME"). 2288 * 2289 * PARAMS 2290 * lpszPath [I] Path to check. 2291 * 2292 * RETURNS 2293 * TRUE If lpszPath is a valid UNC server name. 2294 * FALSE Otherwise. 2295 * 2296 * NOTES 2297 * This routine is bug compatible with Win32: Server names with a 2298 * trailing backslash (e.g. "\\FOO\"), return FALSE incorrectly. 2299 * Fixing this bug may break other shlwapi functions! 2300 */ 2301 BOOL WINAPI PathIsUNCServerA(LPCSTR lpszPath) 2302 { 2303 TRACE("(%s)\n", debugstr_a(lpszPath)); 2304 2305 if (lpszPath && *lpszPath++ == '\\' && *lpszPath++ == '\\') 2306 { 2307 while (*lpszPath) 2308 { 2309 if (*lpszPath == '\\') 2310 return FALSE; 2311 lpszPath = CharNextA(lpszPath); 2312 } 2313 return TRUE; 2314 } 2315 return FALSE; 2316 } 2317 2318 /************************************************************************* 2319 * PathIsUNCServerW [SHLWAPI.@] 2320 * 2321 * See PathIsUNCServerA. 2322 */ 2323 BOOL WINAPI PathIsUNCServerW(LPCWSTR lpszPath) 2324 { 2325 TRACE("(%s)\n", debugstr_w(lpszPath)); 2326 2327 if (lpszPath && lpszPath[0] == '\\' && lpszPath[1] == '\\') 2328 { 2329 return !strchrW( lpszPath + 2, '\\' ); 2330 } 2331 return FALSE; 2332 } 2333 2334 /************************************************************************* 2335 * PathIsUNCServerShareA [SHLWAPI.@] 2336 * 2337 * Determine if a path is a UNC server share ("\\SHARENAME\SHARE"). 2338 * 2339 * PARAMS 2340 * lpszPath [I] Path to check. 2341 * 2342 * RETURNS 2343 * TRUE If lpszPath is a valid UNC server share. 2344 * FALSE Otherwise. 2345 * 2346 * NOTES 2347 * This routine is bug compatible with Win32: Server shares with a 2348 * trailing backslash (e.g. "\\FOO\BAR\"), return FALSE incorrectly. 2349 * Fixing this bug may break other shlwapi functions! 2350 */ 2351 BOOL WINAPI PathIsUNCServerShareA(LPCSTR lpszPath) 2352 { 2353 TRACE("(%s)\n", debugstr_a(lpszPath)); 2354 2355 if (lpszPath && *lpszPath++ == '\\' && *lpszPath++ == '\\') 2356 { 2357 BOOL bSeenSlash = FALSE; 2358 while (*lpszPath) 2359 { 2360 if (*lpszPath == '\\') 2361 { 2362 if (bSeenSlash) 2363 return FALSE; 2364 bSeenSlash = TRUE; 2365 } 2366 lpszPath = CharNextA(lpszPath); 2367 } 2368 return bSeenSlash; 2369 } 2370 return FALSE; 2371 } 2372 2373 /************************************************************************* 2374 * PathIsUNCServerShareW [SHLWAPI.@] 2375 * 2376 * See PathIsUNCServerShareA. 2377 */ 2378 BOOL WINAPI PathIsUNCServerShareW(LPCWSTR lpszPath) 2379 { 2380 TRACE("(%s)\n", debugstr_w(lpszPath)); 2381 2382 if (lpszPath && *lpszPath++ == '\\' && *lpszPath++ == '\\') 2383 { 2384 BOOL bSeenSlash = FALSE; 2385 while (*lpszPath) 2386 { 2387 if (*lpszPath == '\\') 2388 { 2389 if (bSeenSlash) 2390 return FALSE; 2391 bSeenSlash = TRUE; 2392 } 2393 lpszPath++; 2394 } 2395 return bSeenSlash; 2396 } 2397 return FALSE; 2398 } 2399 2400 /************************************************************************* 2401 * PathCanonicalizeA [SHLWAPI.@] 2402 * 2403 * Convert a path to its canonical form. 2404 * 2405 * PARAMS 2406 * lpszBuf [O] Output path 2407 * lpszPath [I] Path to canonicalize 2408 * 2409 * RETURNS 2410 * Success: TRUE. lpszBuf contains the output path, 2411 * Failure: FALSE, If input path is invalid. lpszBuf is undefined 2412 */ 2413 BOOL WINAPI PathCanonicalizeA(LPSTR lpszBuf, LPCSTR lpszPath) 2414 { 2415 BOOL bRet = FALSE; 2416 2417 TRACE("(%p,%s)\n", lpszBuf, debugstr_a(lpszPath)); 2418 2419 if (lpszBuf) 2420 *lpszBuf = '\0'; 2421 2422 if (!lpszBuf || !lpszPath) 2423 SetLastError(ERROR_INVALID_PARAMETER); 2424 else 2425 { 2426 WCHAR szPath[MAX_PATH]; 2427 WCHAR szBuff[MAX_PATH]; 2428 int ret = MultiByteToWideChar(CP_ACP,0,lpszPath,-1,szPath,MAX_PATH); 2429 2430 if (!ret) { 2431 WARN("Failed to convert string to widechar (too long?), LE %d.\n", GetLastError()); 2432 return FALSE; 2433 } 2434 bRet = PathCanonicalizeW(szBuff, szPath); 2435 WideCharToMultiByte(CP_ACP,0,szBuff,-1,lpszBuf,MAX_PATH,0,0); 2436 } 2437 return bRet; 2438 } 2439 2440 2441 /************************************************************************* 2442 * PathCanonicalizeW [SHLWAPI.@] 2443 * 2444 * See PathCanonicalizeA. 2445 */ 2446 BOOL WINAPI PathCanonicalizeW(LPWSTR lpszBuf, LPCWSTR lpszPath) 2447 { 2448 LPWSTR lpszDst = lpszBuf; 2449 LPCWSTR lpszSrc = lpszPath; 2450 2451 TRACE("(%p,%s)\n", lpszBuf, debugstr_w(lpszPath)); 2452 2453 if (lpszBuf) 2454 *lpszDst = '\0'; 2455 2456 if (!lpszBuf || !lpszPath) 2457 { 2458 SetLastError(ERROR_INVALID_PARAMETER); 2459 return FALSE; 2460 } 2461 2462 if (!*lpszPath) 2463 { 2464 *lpszBuf++ = '\\'; 2465 *lpszBuf = '\0'; 2466 return TRUE; 2467 } 2468 2469 /* Copy path root */ 2470 if (*lpszSrc == '\\') 2471 { 2472 *lpszDst++ = *lpszSrc++; 2473 } 2474 else if (*lpszSrc && lpszSrc[1] == ':') 2475 { 2476 /* X:\ */ 2477 *lpszDst++ = *lpszSrc++; 2478 *lpszDst++ = *lpszSrc++; 2479 if (*lpszSrc == '\\') 2480 *lpszDst++ = *lpszSrc++; 2481 } 2482 2483 /* Canonicalize the rest of the path */ 2484 while (*lpszSrc) 2485 { 2486 if (*lpszSrc == '.') 2487 { 2488 if (lpszSrc[1] == '\\' && (lpszSrc == lpszPath || lpszSrc[-1] == '\\' || lpszSrc[-1] == ':')) 2489 { 2490 lpszSrc += 2; /* Skip .\ */ 2491 } 2492 else if (lpszSrc[1] == '.' && (lpszDst == lpszBuf || lpszDst[-1] == '\\')) 2493 { 2494 /* \.. backs up a directory, over the root if it has no \ following X:. 2495 * .. is ignored if it would remove a UNC server name or initial \\ 2496 */ 2497 if (lpszDst != lpszBuf) 2498 { 2499 *lpszDst = '\0'; /* Allow PathIsUNCServerShareA test on lpszBuf */ 2500 if (lpszDst > lpszBuf+1 && lpszDst[-1] == '\\' && 2501 (lpszDst[-2] != '\\' || lpszDst > lpszBuf+2)) 2502 { 2503 if (lpszDst[-2] == ':' && (lpszDst > lpszBuf+3 || lpszDst[-3] == ':')) 2504 { 2505 lpszDst -= 2; 2506 while (lpszDst > lpszBuf && *lpszDst != '\\') 2507 lpszDst--; 2508 if (*lpszDst == '\\') 2509 lpszDst++; /* Reset to last '\' */ 2510 else 2511 lpszDst = lpszBuf; /* Start path again from new root */ 2512 } 2513 else if (lpszDst[-2] != ':' && !PathIsUNCServerShareW(lpszBuf)) 2514 lpszDst -= 2; 2515 } 2516 while (lpszDst > lpszBuf && *lpszDst != '\\') 2517 lpszDst--; 2518 if (lpszDst == lpszBuf) 2519 { 2520 *lpszDst++ = '\\'; 2521 lpszSrc++; 2522 } 2523 } 2524 lpszSrc += 2; /* Skip .. in src path */ 2525 } 2526 else 2527 *lpszDst++ = *lpszSrc++; 2528 } 2529 else 2530 *lpszDst++ = *lpszSrc++; 2531 } 2532 /* Append \ to naked drive specs */ 2533 if (lpszDst - lpszBuf == 2 && lpszDst[-1] == ':') 2534 *lpszDst++ = '\\'; 2535 *lpszDst++ = '\0'; 2536 return TRUE; 2537 } 2538 2539 /************************************************************************* 2540 * PathFindNextComponentA [SHLWAPI.@] 2541 * 2542 * Find the next component in a path. 2543 * 2544 * PARAMS 2545 * lpszPath [I] Path to find next component in 2546 * 2547 * RETURNS 2548 * Success: A pointer to the next component, or the end of the string. 2549 * Failure: NULL, If lpszPath is invalid 2550 * 2551 * NOTES 2552 * A 'component' is either a backslash character (\) or UNC marker (\\). 2553 * Because of this, relative paths (e.g "c:foo") are regarded as having 2554 * only one component. 2555 */ 2556 LPSTR WINAPI PathFindNextComponentA(LPCSTR lpszPath) 2557 { 2558 LPSTR lpszSlash; 2559 2560 TRACE("(%s)\n", debugstr_a(lpszPath)); 2561 2562 if(!lpszPath || !*lpszPath) 2563 return NULL; 2564 2565 if ((lpszSlash = StrChrA(lpszPath, '\\'))) 2566 { 2567 if (lpszSlash[1] == '\\') 2568 lpszSlash++; 2569 return lpszSlash + 1; 2570 } 2571 return (LPSTR)lpszPath + strlen(lpszPath); 2572 } 2573 2574 /************************************************************************* 2575 * PathFindNextComponentW [SHLWAPI.@] 2576 * 2577 * See PathFindNextComponentA. 2578 */ 2579 LPWSTR WINAPI PathFindNextComponentW(LPCWSTR lpszPath) 2580 { 2581 LPWSTR lpszSlash; 2582 2583 TRACE("(%s)\n", debugstr_w(lpszPath)); 2584 2585 if(!lpszPath || !*lpszPath) 2586 return NULL; 2587 2588 if ((lpszSlash = StrChrW(lpszPath, '\\'))) 2589 { 2590 if (lpszSlash[1] == '\\') 2591 lpszSlash++; 2592 return lpszSlash + 1; 2593 } 2594 return (LPWSTR)lpszPath + strlenW(lpszPath); 2595 } 2596 2597 /************************************************************************* 2598 * PathAddExtensionA [SHLWAPI.@] 2599 * 2600 * Add a file extension to a path 2601 * 2602 * PARAMS 2603 * lpszPath [I/O] Path to add extension to 2604 * lpszExtension [I] Extension to add to lpszPath 2605 * 2606 * RETURNS 2607 * TRUE If the path was modified, 2608 * FALSE If lpszPath or lpszExtension are invalid, lpszPath has an 2609 * extension already, or the new path length is too big. 2610 * 2611 * FIXME 2612 * What version of shlwapi.dll adds "exe" if lpszExtension is NULL? Win2k 2613 * does not do this, so the behaviour was removed. 2614 */ 2615 BOOL WINAPI PathAddExtensionA(LPSTR lpszPath, LPCSTR lpszExtension) 2616 { 2617 size_t dwLen; 2618 2619 TRACE("(%s,%s)\n", debugstr_a(lpszPath), debugstr_a(lpszExtension)); 2620 2621 if (!lpszPath || !lpszExtension || *(PathFindExtensionA(lpszPath))) 2622 return FALSE; 2623 2624 dwLen = strlen(lpszPath); 2625 2626 if (dwLen + strlen(lpszExtension) >= MAX_PATH) 2627 return FALSE; 2628 2629 strcpy(lpszPath + dwLen, lpszExtension); 2630 return TRUE; 2631 } 2632 2633 /************************************************************************* 2634 * PathAddExtensionW [SHLWAPI.@] 2635 * 2636 * See PathAddExtensionA. 2637 */ 2638 BOOL WINAPI PathAddExtensionW(LPWSTR lpszPath, LPCWSTR lpszExtension) 2639 { 2640 size_t dwLen; 2641 2642 TRACE("(%s,%s)\n", debugstr_w(lpszPath), debugstr_w(lpszExtension)); 2643 2644 if (!lpszPath || !lpszExtension || *(PathFindExtensionW(lpszPath))) 2645 return FALSE; 2646 2647 dwLen = strlenW(lpszPath); 2648 2649 if (dwLen + strlenW(lpszExtension) >= MAX_PATH) 2650 return FALSE; 2651 2652 strcpyW(lpszPath + dwLen, lpszExtension); 2653 return TRUE; 2654 } 2655 2656 /************************************************************************* 2657 * PathMakePrettyA [SHLWAPI.@] 2658 * 2659 * Convert an uppercase DOS filename into lowercase. 2660 * 2661 * PARAMS 2662 * lpszPath [I/O] Path to convert. 2663 * 2664 * RETURNS 2665 * TRUE If the path was an uppercase DOS path and was converted, 2666 * FALSE Otherwise. 2667 */ 2668 BOOL WINAPI PathMakePrettyA(LPSTR lpszPath) 2669 { 2670 LPSTR pszIter = lpszPath; 2671 2672 TRACE("(%s)\n", debugstr_a(lpszPath)); 2673 2674 if (!pszIter) 2675 return FALSE; 2676 2677 if (*pszIter) 2678 { 2679 do 2680 { 2681 if (islower(*pszIter) || IsDBCSLeadByte(*pszIter)) 2682 return FALSE; /* Not DOS path */ 2683 pszIter++; 2684 } while (*pszIter); 2685 pszIter = lpszPath + 1; 2686 while (*pszIter) 2687 { 2688 *pszIter = tolower(*pszIter); 2689 pszIter++; 2690 } 2691 } 2692 return TRUE; 2693 } 2694 2695 /************************************************************************* 2696 * PathMakePrettyW [SHLWAPI.@] 2697 * 2698 * See PathMakePrettyA. 2699 */ 2700 BOOL WINAPI PathMakePrettyW(LPWSTR lpszPath) 2701 { 2702 LPWSTR pszIter = lpszPath; 2703 2704 TRACE("(%s)\n", debugstr_w(lpszPath)); 2705 2706 if (!pszIter) 2707 return FALSE; 2708 2709 if (*pszIter) 2710 { 2711 do 2712 { 2713 if (islowerW(*pszIter)) 2714 return FALSE; /* Not DOS path */ 2715 pszIter++; 2716 } while (*pszIter); 2717 pszIter = lpszPath + 1; 2718 while (*pszIter) 2719 { 2720 *pszIter = tolowerW(*pszIter); 2721 pszIter++; 2722 } 2723 } 2724 return TRUE; 2725 } 2726 2727 /************************************************************************* 2728 * PathCommonPrefixA [SHLWAPI.@] 2729 * 2730 * Determine the length of the common prefix between two paths. 2731 * 2732 * PARAMS 2733 * lpszFile1 [I] First path for comparison 2734 * lpszFile2 [I] Second path for comparison 2735 * achPath [O] Destination for common prefix string 2736 * 2737 * RETURNS 2738 * The length of the common prefix. This is 0 if there is no common 2739 * prefix between the paths or if any parameters are invalid. If the prefix 2740 * is non-zero and achPath is not NULL, achPath is filled with the common 2741 * part of the prefix and NUL terminated. 2742 * 2743 * NOTES 2744 * A common prefix of 2 is always returned as 3. It is thus possible for 2745 * the length returned to be invalid (i.e. Longer than one or both of the 2746 * strings given as parameters). This Win32 behaviour has been implemented 2747 * here, and cannot be changed (fixed?) without breaking other SHLWAPI calls. 2748 * To work around this when using this function, always check that the byte 2749 * at [common_prefix_len-1] is not a NUL. If it is, deduct 1 from the prefix. 2750 */ 2751 int WINAPI PathCommonPrefixA(LPCSTR lpszFile1, LPCSTR lpszFile2, LPSTR achPath) 2752 { 2753 size_t iLen = 0; 2754 LPCSTR lpszIter1 = lpszFile1; 2755 LPCSTR lpszIter2 = lpszFile2; 2756 2757 TRACE("(%s,%s,%p)\n", debugstr_a(lpszFile1), debugstr_a(lpszFile2), achPath); 2758 2759 if (achPath) 2760 *achPath = '\0'; 2761 2762 if (!lpszFile1 || !lpszFile2) 2763 return 0; 2764 2765 /* Handle roots first */ 2766 if (PathIsUNCA(lpszFile1)) 2767 { 2768 if (!PathIsUNCA(lpszFile2)) 2769 return 0; 2770 lpszIter1 += 2; 2771 lpszIter2 += 2; 2772 } 2773 else if (PathIsUNCA(lpszFile2)) 2774 return 0; /* Know already lpszFile1 is not UNC */ 2775 2776 do 2777 { 2778 /* Update len */ 2779 if ((!*lpszIter1 || *lpszIter1 == '\\') && 2780 (!*lpszIter2 || *lpszIter2 == '\\')) 2781 iLen = lpszIter1 - lpszFile1; /* Common to this point */ 2782 2783 if (!*lpszIter1 || (tolower(*lpszIter1) != tolower(*lpszIter2))) 2784 break; /* Strings differ at this point */ 2785 2786 lpszIter1++; 2787 lpszIter2++; 2788 } while (1); 2789 2790 if (iLen == 2) 2791 iLen++; /* Feature/Bug compatible with Win32 */ 2792 2793 if (iLen && achPath) 2794 { 2795 memcpy(achPath,lpszFile1,iLen); 2796 achPath[iLen] = '\0'; 2797 } 2798 return iLen; 2799 } 2800 2801 /************************************************************************* 2802 * PathCommonPrefixW [SHLWAPI.@] 2803 * 2804 * See PathCommonPrefixA. 2805 */ 2806 int WINAPI PathCommonPrefixW(LPCWSTR lpszFile1, LPCWSTR lpszFile2, LPWSTR achPath) 2807 { 2808 size_t iLen = 0; 2809 LPCWSTR lpszIter1 = lpszFile1; 2810 LPCWSTR lpszIter2 = lpszFile2; 2811 2812 TRACE("(%s,%s,%p)\n", debugstr_w(lpszFile1), debugstr_w(lpszFile2), achPath); 2813 2814 if (achPath) 2815 *achPath = '\0'; 2816 2817 if (!lpszFile1 || !lpszFile2) 2818 return 0; 2819 2820 /* Handle roots first */ 2821 if (PathIsUNCW(lpszFile1)) 2822 { 2823 if (!PathIsUNCW(lpszFile2)) 2824 return 0; 2825 lpszIter1 += 2; 2826 lpszIter2 += 2; 2827 } 2828 else if (PathIsUNCW(lpszFile2)) 2829 return 0; /* Know already lpszFile1 is not UNC */ 2830 2831 do 2832 { 2833 /* Update len */ 2834 if ((!*lpszIter1 || *lpszIter1 == '\\') && 2835 (!*lpszIter2 || *lpszIter2 == '\\')) 2836 iLen = lpszIter1 - lpszFile1; /* Common to this point */ 2837 2838 if (!*lpszIter1 || (tolowerW(*lpszIter1) != tolowerW(*lpszIter2))) 2839 break; /* Strings differ at this point */ 2840 2841 lpszIter1++; 2842 lpszIter2++; 2843 } while (1); 2844 2845 if (iLen == 2) 2846 iLen++; /* Feature/Bug compatible with Win32 */ 2847 2848 if (iLen && achPath) 2849 { 2850 memcpy(achPath,lpszFile1,iLen * sizeof(WCHAR)); 2851 achPath[iLen] = '\0'; 2852 } 2853 return iLen; 2854 } 2855 2856 /************************************************************************* 2857 * PathCompactPathA [SHLWAPI.@] 2858 * 2859 * Make a path fit into a given width when printed to a DC. 2860 * 2861 * PARAMS 2862 * hDc [I] Destination DC 2863 * lpszPath [I/O] Path to be printed to hDc 2864 * dx [I] Desired width 2865 * 2866 * RETURNS 2867 * TRUE If the path was modified/went well. 2868 * FALSE Otherwise. 2869 */ 2870 BOOL WINAPI PathCompactPathA(HDC hDC, LPSTR lpszPath, UINT dx) 2871 { 2872 BOOL bRet = FALSE; 2873 2874 TRACE("(%p,%s,%d)\n", hDC, debugstr_a(lpszPath), dx); 2875 2876 if (lpszPath) 2877 { 2878 WCHAR szPath[MAX_PATH]; 2879 MultiByteToWideChar(CP_ACP,0,lpszPath,-1,szPath,MAX_PATH); 2880 bRet = PathCompactPathW(hDC, szPath, dx); 2881 WideCharToMultiByte(CP_ACP,0,szPath,-1,lpszPath,MAX_PATH,0,0); 2882 } 2883 return bRet; 2884 } 2885 2886 /************************************************************************* 2887 * PathCompactPathW [SHLWAPI.@] 2888 * 2889 * See PathCompactPathA. 2890 */ 2891 BOOL WINAPI PathCompactPathW(HDC hDC, LPWSTR lpszPath, UINT dx) 2892 { 2893 static const WCHAR szEllipses[] = { '.', '.', '.', '\0' }; 2894 BOOL bRet = TRUE; 2895 HDC hdc = 0; 2896 WCHAR buff[MAX_PATH]; 2897 SIZE size; 2898 DWORD dwLen; 2899 2900 TRACE("(%p,%s,%d)\n", hDC, debugstr_w(lpszPath), dx); 2901 2902 if (!lpszPath) 2903 return FALSE; 2904 2905 if (!hDC) 2906 hdc = hDC = GetDC(0); 2907 2908 /* Get the length of the whole path */ 2909 dwLen = strlenW(lpszPath); 2910 GetTextExtentPointW(hDC, lpszPath, dwLen, &size); 2911 2912 if ((UINT)size.cx > dx) 2913 { 2914 /* Path too big, must reduce it */ 2915 LPWSTR sFile; 2916 DWORD dwEllipsesLen = 0, dwPathLen = 0; 2917 2918 sFile = PathFindFileNameW(lpszPath); 2919 if (sFile != lpszPath) sFile--; 2920 2921 /* Get the size of ellipses */ 2922 GetTextExtentPointW(hDC, szEllipses, 3, &size); 2923 dwEllipsesLen = size.cx; 2924 /* Get the size of the file name */ 2925 GetTextExtentPointW(hDC, sFile, strlenW(sFile), &size); 2926 dwPathLen = size.cx; 2927 2928 if (sFile != lpszPath) 2929 { 2930 LPWSTR sPath = sFile; 2931 BOOL bEllipses = FALSE; 2932 2933 /* The path includes a file name. Include as much of the path prior to 2934 * the file name as possible, allowing for the ellipses, e.g: 2935 * c:\some very long path\filename ==> c:\some v...\filename 2936 */ 2937 lstrcpynW(buff, sFile, MAX_PATH); 2938 2939 do 2940 { 2941 DWORD dwTotalLen = bEllipses? dwPathLen + dwEllipsesLen : dwPathLen; 2942 2943 GetTextExtentPointW(hDC, lpszPath, sPath - lpszPath, &size); 2944 dwTotalLen += size.cx; 2945 if (dwTotalLen <= dx) 2946 break; 2947 sPath--; 2948 if (!bEllipses) 2949 { 2950 bEllipses = TRUE; 2951 sPath -= 2; 2952 } 2953 } while (sPath > lpszPath); 2954 2955 if (sPath > lpszPath) 2956 { 2957 if (bEllipses) 2958 { 2959 strcpyW(sPath, szEllipses); 2960 strcpyW(sPath+3, buff); 2961 } 2962 bRet = TRUE; 2963 goto end; 2964 } 2965 strcpyW(lpszPath, szEllipses); 2966 strcpyW(lpszPath+3, buff); 2967 bRet = FALSE; 2968 goto end; 2969 } 2970 2971 /* Trim the path by adding ellipses to the end, e.g: 2972 * A very long file name.txt ==> A very... 2973 */ 2974 dwLen = strlenW(lpszPath); 2975 2976 if (dwLen > MAX_PATH - 3) 2977 dwLen = MAX_PATH - 3; 2978 lstrcpynW(buff, sFile, dwLen); 2979 2980 do { 2981 dwLen--; 2982 GetTextExtentPointW(hDC, buff, dwLen, &size); 2983 } while (dwLen && size.cx + dwEllipsesLen > dx); 2984 2985 if (!dwLen) 2986 { 2987 DWORD dwWritten = 0; 2988 2989 dwEllipsesLen /= 3; /* Size of a single '.' */ 2990 2991 /* Write as much of the Ellipses string as possible */ 2992 while (dwWritten + dwEllipsesLen < dx && dwLen < 3) 2993 { 2994 *lpszPath++ = '.'; 2995 dwWritten += dwEllipsesLen; 2996 dwLen++; 2997 } 2998 *lpszPath = '\0'; 2999 bRet = FALSE; 3000 } 3001 else 3002 { 3003 strcpyW(buff + dwLen, szEllipses); 3004 strcpyW(lpszPath, buff); 3005 } 3006 } 3007 3008 end: 3009 if (hdc) 3010 ReleaseDC(0, hdc); 3011 3012 return bRet; 3013 } 3014 3015 /************************************************************************* 3016 * PathGetCharTypeA [SHLWAPI.@] 3017 * 3018 * Categorise a character from a file path. 3019 * 3020 * PARAMS 3021 * ch [I] Character to get the type of 3022 * 3023 * RETURNS 3024 * A set of GCT_ bit flags (from "shlwapi.h") indicating the character type. 3025 */ 3026 UINT WINAPI PathGetCharTypeA(UCHAR ch) 3027 { 3028 return PathGetCharTypeW(ch); 3029 } 3030 3031 /************************************************************************* 3032 * PathGetCharTypeW [SHLWAPI.@] 3033 * 3034 * See PathGetCharTypeA. 3035 */ 3036 UINT WINAPI PathGetCharTypeW(WCHAR ch) 3037 { 3038 UINT flags = 0; 3039 3040 TRACE("(%d)\n", ch); 3041 3042 if (!ch || ch < ' ' || ch == '<' || ch == '>' || 3043 ch == '"' || ch == '|' || ch == '/') 3044 flags = GCT_INVALID; /* Invalid */ 3045 else if (ch == '*' || ch=='?') 3046 flags = GCT_WILD; /* Wildchars */ 3047 else if ((ch == '\\') || (ch == ':')) 3048 return GCT_SEPARATOR; /* Path separators */ 3049 else 3050 { 3051 if (ch < 126) 3052 { 3053 if (((ch & 0x1) && ch != ';') || !ch || isalnum(ch) || ch == '$' || ch == '&' || ch == '(' || 3054 ch == '.' || ch == '@' || ch == '^' || 3055 ch == '\'' || ch == 130 || ch == '`') 3056 flags |= GCT_SHORTCHAR; /* All these are valid for DOS */ 3057 } 3058 else 3059 flags |= GCT_SHORTCHAR; /* Bug compatible with win32 */ 3060 flags |= GCT_LFNCHAR; /* Valid for long file names */ 3061 } 3062 return flags; 3063 } 3064 3065 /************************************************************************* 3066 * SHLWAPI_UseSystemForSystemFolders 3067 * 3068 * Internal helper for PathMakeSystemFolderW. 3069 */ 3070 static BOOL SHLWAPI_UseSystemForSystemFolders(void) 3071 { 3072 static BOOL bCheckedReg = FALSE; 3073 static BOOL bUseSystemForSystemFolders = FALSE; 3074 3075 if (!bCheckedReg) 3076 { 3077 bCheckedReg = TRUE; 3078 3079 /* Key tells Win what file attributes to use on system folders */ 3080 if (SHGetValueA(HKEY_LOCAL_MACHINE, 3081 "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer", 3082 "UseSystemForSystemFolders", 0, 0, 0)) 3083 bUseSystemForSystemFolders = TRUE; 3084 } 3085 return bUseSystemForSystemFolders; 3086 } 3087 3088 /************************************************************************* 3089 * PathMakeSystemFolderA [SHLWAPI.@] 3090 * 3091 * Set system folder attribute for a path. 3092 * 3093 * PARAMS 3094 * lpszPath [I] The path to turn into a system folder 3095 * 3096 * RETURNS 3097 * TRUE If the path was changed to/already was a system folder 3098 * FALSE If the path is invalid or SetFileAttributesA() fails 3099 */ 3100 BOOL WINAPI PathMakeSystemFolderA(LPCSTR lpszPath) 3101 { 3102 BOOL bRet = FALSE; 3103 3104 TRACE("(%s)\n", debugstr_a(lpszPath)); 3105 3106 if (lpszPath && *lpszPath) 3107 { 3108 WCHAR szPath[MAX_PATH]; 3109 MultiByteToWideChar(CP_ACP,0,lpszPath,-1,szPath,MAX_PATH); 3110 bRet = PathMakeSystemFolderW(szPath); 3111 } 3112 return bRet; 3113 } 3114 3115 /************************************************************************* 3116 * PathMakeSystemFolderW [SHLWAPI.@] 3117 * 3118 * See PathMakeSystemFolderA. 3119 */ 3120 BOOL WINAPI PathMakeSystemFolderW(LPCWSTR lpszPath) 3121 { 3122 DWORD dwDefaultAttr = FILE_ATTRIBUTE_READONLY, dwAttr; 3123 WCHAR buff[MAX_PATH]; 3124 3125 TRACE("(%s)\n", debugstr_w(lpszPath)); 3126 3127 if (!lpszPath || !*lpszPath) 3128 return FALSE; 3129 3130 /* If the directory is already a system directory, don't do anything */ 3131 GetSystemDirectoryW(buff, MAX_PATH); 3132 if (!strcmpW(buff, lpszPath)) 3133 return TRUE; 3134 3135 GetWindowsDirectoryW(buff, MAX_PATH); 3136 if (!strcmpW(buff, lpszPath)) 3137 return TRUE; 3138 3139 /* "UseSystemForSystemFolders" Tells Win what attributes to use */ 3140 if (SHLWAPI_UseSystemForSystemFolders()) 3141 dwDefaultAttr = FILE_ATTRIBUTE_SYSTEM; 3142 3143 if ((dwAttr = GetFileAttributesW(lpszPath)) == INVALID_FILE_ATTRIBUTES) 3144 return FALSE; 3145 3146 /* Change file attributes to system attributes */ 3147 dwAttr &= ~(FILE_ATTRIBUTE_SYSTEM|FILE_ATTRIBUTE_HIDDEN|FILE_ATTRIBUTE_READONLY); 3148 return SetFileAttributesW(lpszPath, dwAttr | dwDefaultAttr); 3149 } 3150 3151 /************************************************************************* 3152 * PathRenameExtensionA [SHLWAPI.@] 3153 * 3154 * Swap the file extension in a path with another extension. 3155 * 3156 * PARAMS 3157 * lpszPath [I/O] Path to swap the extension in 3158 * lpszExt [I] The new extension 3159 * 3160 * RETURNS 3161 * TRUE if lpszPath was modified, 3162 * FALSE if lpszPath or lpszExt is NULL, or the new path is too long 3163 */ 3164 BOOL WINAPI PathRenameExtensionA(LPSTR lpszPath, LPCSTR lpszExt) 3165 { 3166 LPSTR lpszExtension; 3167 3168 TRACE("(%s,%s)\n", debugstr_a(lpszPath), debugstr_a(lpszExt)); 3169 3170 lpszExtension = PathFindExtensionA(lpszPath); 3171 3172 if (!lpszExtension || (lpszExtension - lpszPath + strlen(lpszExt) >= MAX_PATH)) 3173 return FALSE; 3174 3175 strcpy(lpszExtension, lpszExt); 3176 return TRUE; 3177 } 3178 3179 /************************************************************************* 3180 * PathRenameExtensionW [SHLWAPI.@] 3181 * 3182 * See PathRenameExtensionA. 3183 */ 3184 BOOL WINAPI PathRenameExtensionW(LPWSTR lpszPath, LPCWSTR lpszExt) 3185 { 3186 LPWSTR lpszExtension; 3187 3188 TRACE("(%s,%s)\n", debugstr_w(lpszPath), debugstr_w(lpszExt)); 3189 3190 lpszExtension = PathFindExtensionW(lpszPath); 3191 3192 if (!lpszExtension || (lpszExtension - lpszPath + strlenW(lpszExt) >= MAX_PATH)) 3193 return FALSE; 3194 3195 strcpyW(lpszExtension, lpszExt); 3196 return TRUE; 3197 } 3198 3199 /************************************************************************* 3200 * PathSearchAndQualifyA [SHLWAPI.@] 3201 * 3202 * Determine if a given path is correct and fully qualified. 3203 * 3204 * PARAMS 3205 * lpszPath [I] Path to check 3206 * lpszBuf [O] Output for correct path 3207 * cchBuf [I] Size of lpszBuf 3208 * 3209 * RETURNS 3210 * Unknown. 3211 */ 3212 BOOL WINAPI PathSearchAndQualifyA(LPCSTR lpszPath, LPSTR lpszBuf, UINT cchBuf) 3213 { 3214 TRACE("(%s,%p,0x%08x)\n", debugstr_a(lpszPath), lpszBuf, cchBuf); 3215 3216 if(SearchPathA(NULL, lpszPath, NULL, cchBuf, lpszBuf, NULL)) 3217 return TRUE; 3218 return !!GetFullPathNameA(lpszPath, cchBuf, lpszBuf, NULL); 3219 } 3220 3221 /************************************************************************* 3222 * PathSearchAndQualifyW [SHLWAPI.@] 3223 * 3224 * See PathSearchAndQualifyA. 3225 */ 3226 BOOL WINAPI PathSearchAndQualifyW(LPCWSTR lpszPath, LPWSTR lpszBuf, UINT cchBuf) 3227 { 3228 TRACE("(%s,%p,0x%08x)\n", debugstr_w(lpszPath), lpszBuf, cchBuf); 3229 3230 if(SearchPathW(NULL, lpszPath, NULL, cchBuf, lpszBuf, NULL)) 3231 return TRUE; 3232 return !!GetFullPathNameW(lpszPath, cchBuf, lpszBuf, NULL); 3233 } 3234 3235 /************************************************************************* 3236 * PathSkipRootA [SHLWAPI.@] 3237 * 3238 * Return the portion of a path following the drive letter or mount point. 3239 * 3240 * PARAMS 3241 * lpszPath [I] The path to skip on 3242 * 3243 * RETURNS 3244 * Success: A pointer to the next character after the root. 3245 * Failure: NULL, if lpszPath is invalid, has no root or is a multibyte string. 3246 */ 3247 LPSTR WINAPI PathSkipRootA(LPCSTR lpszPath) 3248 { 3249 TRACE("(%s)\n", debugstr_a(lpszPath)); 3250 3251 if (!lpszPath || !*lpszPath) 3252 return NULL; 3253 3254 if (*lpszPath == '\\' && lpszPath[1] == '\\') 3255 { 3256 /* Network share: skip share server and mount point */ 3257 lpszPath += 2; 3258 if ((lpszPath = StrChrA(lpszPath, '\\')) && 3259 (lpszPath = StrChrA(lpszPath + 1, '\\'))) 3260 lpszPath++; 3261 return (LPSTR)lpszPath; 3262 } 3263 3264 if (IsDBCSLeadByte(*lpszPath)) 3265 return NULL; 3266 3267 /* Check x:\ */ 3268 if (lpszPath[0] && lpszPath[1] == ':' && lpszPath[2] == '\\') 3269 return (LPSTR)lpszPath + 3; 3270 return NULL; 3271 } 3272 3273 /************************************************************************* 3274 * PathSkipRootW [SHLWAPI.@] 3275 * 3276 * See PathSkipRootA. 3277 */ 3278 LPWSTR WINAPI PathSkipRootW(LPCWSTR lpszPath) 3279 { 3280 TRACE("(%s)\n", debugstr_w(lpszPath)); 3281 3282 if (!lpszPath || !*lpszPath) 3283 return NULL; 3284 3285 if (*lpszPath == '\\' && lpszPath[1] == '\\') 3286 { 3287 /* Network share: skip share server and mount point */ 3288 lpszPath += 2; 3289 if ((lpszPath = StrChrW(lpszPath, '\\')) && 3290 (lpszPath = StrChrW(lpszPath + 1, '\\'))) 3291 lpszPath++; 3292 return (LPWSTR)lpszPath; 3293 } 3294 3295 /* Check x:\ */ 3296 if (lpszPath[0] && lpszPath[1] == ':' && lpszPath[2] == '\\') 3297 return (LPWSTR)lpszPath + 3; 3298 return NULL; 3299 } 3300 3301 /************************************************************************* 3302 * PathCreateFromUrlA [SHLWAPI.@] 3303 * 3304 * See PathCreateFromUrlW 3305 */ 3306 HRESULT WINAPI PathCreateFromUrlA(LPCSTR pszUrl, LPSTR pszPath, 3307 LPDWORD pcchPath, DWORD dwReserved) 3308 { 3309 WCHAR bufW[MAX_PATH]; 3310 WCHAR *pathW = bufW; 3311 UNICODE_STRING urlW; 3312 HRESULT ret; 3313 DWORD lenW = sizeof(bufW)/sizeof(WCHAR), lenA; 3314 3315 if (!pszUrl || !pszPath || !pcchPath || !*pcchPath) 3316 return E_INVALIDARG; 3317 3318 if(!RtlCreateUnicodeStringFromAsciiz(&urlW, pszUrl)) 3319 return E_INVALIDARG; 3320 if((ret = PathCreateFromUrlW(urlW.Buffer, pathW, &lenW, dwReserved)) == E_POINTER) { 3321 pathW = HeapAlloc(GetProcessHeap(), 0, lenW * sizeof(WCHAR)); 3322 ret = PathCreateFromUrlW(urlW.Buffer, pathW, &lenW, dwReserved); 3323 } 3324 if(ret == S_OK) { 3325 RtlUnicodeToMultiByteSize(&lenA, pathW, lenW * sizeof(WCHAR)); 3326 if(*pcchPath > lenA) { 3327 RtlUnicodeToMultiByteN(pszPath, *pcchPath - 1, &lenA, pathW, lenW * sizeof(WCHAR)); 3328 pszPath[lenA] = 0; 3329 *pcchPath = lenA; 3330 } else { 3331 *pcchPath = lenA + 1; 3332 ret = E_POINTER; 3333 } 3334 } 3335 if(pathW != bufW) HeapFree(GetProcessHeap(), 0, pathW); 3336 RtlFreeUnicodeString(&urlW); 3337 return ret; 3338 } 3339 3340 /************************************************************************* 3341 * PathCreateFromUrlW [SHLWAPI.@] 3342 * 3343 * Create a path from a URL 3344 * 3345 * PARAMS 3346 * lpszUrl [I] URL to convert into a path 3347 * lpszPath [O] Output buffer for the resulting Path 3348 * pcchPath [I] Length of lpszPath 3349 * dwFlags [I] Flags controlling the conversion 3350 * 3351 * RETURNS 3352 * Success: S_OK. lpszPath contains the URL in path format, 3353 * Failure: An HRESULT error code such as E_INVALIDARG. 3354 */ 3355 HRESULT WINAPI PathCreateFromUrlW(LPCWSTR pszUrl, LPWSTR pszPath, 3356 LPDWORD pcchPath, DWORD dwReserved) 3357 { 3358 static const WCHAR file_colon[] = { 'f','i','l','e',':',0 }; 3359 static const WCHAR localhost[] = { 'l','o','c','a','l','h','o','s','t',0 }; 3360 DWORD nslashes, unescape, len; 3361 const WCHAR *src; 3362 WCHAR *tpath, *dst; 3363 HRESULT ret; 3364 3365 TRACE("(%s,%p,%p,0x%08x)\n", debugstr_w(pszUrl), pszPath, pcchPath, dwReserved); 3366 3367 if (!pszUrl || !pszPath || !pcchPath || !*pcchPath) 3368 return E_INVALIDARG; 3369 3370 if (lstrlenW(pszUrl) < 5) 3371 return E_INVALIDARG; 3372 3373 if (CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, pszUrl, 5, 3374 file_colon, 5) != CSTR_EQUAL) 3375 return E_INVALIDARG; 3376 pszUrl += 5; 3377 ret = S_OK; 3378 3379 src = pszUrl; 3380 nslashes = 0; 3381 while (*src == '/' || *src == '\\') { 3382 nslashes++; 3383 src++; 3384 } 3385 3386 /* We need a temporary buffer so we can compute what size to ask for. 3387 * We know that the final string won't be longer than the current pszUrl 3388 * plus at most two backslashes. All the other transformations make it 3389 * shorter. 3390 */ 3391 len = 2 + lstrlenW(pszUrl) + 1; 3392 if (*pcchPath < len) 3393 tpath = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR)); 3394 else 3395 tpath = pszPath; 3396 3397 len = 0; 3398 dst = tpath; 3399 unescape = 1; 3400 switch (nslashes) 3401 { 3402 case 0: 3403 /* 'file:' + escaped DOS path */ 3404 break; 3405 case 1: 3406 /* 'file:/' + escaped DOS path */ 3407 /* fall through */ 3408 case 3: 3409 /* 'file:///' (implied localhost) + escaped DOS path */ 3410 if (!isalphaW(*src) || (src[1] != ':' && src[1] != '|')) 3411 src -= 1; 3412 break; 3413 case 2: 3414 if (lstrlenW(src) >= 10 && CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, 3415 src, 9, localhost, 9) == CSTR_EQUAL && (src[9] == '/' || src[9] == '\\')) 3416 { 3417 /* 'file://localhost/' + escaped DOS path */ 3418 src += 10; 3419 } 3420 else if (isalphaW(*src) && (src[1] == ':' || src[1] == '|')) 3421 { 3422 /* 'file://' + unescaped DOS path */ 3423 unescape = 0; 3424 } 3425 else 3426 { 3427 /* 'file://hostname:port/path' (where path is escaped) 3428 * or 'file:' + escaped UNC path (\\server\share\path) 3429 * The second form is clearly specific to Windows and it might 3430 * even be doing a network lookup to try to figure it out. 3431 */ 3432 while (*src && *src != '/' && *src != '\\') 3433 src++; 3434 len = src - pszUrl; 3435 StrCpyNW(dst, pszUrl, len + 1); 3436 dst += len; 3437 if (*src && isalphaW(src[1]) && (src[2] == ':' || src[2] == '|')) 3438 { 3439 /* 'Forget' to add a trailing '/', just like Windows */ 3440 src++; 3441 } 3442 } 3443 break; 3444 case 4: 3445 /* 'file://' + unescaped UNC path (\\server\share\path) */ 3446 unescape = 0; 3447 if (isalphaW(*src) && (src[1] == ':' || src[1] == '|')) 3448 break; 3449 /* fall through */ 3450 default: 3451 /* 'file:/...' + escaped UNC path (\\server\share\path) */ 3452 src -= 2; 3453 } 3454 3455 /* Copy the remainder of the path */ 3456 len += lstrlenW(src); 3457 StrCpyW(dst, src); 3458 3459 /* First do the Windows-specific path conversions */ 3460 for (dst = tpath; *dst; dst++) 3461 if (*dst == '/') *dst = '\\'; 3462 if (isalphaW(*tpath) && tpath[1] == '|') 3463 tpath[1] = ':'; /* c| -> c: */ 3464 3465 /* And only then unescape the path (i.e. escaped slashes are left as is) */ 3466 if (unescape) 3467 { 3468 ret = UrlUnescapeW(tpath, NULL, &len, URL_UNESCAPE_INPLACE); 3469 if (ret == S_OK) 3470 { 3471 /* When working in-place UrlUnescapeW() does not set len */ 3472 len = lstrlenW(tpath); 3473 } 3474 } 3475 3476 if (*pcchPath < len + 1) 3477 { 3478 ret = E_POINTER; 3479 *pcchPath = len + 1; 3480 } 3481 else 3482 { 3483 *pcchPath = len; 3484 if (tpath != pszPath) 3485 StrCpyW(pszPath, tpath); 3486 } 3487 if (tpath != pszPath) 3488 HeapFree(GetProcessHeap(), 0, tpath); 3489 3490 TRACE("Returning (%u) %s\n", *pcchPath, debugstr_w(pszPath)); 3491 return ret; 3492 } 3493 3494 /************************************************************************* 3495 * PathCreateFromUrlAlloc [SHLWAPI.@] 3496 */ 3497 HRESULT WINAPI PathCreateFromUrlAlloc(LPCWSTR pszUrl, LPWSTR *pszPath, 3498 DWORD dwReserved) 3499 { 3500 WCHAR pathW[MAX_PATH]; 3501 DWORD size; 3502 HRESULT hr; 3503 3504 size = MAX_PATH; 3505 hr = PathCreateFromUrlW(pszUrl, pathW, &size, dwReserved); 3506 if (SUCCEEDED(hr)) 3507 { 3508 /* Yes, this is supposed to crash if pszPath is NULL */ 3509 *pszPath = StrDupW(pathW); 3510 } 3511 return hr; 3512 } 3513 3514 /************************************************************************* 3515 * PathRelativePathToA [SHLWAPI.@] 3516 * 3517 * Create a relative path from one path to another. 3518 * 3519 * PARAMS 3520 * lpszPath [O] Destination for relative path 3521 * lpszFrom [I] Source path 3522 * dwAttrFrom [I] File attribute of source path 3523 * lpszTo [I] Destination path 3524 * dwAttrTo [I] File attributes of destination path 3525 * 3526 * RETURNS 3527 * TRUE If a relative path can be formed. lpszPath contains the new path 3528 * FALSE If the paths are not relative or any parameters are invalid 3529 * 3530 * NOTES 3531 * lpszTo should be at least MAX_PATH in length. 3532 * 3533 * Calling this function with relative paths for lpszFrom or lpszTo may 3534 * give erroneous results. 3535 * 3536 * The Win32 version of this function contains a bug where the lpszTo string 3537 * may be referenced 1 byte beyond the end of the string. As a result random 3538 * garbage may be written to the output path, depending on what lies beyond 3539 * the last byte of the string. This bug occurs because of the behaviour of 3540 * PathCommonPrefix() (see notes for that function), and no workaround seems 3541 * possible with Win32. 3542 * 3543 * This bug has been fixed here, so for example the relative path from "\\" 3544 * to "\\" is correctly determined as "." in this implementation. 3545 */ 3546 BOOL WINAPI PathRelativePathToA(LPSTR lpszPath, LPCSTR lpszFrom, DWORD dwAttrFrom, 3547 LPCSTR lpszTo, DWORD dwAttrTo) 3548 { 3549 BOOL bRet = FALSE; 3550 3551 TRACE("(%p,%s,0x%08x,%s,0x%08x)\n", lpszPath, debugstr_a(lpszFrom), 3552 dwAttrFrom, debugstr_a(lpszTo), dwAttrTo); 3553 3554 if(lpszPath && lpszFrom && lpszTo) 3555 { 3556 WCHAR szPath[MAX_PATH]; 3557 WCHAR szFrom[MAX_PATH]; 3558 WCHAR szTo[MAX_PATH]; 3559 MultiByteToWideChar(CP_ACP,0,lpszFrom,-1,szFrom,MAX_PATH); 3560 MultiByteToWideChar(CP_ACP,0,lpszTo,-1,szTo,MAX_PATH); 3561 bRet = PathRelativePathToW(szPath,szFrom,dwAttrFrom,szTo,dwAttrTo); 3562 WideCharToMultiByte(CP_ACP,0,szPath,-1,lpszPath,MAX_PATH,0,0); 3563 } 3564 return bRet; 3565 } 3566 3567 /************************************************************************* 3568 * PathRelativePathToW [SHLWAPI.@] 3569 * 3570 * See PathRelativePathToA. 3571 */ 3572 BOOL WINAPI PathRelativePathToW(LPWSTR lpszPath, LPCWSTR lpszFrom, DWORD dwAttrFrom, 3573 LPCWSTR lpszTo, DWORD dwAttrTo) 3574 { 3575 static const WCHAR szPrevDirSlash[] = { '.', '.', '\\', '\0' }; 3576 static const WCHAR szPrevDir[] = { '.', '.', '\0' }; 3577 WCHAR szFrom[MAX_PATH]; 3578 WCHAR szTo[MAX_PATH]; 3579 DWORD dwLen; 3580 3581 TRACE("(%p,%s,0x%08x,%s,0x%08x)\n", lpszPath, debugstr_w(lpszFrom), 3582 dwAttrFrom, debugstr_w(lpszTo), dwAttrTo); 3583 3584 if(!lpszPath || !lpszFrom || !lpszTo) 3585 return FALSE; 3586 3587 *lpszPath = '\0'; 3588 lstrcpynW(szFrom, lpszFrom, MAX_PATH); 3589 lstrcpynW(szTo, lpszTo, MAX_PATH); 3590 3591 if(!(dwAttrFrom & FILE_ATTRIBUTE_DIRECTORY)) 3592 PathRemoveFileSpecW(szFrom); 3593 if(!(dwAttrTo & FILE_ATTRIBUTE_DIRECTORY)) 3594 PathRemoveFileSpecW(szTo); 3595 3596 /* Paths can only be relative if they have a common root */ 3597 if(!(dwLen = PathCommonPrefixW(szFrom, szTo, 0))) 3598 return FALSE; 3599 3600 /* Strip off lpszFrom components to the root, by adding "..\" */ 3601 lpszFrom = szFrom + dwLen; 3602 if (!*lpszFrom) 3603 { 3604 lpszPath[0] = '.'; 3605 lpszPath[1] = '\0'; 3606 } 3607 if (*lpszFrom == '\\') 3608 lpszFrom++; 3609 3610 while (*lpszFrom) 3611 { 3612 lpszFrom = PathFindNextComponentW(lpszFrom); 3613 strcatW(lpszPath, *lpszFrom ? szPrevDirSlash : szPrevDir); 3614 } 3615 3616 /* From the root add the components of lpszTo */ 3617 lpszTo += dwLen; 3618 /* We check lpszTo[-1] to avoid skipping end of string. See the notes for 3619 * this function. 3620 */ 3621 if (*lpszTo && lpszTo[-1]) 3622 { 3623 if (*lpszTo != '\\') 3624 lpszTo--; 3625 dwLen = strlenW(lpszPath); 3626 if (dwLen + strlenW(lpszTo) >= MAX_PATH) 3627 { 3628 *lpszPath = '\0'; 3629 return FALSE; 3630 } 3631 strcpyW(lpszPath + dwLen, lpszTo); 3632 } 3633 return TRUE; 3634 } 3635 3636 /************************************************************************* 3637 * PathUnmakeSystemFolderA [SHLWAPI.@] 3638 * 3639 * Remove the system folder attributes from a path. 3640 * 3641 * PARAMS 3642 * lpszPath [I] The path to remove attributes from 3643 * 3644 * RETURNS 3645 * Success: TRUE. 3646 * Failure: FALSE, if lpszPath is NULL, empty, not a directory, or calling 3647 * SetFileAttributesA() fails. 3648 */ 3649 BOOL WINAPI PathUnmakeSystemFolderA(LPCSTR lpszPath) 3650 { 3651 DWORD dwAttr; 3652 3653 TRACE("(%s)\n", debugstr_a(lpszPath)); 3654 3655 if (!lpszPath || !*lpszPath || (dwAttr = GetFileAttributesA(lpszPath)) == INVALID_FILE_ATTRIBUTES || 3656 !(dwAttr & FILE_ATTRIBUTE_DIRECTORY)) 3657 return FALSE; 3658 3659 dwAttr &= ~(FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM); 3660 return SetFileAttributesA(lpszPath, dwAttr); 3661 } 3662 3663 /************************************************************************* 3664 * PathUnmakeSystemFolderW [SHLWAPI.@] 3665 * 3666 * See PathUnmakeSystemFolderA. 3667 */ 3668 BOOL WINAPI PathUnmakeSystemFolderW(LPCWSTR lpszPath) 3669 { 3670 DWORD dwAttr; 3671 3672 TRACE("(%s)\n", debugstr_w(lpszPath)); 3673 3674 if (!lpszPath || !*lpszPath || (dwAttr = GetFileAttributesW(lpszPath)) == INVALID_FILE_ATTRIBUTES || 3675 !(dwAttr & FILE_ATTRIBUTE_DIRECTORY)) 3676 return FALSE; 3677 3678 dwAttr &= ~(FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM); 3679 return SetFileAttributesW(lpszPath, dwAttr); 3680 } 3681 3682 3683 /************************************************************************* 3684 * PathSetDlgItemPathA [SHLWAPI.@] 3685 * 3686 * Set the text of a dialog item to a path, shrinking the path to fit 3687 * if it is too big for the item. 3688 * 3689 * PARAMS 3690 * hDlg [I] Dialog handle 3691 * id [I] ID of item in the dialog 3692 * lpszPath [I] Path to set as the items text 3693 * 3694 * RETURNS 3695 * Nothing. 3696 * 3697 * NOTES 3698 * If lpszPath is NULL, a blank string ("") is set (i.e. The previous 3699 * window text is erased). 3700 */ 3701 VOID WINAPI PathSetDlgItemPathA(HWND hDlg, int id, LPCSTR lpszPath) 3702 { 3703 WCHAR szPath[MAX_PATH]; 3704 3705 TRACE("(%p,%8x,%s)\n",hDlg, id, debugstr_a(lpszPath)); 3706 3707 if (lpszPath) 3708 MultiByteToWideChar(CP_ACP,0,lpszPath,-1,szPath,MAX_PATH); 3709 else 3710 szPath[0] = '\0'; 3711 PathSetDlgItemPathW(hDlg, id, szPath); 3712 } 3713 3714 /************************************************************************* 3715 * PathSetDlgItemPathW [SHLWAPI.@] 3716 * 3717 * See PathSetDlgItemPathA. 3718 */ 3719 VOID WINAPI PathSetDlgItemPathW(HWND hDlg, int id, LPCWSTR lpszPath) 3720 { 3721 WCHAR path[MAX_PATH + 1]; 3722 HWND hwItem; 3723 RECT rect; 3724 HDC hdc; 3725 HGDIOBJ hPrevObj; 3726 3727 TRACE("(%p,%8x,%s)\n",hDlg, id, debugstr_w(lpszPath)); 3728 3729 if (!(hwItem = GetDlgItem(hDlg, id))) 3730 return; 3731 3732 if (lpszPath) 3733 lstrcpynW(path, lpszPath, sizeof(path) / sizeof(WCHAR)); 3734 else 3735 path[0] = '\0'; 3736 3737 GetClientRect(hwItem, &rect); 3738 hdc = GetDC(hDlg); 3739 hPrevObj = SelectObject(hdc, (HGDIOBJ)SendMessageW(hwItem,WM_GETFONT,0,0)); 3740 3741 if (hPrevObj) 3742 { 3743 PathCompactPathW(hdc, path, rect.right); 3744 SelectObject(hdc, hPrevObj); 3745 } 3746 3747 ReleaseDC(hDlg, hdc); 3748 SetWindowTextW(hwItem, path); 3749 } 3750 3751 /************************************************************************* 3752 * PathIsNetworkPathA [SHLWAPI.@] 3753 * 3754 * Determine if the given path is a network path. 3755 * 3756 * PARAMS 3757 * lpszPath [I] Path to check 3758 * 3759 * RETURNS 3760 * TRUE If lpszPath is a UNC share or mapped network drive, or 3761 * FALSE If lpszPath is a local drive or cannot be determined 3762 */ 3763 BOOL WINAPI PathIsNetworkPathA(LPCSTR lpszPath) 3764 { 3765 int dwDriveNum; 3766 3767 TRACE("(%s)\n",debugstr_a(lpszPath)); 3768 3769 if (!lpszPath) 3770 return FALSE; 3771 if (*lpszPath == '\\' && lpszPath[1] == '\\') 3772 return TRUE; 3773 dwDriveNum = PathGetDriveNumberA(lpszPath); 3774 if (dwDriveNum == -1) 3775 return FALSE; 3776 #ifdef __REACTOS__ 3777 return IsNetDrive(dwDriveNum); 3778 #else 3779 GET_FUNC(pIsNetDrive, shell32, (LPCSTR)66, FALSE); /* ord 66 = shell32.IsNetDrive */ 3780 return pIsNetDrive(dwDriveNum); 3781 #endif 3782 } 3783 3784 /************************************************************************* 3785 * PathIsNetworkPathW [SHLWAPI.@] 3786 * 3787 * See PathIsNetworkPathA. 3788 */ 3789 BOOL WINAPI PathIsNetworkPathW(LPCWSTR lpszPath) 3790 { 3791 int dwDriveNum; 3792 3793 TRACE("(%s)\n", debugstr_w(lpszPath)); 3794 3795 if (!lpszPath) 3796 return FALSE; 3797 if (*lpszPath == '\\' && lpszPath[1] == '\\') 3798 return TRUE; 3799 dwDriveNum = PathGetDriveNumberW(lpszPath); 3800 if (dwDriveNum == -1) 3801 return FALSE; 3802 #ifdef __REACTOS__ 3803 return IsNetDrive(dwDriveNum); 3804 #else 3805 GET_FUNC(pIsNetDrive, shell32, (LPCSTR)66, FALSE); /* ord 66 = shell32.IsNetDrive */ 3806 return pIsNetDrive(dwDriveNum); 3807 #endif 3808 } 3809 3810 /************************************************************************* 3811 * PathIsLFNFileSpecA [SHLWAPI.@] 3812 * 3813 * Determine if the given path is a long file name 3814 * 3815 * PARAMS 3816 * lpszPath [I] Path to check 3817 * 3818 * RETURNS 3819 * TRUE If path is a long file name, 3820 * FALSE If path is a valid DOS short file name 3821 */ 3822 BOOL WINAPI PathIsLFNFileSpecA(LPCSTR lpszPath) 3823 { 3824 DWORD dwNameLen = 0, dwExtLen = 0; 3825 3826 TRACE("(%s)\n",debugstr_a(lpszPath)); 3827 3828 if (!lpszPath) 3829 return FALSE; 3830 3831 while (*lpszPath) 3832 { 3833 if (*lpszPath == ' ') 3834 return TRUE; /* DOS names cannot have spaces */ 3835 if (*lpszPath == '.') 3836 { 3837 if (dwExtLen) 3838 return TRUE; /* DOS names have only one dot */ 3839 dwExtLen = 1; 3840 } 3841 else if (dwExtLen) 3842 { 3843 dwExtLen++; 3844 if (dwExtLen > 4) 3845 return TRUE; /* DOS extensions are <= 3 chars*/ 3846 } 3847 else 3848 { 3849 dwNameLen++; 3850 if (dwNameLen > 8) 3851 return TRUE; /* DOS names are <= 8 chars */ 3852 } 3853 lpszPath += IsDBCSLeadByte(*lpszPath) ? 2 : 1; 3854 } 3855 return FALSE; /* Valid DOS path */ 3856 } 3857 3858 /************************************************************************* 3859 * PathIsLFNFileSpecW [SHLWAPI.@] 3860 * 3861 * See PathIsLFNFileSpecA. 3862 */ 3863 BOOL WINAPI PathIsLFNFileSpecW(LPCWSTR lpszPath) 3864 { 3865 DWORD dwNameLen = 0, dwExtLen = 0; 3866 3867 TRACE("(%s)\n",debugstr_w(lpszPath)); 3868 3869 if (!lpszPath) 3870 return FALSE; 3871 3872 while (*lpszPath) 3873 { 3874 if (*lpszPath == ' ') 3875 return TRUE; /* DOS names cannot have spaces */ 3876 if (*lpszPath == '.') 3877 { 3878 if (dwExtLen) 3879 return TRUE; /* DOS names have only one dot */ 3880 dwExtLen = 1; 3881 } 3882 else if (dwExtLen) 3883 { 3884 dwExtLen++; 3885 if (dwExtLen > 4) 3886 return TRUE; /* DOS extensions are <= 3 chars*/ 3887 } 3888 else 3889 { 3890 dwNameLen++; 3891 if (dwNameLen > 8) 3892 return TRUE; /* DOS names are <= 8 chars */ 3893 } 3894 lpszPath++; 3895 } 3896 return FALSE; /* Valid DOS path */ 3897 } 3898 3899 /************************************************************************* 3900 * PathIsDirectoryEmptyA [SHLWAPI.@] 3901 * 3902 * Determine if a given directory is empty. 3903 * 3904 * PARAMS 3905 * lpszPath [I] Directory to check 3906 * 3907 * RETURNS 3908 * TRUE If the directory exists and contains no files, 3909 * FALSE Otherwise 3910 */ 3911 BOOL WINAPI PathIsDirectoryEmptyA(LPCSTR lpszPath) 3912 { 3913 BOOL bRet = FALSE; 3914 3915 TRACE("(%s)\n",debugstr_a(lpszPath)); 3916 3917 if (lpszPath) 3918 { 3919 WCHAR szPath[MAX_PATH]; 3920 MultiByteToWideChar(CP_ACP,0,lpszPath,-1,szPath,MAX_PATH); 3921 bRet = PathIsDirectoryEmptyW(szPath); 3922 } 3923 return bRet; 3924 } 3925 3926 /************************************************************************* 3927 * PathIsDirectoryEmptyW [SHLWAPI.@] 3928 * 3929 * See PathIsDirectoryEmptyA. 3930 */ 3931 BOOL WINAPI PathIsDirectoryEmptyW(LPCWSTR lpszPath) 3932 { 3933 static const WCHAR szAllFiles[] = { '*', '.', '*', '\0' }; 3934 WCHAR szSearch[MAX_PATH]; 3935 DWORD dwLen; 3936 HANDLE hfind; 3937 BOOL retVal = TRUE; 3938 WIN32_FIND_DATAW find_data; 3939 3940 TRACE("(%s)\n",debugstr_w(lpszPath)); 3941 3942 if (!lpszPath || !PathIsDirectoryW(lpszPath)) 3943 return FALSE; 3944 3945 lstrcpynW(szSearch, lpszPath, MAX_PATH); 3946 PathAddBackslashW(szSearch); 3947 dwLen = strlenW(szSearch); 3948 if (dwLen > MAX_PATH - 4) 3949 return FALSE; 3950 3951 strcpyW(szSearch + dwLen, szAllFiles); 3952 hfind = FindFirstFileW(szSearch, &find_data); 3953 if (hfind == INVALID_HANDLE_VALUE) 3954 return FALSE; 3955 3956 do 3957 { 3958 if (find_data.cFileName[0] == '.') 3959 { 3960 if (find_data.cFileName[1] == '\0') continue; 3961 if (find_data.cFileName[1] == '.' && find_data.cFileName[2] == '\0') continue; 3962 } 3963 3964 retVal = FALSE; 3965 break; 3966 } 3967 while (FindNextFileW(hfind, &find_data)); 3968 3969 FindClose(hfind); 3970 return retVal; 3971 } 3972 3973 3974 /************************************************************************* 3975 * PathFindSuffixArrayA [SHLWAPI.@] 3976 * 3977 * Find a suffix string in an array of suffix strings 3978 * 3979 * PARAMS 3980 * lpszSuffix [I] Suffix string to search for 3981 * lppszArray [I] Array of suffix strings to search 3982 * dwCount [I] Number of elements in lppszArray 3983 * 3984 * RETURNS 3985 * Success: The index of the position of lpszSuffix in lppszArray 3986 * Failure: 0, if any parameters are invalid or lpszSuffix is not found 3987 * 3988 * NOTES 3989 * The search is case sensitive. 3990 * The match is made against the end of the suffix string, so for example: 3991 * lpszSuffix="fooBAR" matches "BAR", but lpszSuffix="fooBARfoo" does not. 3992 */ 3993 LPCSTR WINAPI PathFindSuffixArrayA(LPCSTR lpszSuffix, LPCSTR *lppszArray, int dwCount) 3994 { 3995 size_t dwLen; 3996 int dwRet = 0; 3997 3998 TRACE("(%s,%p,%d)\n",debugstr_a(lpszSuffix), lppszArray, dwCount); 3999 4000 if (lpszSuffix && lppszArray && dwCount > 0) 4001 { 4002 dwLen = strlen(lpszSuffix); 4003 4004 while (dwRet < dwCount) 4005 { 4006 size_t dwCompareLen = strlen(*lppszArray); 4007 if (dwCompareLen < dwLen) 4008 { 4009 if (!strcmp(lpszSuffix + dwLen - dwCompareLen, *lppszArray)) 4010 return *lppszArray; /* Found */ 4011 } 4012 dwRet++; 4013 lppszArray++; 4014 } 4015 } 4016 return NULL; 4017 } 4018 4019 /************************************************************************* 4020 * PathFindSuffixArrayW [SHLWAPI.@] 4021 * 4022 * See PathFindSuffixArrayA. 4023 */ 4024 LPCWSTR WINAPI PathFindSuffixArrayW(LPCWSTR lpszSuffix, LPCWSTR *lppszArray, int dwCount) 4025 { 4026 size_t dwLen; 4027 int dwRet = 0; 4028 4029 TRACE("(%s,%p,%d)\n",debugstr_w(lpszSuffix), lppszArray, dwCount); 4030 4031 if (lpszSuffix && lppszArray && dwCount > 0) 4032 { 4033 dwLen = strlenW(lpszSuffix); 4034 4035 while (dwRet < dwCount) 4036 { 4037 size_t dwCompareLen = strlenW(*lppszArray); 4038 if (dwCompareLen < dwLen) 4039 { 4040 if (!strcmpW(lpszSuffix + dwLen - dwCompareLen, *lppszArray)) 4041 return *lppszArray; /* Found */ 4042 } 4043 dwRet++; 4044 lppszArray++; 4045 } 4046 } 4047 return NULL; 4048 } 4049 4050 /************************************************************************* 4051 * PathUndecorateA [SHLWAPI.@] 4052 * 4053 * Undecorate a file path 4054 * 4055 * PARAMS 4056 * lpszPath [I/O] Path to remove any decoration from 4057 * 4058 * RETURNS 4059 * Nothing 4060 * 4061 * NOTES 4062 * A decorations form is "path[n].ext" where "n" is an optional decimal number. 4063 */ 4064 VOID WINAPI PathUndecorateA(LPSTR lpszPath) 4065 { 4066 TRACE("(%s)\n",debugstr_a(lpszPath)); 4067 4068 if (lpszPath) 4069 { 4070 LPSTR lpszExt = PathFindExtensionA(lpszPath); 4071 if (lpszExt > lpszPath && lpszExt[-1] == ']') 4072 { 4073 LPSTR lpszSkip = lpszExt - 2; 4074 if (*lpszSkip == '[') 4075 lpszSkip++; /* [] (no number) */ 4076 else 4077 while (lpszSkip > lpszPath && isdigit(lpszSkip[-1])) 4078 lpszSkip--; 4079 if (lpszSkip > lpszPath && lpszSkip[-1] == '[' && lpszSkip[-2] != '\\') 4080 { 4081 /* remove the [n] */ 4082 lpszSkip--; 4083 while (*lpszExt) 4084 *lpszSkip++ = *lpszExt++; 4085 *lpszSkip = '\0'; 4086 } 4087 } 4088 } 4089 } 4090 4091 /************************************************************************* 4092 * PathUndecorateW [SHLWAPI.@] 4093 * 4094 * See PathUndecorateA. 4095 */ 4096 VOID WINAPI PathUndecorateW(LPWSTR lpszPath) 4097 { 4098 TRACE("(%s)\n",debugstr_w(lpszPath)); 4099 4100 if (lpszPath) 4101 { 4102 LPWSTR lpszExt = PathFindExtensionW(lpszPath); 4103 if (lpszExt > lpszPath && lpszExt[-1] == ']') 4104 { 4105 LPWSTR lpszSkip = lpszExt - 2; 4106 if (*lpszSkip == '[') 4107 lpszSkip++; /* [] (no number) */ 4108 else 4109 while (lpszSkip > lpszPath && isdigitW(lpszSkip[-1])) 4110 lpszSkip--; 4111 if (lpszSkip > lpszPath && lpszSkip[-1] == '[' && lpszSkip[-2] != '\\') 4112 { 4113 /* remove the [n] */ 4114 lpszSkip--; 4115 while (*lpszExt) 4116 *lpszSkip++ = *lpszExt++; 4117 *lpszSkip = '\0'; 4118 } 4119 } 4120 } 4121 } 4122 4123 /************************************************************************* 4124 * PathUnExpandEnvStringsA [SHLWAPI.@] 4125 * 4126 * Substitute folder names in a path with their corresponding environment 4127 * strings. 4128 * 4129 * PARAMS 4130 * path [I] Buffer containing the path to unexpand. 4131 * buffer [O] Buffer to receive the unexpanded path. 4132 * buf_len [I] Size of pszBuf in characters. 4133 * 4134 * RETURNS 4135 * Success: TRUE 4136 * Failure: FALSE 4137 */ 4138 BOOL WINAPI PathUnExpandEnvStringsA(LPCSTR path, LPSTR buffer, UINT buf_len) 4139 { 4140 WCHAR bufferW[MAX_PATH], *pathW; 4141 DWORD len; 4142 BOOL ret; 4143 4144 TRACE("(%s, %p, %d)\n", debugstr_a(path), buffer, buf_len); 4145 4146 pathW = heap_strdupAtoW(path); 4147 if (!pathW) return FALSE; 4148 4149 ret = PathUnExpandEnvStringsW(pathW, bufferW, MAX_PATH); 4150 HeapFree(GetProcessHeap(), 0, pathW); 4151 if (!ret) return FALSE; 4152 4153 len = WideCharToMultiByte(CP_ACP, 0, bufferW, -1, NULL, 0, NULL, NULL); 4154 if (buf_len < len + 1) return FALSE; 4155 4156 WideCharToMultiByte(CP_ACP, 0, bufferW, -1, buffer, buf_len, NULL, NULL); 4157 return TRUE; 4158 } 4159 4160 static const WCHAR allusersprofileW[] = {'%','A','L','L','U','S','E','R','S','P','R','O','F','I','L','E','%',0}; 4161 static const WCHAR appdataW[] = {'%','A','P','P','D','A','T','A','%',0}; 4162 static const WCHAR programfilesW[] = {'%','P','r','o','g','r','a','m','F','i','l','e','s','%',0}; 4163 static const WCHAR systemrootW[] = {'%','S','y','s','t','e','m','R','o','o','t','%',0}; 4164 static const WCHAR systemdriveW[] = {'%','S','y','s','t','e','m','D','r','i','v','e','%',0}; 4165 static const WCHAR userprofileW[] = {'%','U','S','E','R','P','R','O','F','I','L','E','%',0}; 4166 4167 struct envvars_map 4168 { 4169 const WCHAR *var; 4170 UINT varlen; 4171 WCHAR path[MAX_PATH]; 4172 DWORD len; 4173 }; 4174 4175 static void init_envvars_map(struct envvars_map *map) 4176 { 4177 while (map->var) 4178 { 4179 map->len = ExpandEnvironmentStringsW(map->var, map->path, sizeof(map->path)/sizeof(WCHAR)); 4180 /* exclude null from length */ 4181 if (map->len) map->len--; 4182 map++; 4183 } 4184 } 4185 4186 /************************************************************************* 4187 * PathUnExpandEnvStringsW [SHLWAPI.@] 4188 * 4189 * Unicode version of PathUnExpandEnvStringsA. 4190 */ 4191 BOOL WINAPI PathUnExpandEnvStringsW(LPCWSTR path, LPWSTR buffer, UINT buf_len) 4192 { 4193 static struct envvars_map null_var = {NULL, 0, {0}, 0}; 4194 struct envvars_map *match = &null_var, *cur; 4195 struct envvars_map envvars[] = { 4196 { allusersprofileW, sizeof(allusersprofileW)/sizeof(WCHAR) }, 4197 { appdataW, sizeof(appdataW)/sizeof(WCHAR) }, 4198 { programfilesW, sizeof(programfilesW)/sizeof(WCHAR) }, 4199 { systemrootW, sizeof(systemrootW)/sizeof(WCHAR) }, 4200 { systemdriveW, sizeof(systemdriveW)/sizeof(WCHAR) }, 4201 { userprofileW, sizeof(userprofileW)/sizeof(WCHAR) }, 4202 { NULL } 4203 }; 4204 DWORD pathlen; 4205 UINT needed; 4206 4207 TRACE("(%s, %p, %d)\n", debugstr_w(path), buffer, buf_len); 4208 4209 pathlen = strlenW(path); 4210 init_envvars_map(envvars); 4211 cur = envvars; 4212 while (cur->var) 4213 { 4214 /* path can't contain expanded value or value wasn't retrieved */ 4215 if (cur->len == 0 || cur->len > pathlen || strncmpiW(cur->path, path, cur->len)) 4216 { 4217 cur++; 4218 continue; 4219 } 4220 4221 if (cur->len > match->len) 4222 match = cur; 4223 cur++; 4224 } 4225 4226 /* 'varlen' includes NULL termination char */ 4227 needed = match->varlen + pathlen - match->len; 4228 if (match->len == 0 || needed > buf_len) return FALSE; 4229 4230 strcpyW(buffer, match->var); 4231 strcatW(buffer, &path[match->len]); 4232 TRACE("ret %s\n", debugstr_w(buffer)); 4233 4234 return TRUE; 4235 } 4236 4237 /************************************************************************* 4238 * @ [SHLWAPI.440] 4239 * 4240 * Find localised or default web content in "%WINDOWS%\web\". 4241 * 4242 * PARAMS 4243 * lpszFile [I] File name containing content to look for 4244 * lpszPath [O] Buffer to contain the full path to the file 4245 * dwPathLen [I] Length of lpszPath 4246 * 4247 * RETURNS 4248 * Success: S_OK. lpszPath contains the full path to the content. 4249 * Failure: E_FAIL. The content does not exist or lpszPath is too short. 4250 */ 4251 HRESULT WINAPI SHGetWebFolderFilePathA(LPCSTR lpszFile, LPSTR lpszPath, DWORD dwPathLen) 4252 { 4253 WCHAR szFile[MAX_PATH], szPath[MAX_PATH]; 4254 HRESULT hRet; 4255 4256 TRACE("(%s,%p,%d)\n", lpszFile, lpszPath, dwPathLen); 4257 4258 MultiByteToWideChar(CP_ACP, 0, lpszFile, -1, szFile, MAX_PATH); 4259 szPath[0] = '\0'; 4260 hRet = SHGetWebFolderFilePathW(szFile, szPath, dwPathLen); 4261 WideCharToMultiByte(CP_ACP, 0, szPath, -1, lpszPath, dwPathLen, 0, 0); 4262 return hRet; 4263 } 4264 4265 /************************************************************************* 4266 * @ [SHLWAPI.441] 4267 * 4268 * Unicode version of SHGetWebFolderFilePathA. 4269 */ 4270 HRESULT WINAPI SHGetWebFolderFilePathW(LPCWSTR lpszFile, LPWSTR lpszPath, DWORD dwPathLen) 4271 { 4272 static const WCHAR szWeb[] = {'\\','W','e','b','\\','\0'}; 4273 static const WCHAR szWebMui[] = {'m','u','i','\\','%','0','4','x','\\','\0'}; 4274 #define szWebLen (sizeof(szWeb)/sizeof(WCHAR)) 4275 #define szWebMuiLen ((sizeof(szWebMui)+1)/sizeof(WCHAR)) 4276 DWORD dwLen, dwFileLen; 4277 LANGID lidSystem, lidUser; 4278 4279 TRACE("(%s,%p,%d)\n", debugstr_w(lpszFile), lpszPath, dwPathLen); 4280 4281 /* Get base directory for web content */ 4282 dwLen = GetSystemWindowsDirectoryW(lpszPath, dwPathLen); 4283 if (dwLen > 0 && lpszPath[dwLen-1] == '\\') 4284 dwLen--; 4285 4286 dwFileLen = strlenW(lpszFile); 4287 4288 if (dwLen + dwFileLen + szWebLen >= dwPathLen) 4289 return E_FAIL; /* lpszPath too short */ 4290 4291 strcpyW(lpszPath+dwLen, szWeb); 4292 dwLen += szWebLen; 4293 dwPathLen = dwPathLen - dwLen; /* Remaining space */ 4294 4295 lidSystem = GetSystemDefaultUILanguage(); 4296 lidUser = GetUserDefaultUILanguage(); 4297 4298 if (lidSystem != lidUser) 4299 { 4300 if (dwFileLen + szWebMuiLen < dwPathLen) 4301 { 4302 /* Use localised content in the users UI language if present */ 4303 wsprintfW(lpszPath + dwLen, szWebMui, lidUser); 4304 strcpyW(lpszPath + dwLen + szWebMuiLen, lpszFile); 4305 if (PathFileExistsW(lpszPath)) 4306 return S_OK; 4307 } 4308 } 4309 4310 /* Fall back to OS default installed content */ 4311 strcpyW(lpszPath + dwLen, lpszFile); 4312 if (PathFileExistsW(lpszPath)) 4313 return S_OK; 4314 return E_FAIL; 4315 } 4316 4317 #ifndef __REACTOS__ /* Defined in <shlwapi_undoc.h> */ 4318 #define PATH_CHAR_CLASS_LETTER 0x00000001 4319 #define PATH_CHAR_CLASS_ASTERIX 0x00000002 4320 #define PATH_CHAR_CLASS_DOT 0x00000004 4321 #define PATH_CHAR_CLASS_BACKSLASH 0x00000008 4322 #define PATH_CHAR_CLASS_COLON 0x00000010 4323 #define PATH_CHAR_CLASS_SEMICOLON 0x00000020 4324 #define PATH_CHAR_CLASS_COMMA 0x00000040 4325 #define PATH_CHAR_CLASS_SPACE 0x00000080 4326 #define PATH_CHAR_CLASS_OTHER_VALID 0x00000100 4327 #define PATH_CHAR_CLASS_DOUBLEQUOTE 0x00000200 4328 4329 #define PATH_CHAR_CLASS_INVALID 0x00000000 4330 #define PATH_CHAR_CLASS_ANY 0xffffffff 4331 #endif 4332 4333 static const DWORD SHELL_charclass[] = 4334 { 4335 /* 0x00 */ PATH_CHAR_CLASS_INVALID, /* 0x01 */ PATH_CHAR_CLASS_INVALID, 4336 /* 0x02 */ PATH_CHAR_CLASS_INVALID, /* 0x03 */ PATH_CHAR_CLASS_INVALID, 4337 /* 0x04 */ PATH_CHAR_CLASS_INVALID, /* 0x05 */ PATH_CHAR_CLASS_INVALID, 4338 /* 0x06 */ PATH_CHAR_CLASS_INVALID, /* 0x07 */ PATH_CHAR_CLASS_INVALID, 4339 /* 0x08 */ PATH_CHAR_CLASS_INVALID, /* 0x09 */ PATH_CHAR_CLASS_INVALID, 4340 /* 0x0a */ PATH_CHAR_CLASS_INVALID, /* 0x0b */ PATH_CHAR_CLASS_INVALID, 4341 /* 0x0c */ PATH_CHAR_CLASS_INVALID, /* 0x0d */ PATH_CHAR_CLASS_INVALID, 4342 /* 0x0e */ PATH_CHAR_CLASS_INVALID, /* 0x0f */ PATH_CHAR_CLASS_INVALID, 4343 /* 0x10 */ PATH_CHAR_CLASS_INVALID, /* 0x11 */ PATH_CHAR_CLASS_INVALID, 4344 /* 0x12 */ PATH_CHAR_CLASS_INVALID, /* 0x13 */ PATH_CHAR_CLASS_INVALID, 4345 /* 0x14 */ PATH_CHAR_CLASS_INVALID, /* 0x15 */ PATH_CHAR_CLASS_INVALID, 4346 /* 0x16 */ PATH_CHAR_CLASS_INVALID, /* 0x17 */ PATH_CHAR_CLASS_INVALID, 4347 /* 0x18 */ PATH_CHAR_CLASS_INVALID, /* 0x19 */ PATH_CHAR_CLASS_INVALID, 4348 /* 0x1a */ PATH_CHAR_CLASS_INVALID, /* 0x1b */ PATH_CHAR_CLASS_INVALID, 4349 /* 0x1c */ PATH_CHAR_CLASS_INVALID, /* 0x1d */ PATH_CHAR_CLASS_INVALID, 4350 /* 0x1e */ PATH_CHAR_CLASS_INVALID, /* 0x1f */ PATH_CHAR_CLASS_INVALID, 4351 /* ' ' */ PATH_CHAR_CLASS_SPACE, /* '!' */ PATH_CHAR_CLASS_OTHER_VALID, 4352 /* '"' */ PATH_CHAR_CLASS_DOUBLEQUOTE, /* '#' */ PATH_CHAR_CLASS_OTHER_VALID, 4353 /* '$' */ PATH_CHAR_CLASS_OTHER_VALID, /* '%' */ PATH_CHAR_CLASS_OTHER_VALID, 4354 /* '&' */ PATH_CHAR_CLASS_OTHER_VALID, /* '\'' */ PATH_CHAR_CLASS_OTHER_VALID, 4355 /* '(' */ PATH_CHAR_CLASS_OTHER_VALID, /* ')' */ PATH_CHAR_CLASS_OTHER_VALID, 4356 /* '*' */ PATH_CHAR_CLASS_ASTERIX, /* '+' */ PATH_CHAR_CLASS_OTHER_VALID, 4357 /* ',' */ PATH_CHAR_CLASS_COMMA, /* '-' */ PATH_CHAR_CLASS_OTHER_VALID, 4358 /* '.' */ PATH_CHAR_CLASS_DOT, /* '/' */ PATH_CHAR_CLASS_INVALID, 4359 /* '0' */ PATH_CHAR_CLASS_OTHER_VALID, /* '1' */ PATH_CHAR_CLASS_OTHER_VALID, 4360 /* '2' */ PATH_CHAR_CLASS_OTHER_VALID, /* '3' */ PATH_CHAR_CLASS_OTHER_VALID, 4361 /* '4' */ PATH_CHAR_CLASS_OTHER_VALID, /* '5' */ PATH_CHAR_CLASS_OTHER_VALID, 4362 /* '6' */ PATH_CHAR_CLASS_OTHER_VALID, /* '7' */ PATH_CHAR_CLASS_OTHER_VALID, 4363 /* '8' */ PATH_CHAR_CLASS_OTHER_VALID, /* '9' */ PATH_CHAR_CLASS_OTHER_VALID, 4364 /* ':' */ PATH_CHAR_CLASS_COLON, /* ';' */ PATH_CHAR_CLASS_SEMICOLON, 4365 /* '<' */ PATH_CHAR_CLASS_INVALID, /* '=' */ PATH_CHAR_CLASS_OTHER_VALID, 4366 /* '>' */ PATH_CHAR_CLASS_INVALID, /* '?' */ PATH_CHAR_CLASS_LETTER, 4367 /* '@' */ PATH_CHAR_CLASS_OTHER_VALID, /* 'A' */ PATH_CHAR_CLASS_ANY, 4368 /* 'B' */ PATH_CHAR_CLASS_ANY, /* 'C' */ PATH_CHAR_CLASS_ANY, 4369 /* 'D' */ PATH_CHAR_CLASS_ANY, /* 'E' */ PATH_CHAR_CLASS_ANY, 4370 /* 'F' */ PATH_CHAR_CLASS_ANY, /* 'G' */ PATH_CHAR_CLASS_ANY, 4371 /* 'H' */ PATH_CHAR_CLASS_ANY, /* 'I' */ PATH_CHAR_CLASS_ANY, 4372 /* 'J' */ PATH_CHAR_CLASS_ANY, /* 'K' */ PATH_CHAR_CLASS_ANY, 4373 /* 'L' */ PATH_CHAR_CLASS_ANY, /* 'M' */ PATH_CHAR_CLASS_ANY, 4374 /* 'N' */ PATH_CHAR_CLASS_ANY, /* 'O' */ PATH_CHAR_CLASS_ANY, 4375 /* 'P' */ PATH_CHAR_CLASS_ANY, /* 'Q' */ PATH_CHAR_CLASS_ANY, 4376 /* 'R' */ PATH_CHAR_CLASS_ANY, /* 'S' */ PATH_CHAR_CLASS_ANY, 4377 /* 'T' */ PATH_CHAR_CLASS_ANY, /* 'U' */ PATH_CHAR_CLASS_ANY, 4378 /* 'V' */ PATH_CHAR_CLASS_ANY, /* 'W' */ PATH_CHAR_CLASS_ANY, 4379 /* 'X' */ PATH_CHAR_CLASS_ANY, /* 'Y' */ PATH_CHAR_CLASS_ANY, 4380 /* 'Z' */ PATH_CHAR_CLASS_ANY, /* '[' */ PATH_CHAR_CLASS_OTHER_VALID, 4381 /* '\\' */ PATH_CHAR_CLASS_BACKSLASH, /* ']' */ PATH_CHAR_CLASS_OTHER_VALID, 4382 /* '^' */ PATH_CHAR_CLASS_OTHER_VALID, /* '_' */ PATH_CHAR_CLASS_OTHER_VALID, 4383 /* '`' */ PATH_CHAR_CLASS_OTHER_VALID, /* 'a' */ PATH_CHAR_CLASS_ANY, 4384 /* 'b' */ PATH_CHAR_CLASS_ANY, /* 'c' */ PATH_CHAR_CLASS_ANY, 4385 /* 'd' */ PATH_CHAR_CLASS_ANY, /* 'e' */ PATH_CHAR_CLASS_ANY, 4386 /* 'f' */ PATH_CHAR_CLASS_ANY, /* 'g' */ PATH_CHAR_CLASS_ANY, 4387 /* 'h' */ PATH_CHAR_CLASS_ANY, /* 'i' */ PATH_CHAR_CLASS_ANY, 4388 /* 'j' */ PATH_CHAR_CLASS_ANY, /* 'k' */ PATH_CHAR_CLASS_ANY, 4389 /* 'l' */ PATH_CHAR_CLASS_ANY, /* 'm' */ PATH_CHAR_CLASS_ANY, 4390 /* 'n' */ PATH_CHAR_CLASS_ANY, /* 'o' */ PATH_CHAR_CLASS_ANY, 4391 /* 'p' */ PATH_CHAR_CLASS_ANY, /* 'q' */ PATH_CHAR_CLASS_ANY, 4392 /* 'r' */ PATH_CHAR_CLASS_ANY, /* 's' */ PATH_CHAR_CLASS_ANY, 4393 /* 't' */ PATH_CHAR_CLASS_ANY, /* 'u' */ PATH_CHAR_CLASS_ANY, 4394 /* 'v' */ PATH_CHAR_CLASS_ANY, /* 'w' */ PATH_CHAR_CLASS_ANY, 4395 /* 'x' */ PATH_CHAR_CLASS_ANY, /* 'y' */ PATH_CHAR_CLASS_ANY, 4396 /* 'z' */ PATH_CHAR_CLASS_ANY, /* '{' */ PATH_CHAR_CLASS_OTHER_VALID, 4397 /* '|' */ PATH_CHAR_CLASS_INVALID, /* '}' */ PATH_CHAR_CLASS_OTHER_VALID, 4398 /* '~' */ PATH_CHAR_CLASS_OTHER_VALID 4399 }; 4400 4401 /************************************************************************* 4402 * @ [SHLWAPI.455] 4403 * 4404 * Check if an ASCII char is of a certain class 4405 */ 4406 BOOL WINAPI PathIsValidCharA( char c, DWORD class ) 4407 { 4408 if ((unsigned)c > 0x7e) 4409 return class & PATH_CHAR_CLASS_OTHER_VALID; 4410 4411 return class & SHELL_charclass[(unsigned)c]; 4412 } 4413 4414 /************************************************************************* 4415 * @ [SHLWAPI.456] 4416 * 4417 * Check if a Unicode char is of a certain class 4418 */ 4419 BOOL WINAPI PathIsValidCharW( WCHAR c, DWORD class ) 4420 { 4421 if (c > 0x7e) 4422 return class & PATH_CHAR_CLASS_OTHER_VALID; 4423 4424 return class & SHELL_charclass[c]; 4425 } 4426