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