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