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