Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1989 lines
60 KiB

  1. #include "shellprv.h"
  2. #pragma hdrstop
  3. // from mtpt.cpp
  4. STDAPI_(BOOL) CMtPt_IsLFN(int iDrive);
  5. STDAPI_(BOOL) CMtPt_IsSlow(int iDrive);
  6. __inline BOOL DBL_BSLASH(LPNCTSTR psz)
  7. {
  8. return (psz[0] == TEXT('\\') && psz[1] == TEXT('\\'));
  9. }
  10. #define IsPathSep(ch) ((ch) == TEXT('\\') || (ch) == TEXT('/'))
  11. // in:
  12. // pszPath fully qualified path (unc or x:\) to test
  13. // NULL for windows directory
  14. //
  15. // returns:
  16. // TRUE volume supports name longer than 12 chars
  17. //
  18. // note: this caches drive letters, but UNCs go through every time
  19. //
  20. STDAPI_(BOOL) IsLFNDrive(LPCTSTR pszPath)
  21. {
  22. TCHAR szRoot[MAX_PATH];
  23. DWORD dwMaxLength = 13; // assume yes
  24. ASSERT(NULL == pszPath || IS_VALID_STRING_PTR(pszPath, -1));
  25. if ((pszPath == NULL) || !*pszPath)
  26. {
  27. *szRoot = 0;
  28. GetWindowsDirectory(szRoot, ARRAYSIZE(szRoot));
  29. pszPath = szRoot;
  30. }
  31. ASSERT(!PathIsRelative(pszPath));
  32. //
  33. // UNC name? gota check each time
  34. //
  35. if (PathIsUNC(pszPath))
  36. {
  37. lstrcpyn(szRoot, pszPath, ARRAYSIZE(szRoot));
  38. PathStripToRoot(szRoot);
  39. // Deal with busted kernel UNC stuff
  40. // Is it a \\foo or a \\foo\bar thing?
  41. if (StrChr(szRoot+2, TEXT('\\')))
  42. {
  43. // "\\foo\bar - Append a slash to be NT compatible.
  44. lstrcat(szRoot, TEXT("\\"));
  45. }
  46. else
  47. {
  48. // "\\foo" - assume it's always a LFN volume
  49. return TRUE;
  50. }
  51. }
  52. //
  53. // removable media? gota check each time
  54. //
  55. else if (IsRemovableDrive(DRIVEID(pszPath)))
  56. {
  57. PathBuildRoot(szRoot, DRIVEID(pszPath));
  58. }
  59. //
  60. // fixed media use cached value.
  61. //
  62. else
  63. {
  64. return CMtPt_IsLFN(DRIVEID(pszPath));
  65. }
  66. //
  67. // Right now we will say that it is an LFN Drive if the maximum
  68. // component is > 12
  69. GetVolumeInformation(szRoot, NULL, 0, NULL, &dwMaxLength, NULL, NULL, 0);
  70. return dwMaxLength > 12;
  71. }
  72. STDAPI_(BOOL) IsLFNDriveA(LPCSTR pszPath) OPTIONAL
  73. {
  74. WCHAR wsz[MAX_PATH];
  75. ASSERT(NULL == pszPath || IS_VALID_STRING_PTRA(pszPath, -1));
  76. if (pszPath)
  77. {
  78. SHAnsiToUnicode(pszPath, wsz, ARRAYSIZE(wsz));
  79. pszPath = (LPCSTR)wsz;
  80. }
  81. return IsLFNDrive((LPCWSTR)pszPath);
  82. }
  83. STDAPI_(BOOL) PathIsRemovable(LPCTSTR pszPath)
  84. {
  85. BOOL fIsEjectable = FALSE;
  86. int iDrive = PathGetDriveNumber(pszPath);
  87. if (iDrive != -1)
  88. {
  89. int nType = DriveType(iDrive);
  90. if ((DRIVE_CDROM == nType) ||
  91. (DRIVE_DVD == nType) ||
  92. (DRIVE_REMOVABLE == nType))
  93. {
  94. fIsEjectable = TRUE;
  95. }
  96. }
  97. return fIsEjectable;
  98. }
  99. STDAPI_(BOOL) PathIsRemote(LPCTSTR pszPath)
  100. {
  101. BOOL fIsRemote = FALSE;
  102. if (PathIsUNC(pszPath))
  103. {
  104. fIsRemote = TRUE;
  105. }
  106. else
  107. {
  108. int iDrive = PathGetDriveNumber(pszPath);
  109. if (iDrive != -1)
  110. {
  111. int nType = DriveType(iDrive);
  112. if (DRIVE_REMOTE == nType || DRIVE_NO_ROOT_DIR == nType)
  113. {
  114. fIsRemote = TRUE;
  115. }
  116. }
  117. }
  118. return fIsRemote;
  119. }
  120. //----------------------------------------------------------------------------
  121. // The following are creterias we currently use to tell whether a file is a temporary file
  122. // Files with FILE_ATTRIBUTE_TEMPORARY set
  123. // Files in Windows temp directory
  124. // Files from the internet cache directory
  125. // Files in the CD burning area
  126. //---------------------------------------------------------------------------
  127. STDAPI_(BOOL) PathIsTemporary(LPCTSTR pszPath)
  128. {
  129. BOOL bRet = FALSE;
  130. DWORD dwAttrib = GetFileAttributes(pszPath);
  131. if ((-1 != dwAttrib) && (dwAttrib & FILE_ATTRIBUTE_TEMPORARY))
  132. {
  133. bRet = TRUE; // we got the attributes and the file says it is temprary
  134. }
  135. else
  136. {
  137. TCHAR szTemp[MAX_PATH];
  138. if (GetTempPath(ARRAYSIZE(szTemp), szTemp))
  139. {
  140. // if possible, expand the input to the long path name so we can compare strings
  141. TCHAR szPath[MAX_PATH];
  142. if (GetLongPathName(pszPath, szPath, ARRAYSIZE(szPath)))
  143. pszPath = szPath;
  144. // GetTempPath() returns short name due to compatibility constraints.
  145. // we need to convert to long name
  146. if (GetLongPathName(szTemp, szTemp, ARRAYSIZE(szTemp)))
  147. {
  148. bRet = PathIsEqualOrSubFolder(szTemp, pszPath) ||
  149. PathIsEqualOrSubFolder(MAKEINTRESOURCE(CSIDL_INTERNET_CACHE), pszPath) ||
  150. PathIsEqualOrSubFolder(MAKEINTRESOURCE(CSIDL_CDBURN_AREA), pszPath);
  151. }
  152. }
  153. }
  154. return bRet;
  155. }
  156. STDAPI_(BOOL) PathIsTemporaryA(LPCSTR pszPath)
  157. {
  158. TCHAR szPath[MAX_PATH];
  159. SHOtherToTChar(pszPath, szPath, ARRAYSIZE(szPath));
  160. return PathIsTemporary(szPath);
  161. }
  162. // unfortunately, this is exported so we need to support it
  163. STDAPI_(LPTSTR) PathGetExtension(LPCTSTR pszPath, LPTSTR pszExtension, int cchExt)
  164. {
  165. LPTSTR pszExt = PathFindExtension(pszPath);
  166. RIPMSG(FALSE, "PathGetExtension should not be called, use PathFindExtension instead");
  167. if (pszExt && *pszExt)
  168. pszExt += 1;
  169. return pszExt;
  170. }
  171. //
  172. // Attempts to truncate the filename pszSpec such that pszDir+pszSpec are less than MAX_PATH-5.
  173. // The extension is protected so it won't get truncated or altered.
  174. //
  175. // in:
  176. // pszDir the path to a directory. No trailing '\' is needed.
  177. // pszSpec the filespec to be truncated. This should not include a path but can have an extension.
  178. // This input buffer can be of any length.
  179. // iTruncLimit The minimum length to truncate pszSpec. If addition truncation would be required we fail.
  180. // out:
  181. // pszSpec The truncated filespec with it's extension unaltered.
  182. // return:
  183. // TRUE if the filename was truncated, FALSE if we were unable to truncate because the directory name
  184. // was too long, the extension was too long, or the iTruncLimit is too high. pszSpec is unaltered
  185. // when this function returns FALSE.
  186. //
  187. STDAPI_(BOOL) PathTruncateKeepExtension(LPCTSTR pszDir, LPTSTR pszSpec, int iTruncLimit)
  188. {
  189. LPTSTR pszExt = PathFindExtension(pszSpec);
  190. RIPMSG(pszDir && IS_VALID_STRING_PTR(pszDir, -1), "PathTruncateKeepExtension: Caller passed bad pszDir");
  191. RIPMSG(pszSpec && IS_VALID_STRING_PTR(pszSpec, -1) && IS_VALID_WRITE_BUFFER(pszSpec, TCHAR, MAX_PATH), "PathTruncateKeepExtension: Caller passed bad pszSpec");
  192. DEBUGWhackPathString(pszSpec, MAX_PATH);
  193. if (pszExt)
  194. {
  195. int cchExt = lstrlen(pszExt);
  196. int cchSpec = (int)(pszExt - pszSpec + cchExt);
  197. int cchKeep = MAX_PATH - lstrlen(pszDir) - 5; // the -5 is just to provide extra padding (max lstrlen(pszExt))
  198. // IF...
  199. // ...the filename is to long
  200. // ...we are within the limit to which we can truncate
  201. // ...the extension is short enough to allow the trunctation
  202. if ((cchSpec > cchKeep) && (cchKeep >= iTruncLimit) && (cchKeep > cchExt))
  203. {
  204. // THEN... go ahead and truncate
  205. StrCpy(pszSpec+cchKeep-cchExt, pszExt);
  206. return TRUE;
  207. }
  208. }
  209. return FALSE;
  210. }
  211. STDAPI_(int) PathCleanupSpec(LPCTSTR pszDir, LPTSTR pszSpec)
  212. {
  213. LPTSTR pszNext, pszCur;
  214. UINT uMatch = IsLFNDrive(pszDir) ? GCT_LFNCHAR : GCT_SHORTCHAR;
  215. int iRet = 0;
  216. LPTSTR pszPrevDot = NULL;
  217. for (pszCur = pszNext = pszSpec; *pszNext; /*pszNext = CharNext(pszNext)*/)
  218. {
  219. if (PathGetCharType(*pszNext) & uMatch)
  220. {
  221. *pszCur = *pszNext;
  222. if (uMatch == GCT_SHORTCHAR && *pszCur == TEXT('.'))
  223. {
  224. if (pszPrevDot) // Only one '.' allowed for short names
  225. {
  226. *pszPrevDot = TEXT('-');
  227. iRet |= PCS_REPLACEDCHAR;
  228. }
  229. pszPrevDot = pszCur;
  230. }
  231. if (IsDBCSLeadByte(*pszNext))
  232. {
  233. LPTSTR pszDBCSNext;
  234. pszDBCSNext = CharNext(pszNext);
  235. *(pszCur + 1) = *(pszNext + 1);
  236. pszNext = pszDBCSNext;
  237. }
  238. else
  239. pszNext = CharNext(pszNext);
  240. pszCur = CharNext(pszCur);
  241. }
  242. else
  243. {
  244. switch (*pszNext)
  245. {
  246. case TEXT('/'): // used often for things like add/remove
  247. case TEXT(' '): // blank (only replaced for short name drives)
  248. *pszCur = TEXT('-');
  249. pszCur = CharNext(pszCur);
  250. iRet |= PCS_REPLACEDCHAR;
  251. break;
  252. default:
  253. iRet |= PCS_REMOVEDCHAR;
  254. }
  255. pszNext = CharNext(pszNext);
  256. }
  257. }
  258. *pszCur = 0; // null terminate
  259. //
  260. // For short names, limit to 8.3
  261. //
  262. if (uMatch == GCT_SHORTCHAR)
  263. {
  264. int i = 8;
  265. for (pszCur = pszNext = pszSpec; *pszNext; pszNext = CharNext(pszNext))
  266. {
  267. if (*pszNext == TEXT('.'))
  268. {
  269. i = 4; // Copy "." + 3 more characters
  270. }
  271. if (i > 0)
  272. {
  273. *pszCur = *pszNext;
  274. pszCur = CharNext(pszCur);
  275. i--;
  276. }
  277. else
  278. {
  279. iRet |= PCS_TRUNCATED;
  280. }
  281. }
  282. *pszCur = 0;
  283. CharUpperNoDBCS(pszSpec);
  284. }
  285. else // Path too long only possible on LFN drives
  286. {
  287. if (pszDir && (lstrlen(pszDir) + lstrlen(pszSpec) > MAX_PATH - 1))
  288. {
  289. iRet |= PCS_PATHTOOLONG | PCS_FATAL;
  290. }
  291. }
  292. return iRet;
  293. }
  294. // PathCleanupSpecEx
  295. //
  296. // Just like PathCleanupSpec, PathCleanupSpecEx removes illegal characters from pszSpec
  297. // and enforces 8.3 format on non-LFN drives. In addition, this function will attempt to
  298. // truncate pszSpec if the combination of pszDir + pszSpec is greater than MAX_PATH.
  299. //
  300. // in:
  301. // pszDir The directory in which the filespec pszSpec will reside
  302. // pszSpec The filespec that is being cleaned up which includes any extension being used
  303. // out:
  304. // pszSpec The modified filespec with illegal characters removed, truncated to
  305. // 8.3 if pszDir is on a non-LFN drive, and truncated to a shorter number
  306. // of characters if pszDir is an LFN drive but pszDir + pszSpec is more
  307. // than MAX_PATH characters.
  308. // return:
  309. // returns a bit mask indicating what happened. This mask can include the following cases:
  310. // PCS_REPLACEDCHAR One or more illegal characters were replaced with legal characters
  311. // PCS_REMOVEDCHAR One or more illegal characters were removed
  312. // PCS_TRUNCATED Truncated to fit 8.3 format or because pszDir+pszSpec was too long
  313. // PCS_PATHTOOLONG pszDir is so long that we cannot truncate pszSpec to form a legal filename
  314. // PCS_FATAL The resultant pszDir+pszSpec is not a legal filename. Always used with PCS_PATHTOOLONG.
  315. //
  316. STDAPI_(int) PathCleanupSpecEx(LPCTSTR pszDir, LPTSTR pszSpec)
  317. {
  318. int iRet = PathCleanupSpec(pszDir, pszSpec);
  319. if (iRet & (PCS_PATHTOOLONG | PCS_FATAL))
  320. {
  321. // 30 is the shortest we want to truncate pszSpec to to satisfy the
  322. // pszDir+pszSpec<MAX_PATH requirement. If this amount of truncation isn't enough
  323. // then we go ahead and return PCS_PATHTOOLONG|PCS_FATAL without doing any further
  324. // truncation of pszSpec
  325. if (PathTruncateKeepExtension(pszDir, pszSpec, 30))
  326. {
  327. // We fixed the error returned by PathCleanupSpec so mask out the error.
  328. iRet |= PCS_TRUNCATED;
  329. iRet &= ~(PCS_PATHTOOLONG|PCS_FATAL);
  330. }
  331. }
  332. else
  333. {
  334. // ensure that if both of these aren't set then neither is set.
  335. ASSERT(!(iRet&PCS_PATHTOOLONG) && !(iRet&PCS_FATAL));
  336. }
  337. return iRet;
  338. }
  339. STDAPI_(BOOL) PathIsWild(LPCTSTR pszPath)
  340. {
  341. while (*pszPath)
  342. {
  343. if (*pszPath == TEXT('?') || *pszPath == TEXT('*'))
  344. return TRUE;
  345. pszPath = CharNext(pszPath);
  346. }
  347. return FALSE;
  348. }
  349. // given a path that potentially points to an un-extensioned program
  350. // file, check to see if a program file exists with that name.
  351. //
  352. // returns: TRUE if a program with that name is found.
  353. // (extension is added to name).
  354. // FALSE no program file found or the path did not have an extension
  355. //
  356. BOOL LookForExtensions(LPTSTR pszPath, LPCTSTR dirs[], BOOL bPathSearch, UINT fExt)
  357. {
  358. ASSERT(fExt); // should have some bits set
  359. if (*PathFindExtension(pszPath) == 0)
  360. {
  361. if (bPathSearch)
  362. {
  363. // NB Try every extension on each path component in turn to
  364. // mimic command.com's search order.
  365. return PathFindOnPathEx(pszPath, dirs, fExt);
  366. }
  367. else
  368. {
  369. return PathFileExistsDefExt(pszPath, fExt);
  370. }
  371. }
  372. return FALSE;
  373. }
  374. //
  375. // converts the relative or unqualified path name to the fully
  376. // qualified path name.
  377. //
  378. // If this path is a URL, this function leaves it alone and
  379. // returns FALSE.
  380. //
  381. // in:
  382. // pszPath path to convert
  383. // pszCurrentDir current directory to use
  384. //
  385. // PRF_TRYPROGRAMEXTENSIONS (implies PRF_VERIFYEXISTS)
  386. // PRF_VERIFYEXISTS
  387. //
  388. // returns:
  389. // TRUE the file was verified to exist
  390. // FALSE the file was not verified to exist (but it may)
  391. //
  392. STDAPI_(BOOL) PathResolve(LPTSTR lpszPath, LPCTSTR dirs[], UINT fFlags)
  393. {
  394. UINT fExt = (fFlags & PRF_DONTFINDLNK) ? (PFOPEX_COM | PFOPEX_BAT | PFOPEX_PIF | PFOPEX_EXE) : PFOPEX_DEFAULT;
  395. //
  396. // NOTE: if VERIFY SetLastError() default to FNF. - ZekeL 9-APR-98
  397. // ShellExec uses GLE() to find out why we failed.
  398. // any win32 API that we end up calling
  399. // will do a SLE() to overrider ours. specifically
  400. // if VERIFY is set we call GetFileAttributes()
  401. //
  402. if (fFlags & PRF_VERIFYEXISTS)
  403. SetLastError(ERROR_FILE_NOT_FOUND);
  404. PathUnquoteSpaces(lpszPath);
  405. if (PathIsRoot(lpszPath))
  406. {
  407. // No sense qualifying just a server or share name...
  408. if (!PathIsUNCServer(lpszPath) && !PathIsUNCServerShare(lpszPath))
  409. {
  410. // Be able to resolve "\" from different drives.
  411. if (lpszPath[0] == TEXT('\\') && lpszPath[1] == 0)
  412. {
  413. PathQualifyDef(lpszPath, fFlags & PRF_FIRSTDIRDEF ? dirs[0] : NULL, 0);
  414. }
  415. }
  416. if (fFlags & PRF_VERIFYEXISTS)
  417. {
  418. if (PathFileExistsAndAttributes(lpszPath, NULL))
  419. {
  420. return(TRUE);
  421. }
  422. #ifdef DEBUG
  423. // PathFileExistsAndAttributes() should catch this well enough.
  424. // If it is a UNC root, then we will see if the root exists
  425. //
  426. if (PathIsUNC(lpszPath))
  427. {
  428. // See if the network knows about this one.
  429. // It appears like some network provider croak if not everything
  430. // if filled in, so we might as well bloat ourself to make them happy...
  431. NETRESOURCE nr = {RESOURCE_GLOBALNET,RESOURCETYPE_ANY,
  432. RESOURCEDISPLAYTYPE_GENERIC, RESOURCEUSAGE_CONTAINER,
  433. NULL, lpszPath, NULL, NULL};
  434. HANDLE hEnum;
  435. if (WNetOpenEnum(RESOURCE_GLOBALNET, RESOURCETYPE_ANY,
  436. RESOURCEUSAGE_ALL, &nr, &hEnum) == WN_SUCCESS)
  437. {
  438. // If it succeeded then assume it worked...
  439. WNetCloseEnum(hEnum);
  440. ASSERT(FALSE);
  441. return(TRUE);
  442. }
  443. }
  444. #endif // DEBUG
  445. return FALSE;
  446. }
  447. return TRUE;
  448. }
  449. else if (PathIsFileSpec(lpszPath))
  450. {
  451. // REVIEW: look for programs before looking for paths
  452. if ((fFlags & PRF_TRYPROGRAMEXTENSIONS) && (LookForExtensions(lpszPath, dirs, TRUE, fExt)))
  453. return TRUE;
  454. if (PathFindOnPath(lpszPath, dirs))
  455. {
  456. // PathFindOnPath() returns TRUE iff PathFileExists(lpszPath),
  457. // so we always returns true here:
  458. //return (!(fFlags & PRF_VERIFYEXISTS)) || PathFileExists(lpszPath);
  459. return TRUE;
  460. }
  461. }
  462. else if (!PathIsURL(lpszPath))
  463. {
  464. // If there is a trailing '.', we should not try extensions
  465. PathQualifyDef(lpszPath, fFlags & PRF_FIRSTDIRDEF ? dirs[0] : NULL,
  466. PQD_NOSTRIPDOTS);
  467. if (fFlags & PRF_VERIFYEXISTS)
  468. {
  469. if ((fFlags & PRF_TRYPROGRAMEXTENSIONS) && (LookForExtensions(lpszPath, dirs, FALSE, fExt)))
  470. return TRUE;
  471. if (PathFileExistsAndAttributes(lpszPath, NULL))
  472. return TRUE;
  473. }
  474. else
  475. {
  476. return TRUE;
  477. }
  478. }
  479. return FALSE;
  480. }
  481. // qualify a DOS (or LFN) file name based on the currently active window.
  482. // this code is not careful to not write more than MAX_PATH characters
  483. // into psz
  484. //
  485. // in:
  486. // psz path to be qualified of at least MAX_PATH characters
  487. // ANSI string
  488. //
  489. // out:
  490. // psz fully qualified version of input string based
  491. // on the current active window (current directory)
  492. //
  493. void PathQualifyDef(LPTSTR psz, LPCTSTR szDefDir, DWORD dwFlags)
  494. {
  495. int cb, nSpaceLeft;
  496. TCHAR szTemp[MAX_PATH], szRoot[MAX_PATH];
  497. int iDrive;
  498. LPTSTR pOrig, pFileName;
  499. BOOL fLFN;
  500. LPTSTR pExt;
  501. RIPMSG(psz && IS_VALID_STRING_PTR(psz, -1) && IS_VALID_WRITE_BUFFER(psz, TCHAR, MAX_PATH), "PathQualifyDef: caller passed bad psz");
  502. RIPMSG(!szDefDir || (IS_VALID_STRING_PTR(szDefDir, -1) && lstrlen(szDefDir)<MAX_PATH), "PathQualifyDef: caller passed bad szDefDir");
  503. DEBUGWhackPathString(psz, MAX_PATH);
  504. /* Save it away. */
  505. lstrcpyn(szTemp, psz, ARRAYSIZE(szTemp));
  506. FixSlashesAndColon(szTemp);
  507. nSpaceLeft = ARRAYSIZE(szTemp);
  508. pOrig = szTemp;
  509. pFileName = PathFindFileName(szTemp);
  510. if (PathIsUNC(pOrig))
  511. {
  512. // leave the \\ in thebuffer so that the various parts
  513. // of the UNC path will be qualified and appended. Note
  514. // we must assume that UNCs are LFN's, since computernames
  515. // and sharenames can be longer than 11 characters.
  516. fLFN = IsLFNDrive(pOrig);
  517. if (fLFN)
  518. {
  519. psz[2] = 0;
  520. nSpaceLeft -= 3;
  521. pOrig += 2;
  522. goto GetComps;
  523. }
  524. else
  525. {
  526. // NB UNC doesn't support LFN's but we don't want to truncate
  527. // \\foo or \\foo\bar so skip them here.
  528. // Is it a \\foo\bar\fred thing?
  529. LPTSTR pszSlash = StrChr(psz+2, TEXT('\\'));
  530. if (pszSlash && (NULL != (pszSlash = StrChr(pszSlash+1, TEXT('\\')))))
  531. {
  532. // Yep - skip the first bits but mush the rest.
  533. *(pszSlash+1) = 0;
  534. nSpaceLeft -= (int)(pszSlash-psz)+1;
  535. pOrig += pszSlash-psz;
  536. goto GetComps;
  537. }
  538. else
  539. {
  540. // Nope - just pretend it's an LFN and leave it alone.
  541. fLFN = TRUE;
  542. psz[2] = 0;
  543. nSpaceLeft -= 2;
  544. pOrig+=2;
  545. goto GetComps;
  546. }
  547. }
  548. }
  549. iDrive = PathGetDriveNumber(pOrig);
  550. if (iDrive != -1)
  551. {
  552. PathBuildRoot(szRoot, iDrive); // root specified by the file name
  553. ASSERT(pOrig[1] == TEXT(':')); // PathGetDriveNumber does this
  554. pOrig += 2; // Skip over the drive letter
  555. // and the slash if it is there...
  556. if (pOrig[0] == TEXT('\\'))
  557. pOrig++;
  558. }
  559. else
  560. {
  561. if (szDefDir)
  562. lstrcpyn(szRoot, szDefDir, ARRAYSIZE(szRoot));
  563. else
  564. {
  565. //
  566. // As a default, use the windows drive (usually "C:\").
  567. //
  568. *szRoot = 0;
  569. GetWindowsDirectory(szRoot, ARRAYSIZE(szRoot));
  570. iDrive = PathGetDriveNumber(szRoot);
  571. if (iDrive != -1)
  572. {
  573. PathBuildRoot(szRoot, iDrive);
  574. }
  575. }
  576. // if path is scopped to the root with "\" use working dir root
  577. if (pOrig[0] == TEXT('\\'))
  578. PathStripToRoot(szRoot);
  579. }
  580. fLFN = IsLFNDrive(szRoot);
  581. // REVIEW, do we really need to do different stuff on LFN names here?
  582. // on FAT devices, replace any illegal chars with underscores
  583. if (!fLFN)
  584. {
  585. LPTSTR pT;
  586. for (pT = pOrig; *pT; pT = CharNext(pT))
  587. {
  588. if (!PathIsValidChar(*pT, PIVC_SFN_FULLPATH))
  589. {
  590. // not a valid sfn path character
  591. *pT = TEXT('_');
  592. }
  593. }
  594. }
  595. lstrcpy(psz, szRoot);
  596. nSpaceLeft -= (lstrlen(psz) + 1);
  597. GetComps:
  598. while (*pOrig && nSpaceLeft > 0)
  599. {
  600. // If the component 0is parent dir, go up one dir.
  601. // If its the current dir, skip it, else add it normally
  602. if (pOrig[0] == TEXT('.'))
  603. {
  604. if (pOrig[1] == TEXT('.') && (!pOrig[2] || pOrig[2] == TEXT('\\')))
  605. PathRemoveFileSpec(psz);
  606. else if (pOrig[1] && pOrig[1] != TEXT('\\'))
  607. goto addcomponent;
  608. while (*pOrig && *pOrig != TEXT('\\'))
  609. pOrig = CharNext(pOrig);
  610. if (*pOrig)
  611. pOrig++;
  612. }
  613. else
  614. {
  615. LPTSTR pT, pTT = NULL;
  616. addcomponent:
  617. PathAddBackslash(psz);
  618. nSpaceLeft--;
  619. pT = psz + lstrlen(psz);
  620. if (fLFN)
  621. {
  622. // copy the component
  623. while (*pOrig && *pOrig != TEXT('\\') && nSpaceLeft>0)
  624. {
  625. nSpaceLeft--;
  626. if (IsDBCSLeadByte(*pOrig))
  627. {
  628. if (nSpaceLeft <= 0)
  629. {
  630. // Copy nothing more
  631. continue;
  632. }
  633. nSpaceLeft--;
  634. *pT++ = *pOrig++;
  635. }
  636. *pT++ = *pOrig++;
  637. }
  638. }
  639. else
  640. {
  641. // copy the filename (up to 8 chars)
  642. for (cb = 8; *pOrig && !IsPathSep(*pOrig) && *pOrig != TEXT('.') && nSpaceLeft > 0;)
  643. {
  644. if (cb > 0)
  645. {
  646. cb--;
  647. nSpaceLeft--;
  648. if (IsDBCSLeadByte(*pOrig))
  649. {
  650. if (nSpaceLeft<=0 || cb<=0)
  651. {
  652. // Copy nothing more
  653. cb = 0;
  654. continue;
  655. }
  656. cb--;
  657. nSpaceLeft--;
  658. *pT++ = *pOrig++;
  659. }
  660. *pT++ = *pOrig++;
  661. }
  662. else
  663. {
  664. pOrig = CharNext(pOrig);
  665. }
  666. }
  667. // if there's an extension, copy it, up to 3 chars
  668. if (*pOrig == TEXT('.') && nSpaceLeft > 0)
  669. {
  670. int nOldSpaceLeft;
  671. *pT++ = TEXT('.');
  672. nSpaceLeft--;
  673. pOrig++;
  674. pExt = pT;
  675. nOldSpaceLeft = nSpaceLeft;
  676. for (cb = 3; *pOrig && *pOrig != TEXT('\\') && nSpaceLeft > 0;)
  677. {
  678. if (*pOrig == TEXT('.'))
  679. {
  680. // Another extension, start again.
  681. cb = 3;
  682. pT = pExt;
  683. nSpaceLeft = nOldSpaceLeft;
  684. pOrig++;
  685. }
  686. if (cb > 0)
  687. {
  688. cb--;
  689. nSpaceLeft--;
  690. if (IsDBCSLeadByte(*pOrig))
  691. {
  692. if (nSpaceLeft<=0 || cb<=0)
  693. {
  694. // Copy nothing more
  695. cb = 0;
  696. continue;
  697. }
  698. cb--;
  699. nSpaceLeft--;
  700. *pT++ = *pOrig++;
  701. }
  702. *pT++ = *pOrig++;
  703. }
  704. else
  705. {
  706. pOrig = CharNext(pOrig);
  707. }
  708. }
  709. }
  710. }
  711. // skip the backslash
  712. if (*pOrig)
  713. pOrig++;
  714. // null terminate for next pass...
  715. *pT = 0;
  716. }
  717. }
  718. PathRemoveBackslash(psz);
  719. if (!(dwFlags & PQD_NOSTRIPDOTS))
  720. {
  721. // remove any trailing dots
  722. LPTSTR pszPrev = CharPrev(psz, psz + lstrlen(psz));
  723. if (*pszPrev == TEXT('.'))
  724. {
  725. *pszPrev = 0;
  726. }
  727. }
  728. }
  729. STDAPI_(void) PathQualify(LPTSTR psz)
  730. {
  731. PathQualifyDef(psz, NULL, 0);
  732. }
  733. BOOL OnExtList(LPCTSTR pszExtList, LPCTSTR pszExt)
  734. {
  735. for (; *pszExtList; pszExtList += lstrlen(pszExtList) + 1)
  736. {
  737. if (!lstrcmpi(pszExt, pszExtList))
  738. {
  739. // yes
  740. return TRUE;
  741. }
  742. }
  743. return FALSE;
  744. }
  745. // Character offset where binary exe extensions begin in above
  746. #define BINARY_EXE_OFFSET 20
  747. const TCHAR c_achExes[] = TEXT(".cmd\0.bat\0.pif\0.scf\0.exe\0.com\0.scr\0");
  748. STDAPI_(BOOL) PathIsBinaryExe(LPCTSTR szFile)
  749. {
  750. ASSERT(BINARY_EXE_OFFSET < ARRAYSIZE(c_achExes) &&
  751. c_achExes[BINARY_EXE_OFFSET] == TEXT('.'));
  752. return OnExtList(c_achExes + BINARY_EXE_OFFSET, PathFindExtension(szFile));
  753. }
  754. //
  755. // determine if a path is a program by looking at the extension
  756. //
  757. STDAPI_(BOOL) PathIsExe(LPCTSTR szFile)
  758. {
  759. LPCTSTR temp = PathFindExtension(szFile);
  760. return OnExtList(c_achExes, temp);
  761. }
  762. //
  763. // determine if a path is a .lnk file by looking at the extension
  764. //
  765. STDAPI_(BOOL) PathIsLnk(LPCTSTR szFile)
  766. {
  767. if (szFile)
  768. {
  769. // Both PathFindExtension() and lstrcmpi() will crash
  770. // if passed NULL. PathFindExtension() will never return
  771. // NULL.
  772. LPCTSTR lpszFileName = PathFindExtension(szFile);
  773. return lstrcmpi(TEXT(".lnk"), lpszFileName) == 0;
  774. }
  775. else
  776. {
  777. return FALSE;
  778. }
  779. }
  780. // Port names are invalid path names
  781. #define IsDigit(c) ((c) >= TEXT('0') && c <= TEXT('9'))
  782. STDAPI_(BOOL) PathIsInvalid(LPCWSTR pszName)
  783. {
  784. static const TCHAR *rgszPorts3[] = {
  785. TEXT("NUL"),
  786. TEXT("PRN"),
  787. TEXT("CON"),
  788. TEXT("AUX"),
  789. };
  790. static const TCHAR *rgszPorts4[] = {
  791. TEXT("LPT"), // LPT#
  792. TEXT("COM"), // COM#
  793. };
  794. TCHAR sz[7];
  795. DWORD cch;
  796. int iMax;
  797. LPCTSTR* rgszPorts;
  798. StrCpyN(sz, pszName, ARRAYSIZE(sz));
  799. PathRemoveExtension(sz);
  800. cch = lstrlen(sz);
  801. iMax = ARRAYSIZE(rgszPorts3);
  802. rgszPorts = rgszPorts3;
  803. if (cch == 4 && IsDigit(sz[3]))
  804. {
  805. // if 4 chars start with LPT checks
  806. // need to filter out:
  807. // COM1, COM2, etc. LPT1, LPT2, etc
  808. // but not:
  809. // COM or LPT or LPT10 or COM10
  810. // COM == 1 and LPT == 0
  811. iMax = ARRAYSIZE(rgszPorts4);
  812. rgszPorts = rgszPorts4;
  813. sz[3] = 0;
  814. cch = 3;
  815. }
  816. if (cch == 3)
  817. {
  818. int i;
  819. for (i = 0; i < iMax; i++)
  820. {
  821. if (!lstrcmpi(rgszPorts[i], sz))
  822. {
  823. break;
  824. }
  825. }
  826. return (i == iMax) ? FALSE : TRUE;
  827. }
  828. return FALSE;
  829. }
  830. //
  831. // Funciton: PathMakeUniqueName
  832. //
  833. // Parameters:
  834. // pszUniqueName -- Specify the buffer where the unique name should be copied
  835. // cchMax -- Specify the size of the buffer
  836. // pszTemplate -- Specify the base name
  837. // pszLongPlate -- Specify the base name for a LFN drive. format below
  838. // pszDir -- Specify the directory (at most MAX_PATH in length)
  839. //
  840. // History:
  841. // 03-11-93 SatoNa Created
  842. //
  843. // REVIEW:
  844. // For long names, we should be able to generate more user friendly name
  845. // such as "Copy of MyDocument" of "Link #2 to MyDocument". In this case,
  846. // we need additional flags which indicates if it is copy, or link.
  847. //
  848. // Format:
  849. // pszLongPlate will search for the first (and then finds the matching)
  850. // to look for a number:
  851. // given: Copy () of my doc gives: Copy (_number_) of my doc
  852. // given: Copy (1023) of my doc gives: Copy (_number_) of my doc
  853. //
  854. // PERF: if making n unique names, the time grows n^2 because it always
  855. // starts from 0 and checks for existing file.
  856. //
  857. STDAPI_(BOOL) PathMakeUniqueNameEx(LPTSTR pszUniqueName, UINT cchMax,
  858. LPCTSTR pszTemplate, LPCTSTR pszLongPlate, LPCTSTR pszDir, int iMinLong)
  859. {
  860. TCHAR szFormat[MAX_PATH]; // should be plenty big
  861. LPTSTR pszName, pszDigit;
  862. LPCTSTR pszStem;
  863. int cchStem, cchDir;
  864. int iMax, iMin, i;
  865. int cchMaxName;
  866. RIPMSG(pszUniqueName && IS_VALID_WRITE_BUFFER(pszUniqueName, TCHAR, cchMax), "PathMakeUniqueNameEx: caller passed bad pszUniqueName");
  867. DEBUGWhackPathBuffer(pszUniqueName, cchMax);
  868. RIPMSG(!pszDir || lstrlen(pszDir)<MAX_PATH, "PathMakeUniqueNameEx: pszDir exceeds MAX_PATH, helper routines don't take cch so this call is broken");
  869. RIPMSG(iMinLong >= 0, "PathMakeUniqueNameEx: negative iMinLong doesn't make sense");
  870. if (0==cchMax || !pszUniqueName)
  871. return FALSE;
  872. *pszUniqueName = 0; // just in case of failure
  873. if (pszLongPlate == NULL)
  874. pszLongPlate = pszTemplate;
  875. // all cases below check the length of optional pszDir, calculate early.
  876. // side effect: this set's up pszName and the directory portion of pszUniqueName;
  877. if (pszDir)
  878. {
  879. cchDir = lstrlen(pszDir);
  880. if ((UINT)cchDir >= cchMax-2) // -2 to allow for NULL and '\' of PathAddBackslash
  881. return FALSE;
  882. lstrcpy(pszUniqueName, pszDir);
  883. pszName = PathAddBackslash(pszUniqueName);
  884. cchDir = lstrlen(pszDir); // we need an accurate count
  885. }
  886. else
  887. {
  888. cchDir = 0;
  889. pszName = pszUniqueName;
  890. }
  891. // Set up:
  892. // pszStem : template we're going to use
  893. // cchStem : length of pszStem we're going to use w/o wsprintf
  894. // szFormat : format string to wsprintf the number with, catenates on to pszStem[0..cchStem]
  895. // iMin : starting number for wsprintf loop
  896. // iMax : maximum number for wsprintf loop
  897. // cchMaxname : !0 implies -> if resulting name length > cchMaxname, then --cchStem (only used in short name case)
  898. //
  899. if (pszLongPlate && IsLFNDrive(pszDir))
  900. {
  901. LPCTSTR pszRest;
  902. int cchTmp;
  903. cchMaxName = 0;
  904. // for long name drives
  905. pszStem = pszLongPlate;
  906. // Has this already been a uniquified name?
  907. pszRest = StrChr(pszLongPlate, TEXT('('));
  908. while (pszRest)
  909. {
  910. // First validate that this is the right one
  911. LPCTSTR pszEndUniq = CharNext(pszRest);
  912. while (*pszEndUniq && *pszEndUniq >= TEXT('0') && *pszEndUniq <= TEXT('9')) {
  913. pszEndUniq++;
  914. }
  915. if (*pszEndUniq == TEXT(')'))
  916. break; // We have the right one!
  917. pszRest = StrChr(CharNext(pszRest), TEXT('('));
  918. }
  919. if (!pszRest)
  920. {
  921. // Never been unique'd before -- tack it on at the end. (but before the extension)
  922. // eg. New Link yields New Link (1)
  923. pszRest = PathFindExtension(pszLongPlate);
  924. cchStem = (int)(pszRest - pszLongPlate);
  925. wnsprintf(szFormat, ARRAYSIZE(szFormat), TEXT(" (%%d)%s"), pszRest ? pszRest : c_szNULL);
  926. }
  927. else
  928. {
  929. // we found (#), so remove the #
  930. // eg. New Link (999) yields New Link (1)
  931. pszRest++; // step over the '('
  932. cchStem = (int) (pszRest - pszLongPlate);
  933. // eat the '#'
  934. while (*pszRest && *pszRest >= TEXT('0') && *pszRest <= TEXT('9')) {
  935. pszRest++;
  936. }
  937. // we are guaranteed enough room because we don't include
  938. // the stuff before the # in this format
  939. wnsprintf(szFormat, ARRAYSIZE(szFormat), TEXT("%%d%s"), pszRest);
  940. }
  941. // how much room do we have to play?
  942. iMin = iMinLong;
  943. cchTmp = cchMax - cchDir - cchStem - (lstrlen(szFormat)-2); // -2 for "%d" which will be replaced
  944. switch(cchTmp)
  945. {
  946. case 1:
  947. iMax = 10;
  948. break;
  949. case 2:
  950. iMax = 100;
  951. break;
  952. default:
  953. if (cchTmp <= 0)
  954. iMax = iMin; // no room, bail
  955. else
  956. iMax = 1000;
  957. break;
  958. }
  959. }
  960. else // short filename case
  961. {
  962. LPCTSTR pszRest;
  963. int cchRest;
  964. int cchFormat;
  965. if (pszTemplate == NULL)
  966. return FALSE;
  967. // for short name drives
  968. pszStem = pszTemplate;
  969. pszRest = PathFindExtension(pszTemplate);
  970. // Calculate cchMaxName, ensuring our base name (cchStem+digits) will never go over 8
  971. //
  972. cchRest=lstrlen(pszRest);
  973. cchMaxName = 8+cchRest;
  974. // Now that we have the extension, we know the format string
  975. //
  976. wnsprintf(szFormat, ARRAYSIZE(szFormat), TEXT("%%d%s"), pszRest);
  977. ASSERT(lstrlen(szFormat)-2 == cchRest); // -2 for "%d" in format string
  978. cchFormat = cchRest;
  979. // Figure out how long the stem really is:
  980. //
  981. cchStem = (int)(pszRest-pszTemplate); // 8 for "fooobarr.foo"
  982. // Remove all the digit characters (previous uniquifying) from the stem
  983. //
  984. for(; cchStem > 1 ; cchStem--)
  985. {
  986. TCHAR ch;
  987. LPCTSTR pszPrev = CharPrev(pszTemplate, pszTemplate + cchStem);
  988. // Don't remove if it is a DBCS character
  989. if (pszPrev != pszTemplate+cchStem-1)
  990. break;
  991. // Don't remove it it is not a digit
  992. ch=pszPrev[0];
  993. if (ch<TEXT('0') || ch>TEXT('9'))
  994. break;
  995. }
  996. // Short file names mean we use the 8.3 rule, so the stem can't be > 8...
  997. //
  998. if ((UINT)cchStem > 8-1)
  999. cchStem = 8-1; // need 1 for a digit
  1000. // Truncate the stem to make it fit when we take the directory path into consideration
  1001. //
  1002. while ((cchStem + cchFormat + cchDir + 1 > (int)cchMax - 1) && (cchStem > 1)) // -1 for NULL, +1 for a digit
  1003. cchStem--;
  1004. // We've allowed for 1 character of digit space, but...
  1005. // How many digits can we really use?
  1006. //
  1007. iMin = 1;
  1008. if (cchStem < 1)
  1009. iMax = iMin; // NONE!
  1010. else if (1 == cchStem)
  1011. iMax = 10; // There's only 1 character of stem left, so use digits 0-9
  1012. else
  1013. iMax = 100; // Room for stem and digits 0-99
  1014. }
  1015. // pszUniqueName has the optional directory in it,
  1016. // pszName points into pszUniqueName where the stem goes,
  1017. // now try to find a unique name!
  1018. //
  1019. lstrcpyn(pszName, pszStem, cchStem+1);
  1020. pszDigit = pszName + cchStem;
  1021. for (i = iMin; i < iMax ; i++)
  1022. {
  1023. wsprintf(pszDigit, szFormat, i);
  1024. //
  1025. // if we have a limit on the length of the name (ie on a non-LFN drive)
  1026. // backup the pszDigit pointer when i wraps from 9to10 and 99to100 etc
  1027. //
  1028. if (cchMaxName && lstrlen(pszName) > cchMaxName)
  1029. {
  1030. pszDigit = CharPrev(pszName, pszDigit);
  1031. wsprintf(pszDigit, szFormat, i);
  1032. }
  1033. TraceMsg(TF_PATH, "PathMakeUniqueNameEx: trying %s", (LPCTSTR)pszUniqueName);
  1034. //
  1035. // Check if this name is unique or not.
  1036. //
  1037. if (!PathFileExists(pszUniqueName))
  1038. {
  1039. return TRUE;
  1040. }
  1041. }
  1042. *pszUniqueName = 0; // we failed, clear out our last attempt
  1043. return FALSE;
  1044. }
  1045. STDAPI_(BOOL) PathMakeUniqueName(LPTSTR pszUniqueName, UINT cchMax,
  1046. LPCTSTR pszTemplate, LPCTSTR pszLongPlate, LPCTSTR pszDir)
  1047. {
  1048. return PathMakeUniqueNameEx(pszUniqueName, cchMax, pszTemplate, pszLongPlate, pszDir, 1);
  1049. }
  1050. // in:
  1051. // pszPath directory to do this into or full dest path
  1052. // if pszShort is NULL
  1053. // pszShort file name (short version) if NULL assumes
  1054. // pszPath is both path and spec
  1055. // pszFileSpec file name (long version)
  1056. //
  1057. // out:
  1058. // pszUniqueName
  1059. //
  1060. // note:
  1061. // pszUniqueName can be the same buffer as pszPath or pszShort or pszFileSpec
  1062. //
  1063. // returns:
  1064. // TRUE success, name can be used
  1065. STDAPI_(BOOL) PathYetAnotherMakeUniqueName(LPTSTR pszUniqueName, LPCTSTR pszPath, LPCTSTR pszShort, LPCTSTR pszFileSpec)
  1066. {
  1067. BOOL fRet = FALSE;
  1068. TCHAR szTemp[MAX_PATH];
  1069. TCHAR szPath[MAX_PATH];
  1070. RIPMSG(pszPath && IS_VALID_STRING_PTR(pszPath, -1) && lstrlen(pszPath)<MAX_PATH-1, "PathYetAnotherMakeUniqueName: caller passed invalid pszPath");
  1071. RIPMSG(!pszShort || IS_VALID_STRING_PTR(pszShort, -1), "PathYetAnotherMakeUniqueName: caller passed invalid pszShort");
  1072. RIPMSG(!pszFileSpec || (IS_VALID_STRING_PTR(pszFileSpec, -1) && lstrlen(pszPath)+lstrlen(pszFileSpec)<MAX_PATH-2), "PathYetAnotherMakeUniqueName: caller passed invalid pszFileSpec");
  1073. RIPMSG(pszUniqueName && IS_VALID_WRITE_BUFFER(pszUniqueName, TCHAR, MAX_PATH), "PathYetAnotherMakeUniqueName: caller passed invalid pszUniqueName");
  1074. #ifdef DEBUG
  1075. if (pszUniqueName == pszPath || pszUniqueName == pszShort || pszUniqueName == pszFileSpec)
  1076. DEBUGWhackPathString(pszUniqueName, MAX_PATH);
  1077. else
  1078. DEBUGWhackPathBuffer(pszUniqueName, MAX_PATH);
  1079. #endif
  1080. if (pszShort == NULL)
  1081. {
  1082. pszShort = PathFindFileName(pszPath);
  1083. lstrcpy(szPath, pszPath);
  1084. PathRemoveFileSpec(szPath);
  1085. pszPath = szPath;
  1086. }
  1087. if (pszFileSpec == NULL)
  1088. {
  1089. pszFileSpec = pszShort;
  1090. }
  1091. if (IsLFNDrive(pszPath))
  1092. {
  1093. LPTSTR lpsz;
  1094. LPTSTR lpszNew;
  1095. // REVIEW: If the path+filename is too long how about this, instead of bailing out we trunctate the name
  1096. // using my new PathTruncateKeepExtension? Currently we have many places where the return result of this
  1097. // function is not checked which cause failures in abserdly long filename cases. The result ends up having
  1098. // the wrong path which screws things up.
  1099. if ((lstrlen(pszPath) + lstrlen(pszFileSpec) + 5) > MAX_PATH)
  1100. return FALSE;
  1101. // try it without the (if there's a space after it
  1102. lpsz = StrChr(pszFileSpec, TEXT('('));
  1103. while (lpsz)
  1104. {
  1105. if (*(CharNext(lpsz)) == TEXT(')'))
  1106. break;
  1107. lpsz = StrChr(CharNext(lpsz), TEXT('('));
  1108. }
  1109. if (lpsz)
  1110. {
  1111. // We have the (). See if we have either x () y or x ().y in which case
  1112. // we probably want to get rid of one of the blanks...
  1113. int ichSkip = 2;
  1114. LPTSTR lpszT = CharPrev(pszFileSpec, lpsz);
  1115. if (*lpszT == TEXT(' '))
  1116. {
  1117. ichSkip = 3;
  1118. lpsz = lpszT;
  1119. }
  1120. lstrcpy(szTemp, pszPath);
  1121. lpszNew = PathAddBackslash(szTemp);
  1122. lstrcpy(lpszNew, pszFileSpec);
  1123. lpszNew += (lpsz - pszFileSpec);
  1124. lstrcpy(lpszNew, lpsz + ichSkip);
  1125. fRet = !PathFileExists(szTemp);
  1126. }
  1127. else
  1128. {
  1129. // 1taro registers its document with '/'.
  1130. if (lpsz = StrChr(pszFileSpec, '/'))
  1131. {
  1132. LPTSTR lpszT = CharNext(lpsz);
  1133. lstrcpy(szTemp, pszPath);
  1134. lpszNew = PathAddBackslash(szTemp);
  1135. lstrcpy(lpszNew, pszFileSpec);
  1136. lpszNew += (lpsz - pszFileSpec);
  1137. lstrcpy(lpszNew, lpszT);
  1138. }
  1139. else
  1140. {
  1141. PathCombine(szTemp, pszPath, pszFileSpec);
  1142. }
  1143. fRet = !PathFileExists(szTemp);
  1144. }
  1145. }
  1146. else
  1147. {
  1148. ASSERT(lstrlen(PathFindExtension(pszShort)) <= 4);
  1149. lstrcpy(szTemp,pszShort);
  1150. PathRemoveExtension(szTemp);
  1151. if (lstrlen(szTemp) <= 8)
  1152. {
  1153. PathCombine(szTemp, pszPath, pszShort);
  1154. fRet = !PathFileExists(szTemp);
  1155. }
  1156. }
  1157. if (!fRet)
  1158. {
  1159. fRet = PathMakeUniqueNameEx(szTemp, ARRAYSIZE(szTemp), pszShort, pszFileSpec, pszPath, 2);
  1160. PathCombine(szTemp, pszPath, szTemp);
  1161. }
  1162. if (fRet)
  1163. lstrcpy(pszUniqueName, szTemp);
  1164. return fRet;
  1165. }
  1166. STDAPI_(void) PathGetShortPath(LPTSTR pszLongPath)
  1167. {
  1168. TCHAR szShortPath[MAX_PATH];
  1169. RIPMSG(pszLongPath && IS_VALID_STRING_PTR(pszLongPath, -1) && IS_VALID_WRITE_BUFFER(pszLongPath, TCHAR, MAX_PATH), "PathGetShortPath: caller passed invalid pszLongPath");
  1170. DEBUGWhackPathString(pszLongPath, MAX_PATH);
  1171. if (GetShortPathName(pszLongPath, szShortPath, ARRAYSIZE(szShortPath)))
  1172. lstrcpy(pszLongPath, szShortPath);
  1173. }
  1174. //
  1175. // pszFile -- file path
  1176. // dwFileAttr -- The file attributes, pass -1 if not available
  1177. //
  1178. // Note: pszFile arg may be NULL if dwFileAttr != -1.
  1179. BOOL PathIsHighLatency(LPCTSTR pszFile /*optional*/, DWORD dwFileAttr)
  1180. {
  1181. BOOL bRet = FALSE;
  1182. if (dwFileAttr == -1)
  1183. {
  1184. ASSERT(pszFile != NULL) ;
  1185. dwFileAttr = pszFile ? GetFileAttributes(pszFile) : -1;
  1186. }
  1187. if ((dwFileAttr != -1) && (dwFileAttr & FILE_ATTRIBUTE_OFFLINE))
  1188. {
  1189. bRet = TRUE;
  1190. }
  1191. return bRet;
  1192. }
  1193. //
  1194. // is a path slow or not
  1195. // dwFileAttr -- The file attributes, pass -1 if not available
  1196. //
  1197. STDAPI_(BOOL) PathIsSlow(LPCTSTR pszFile, DWORD dwFileAttr)
  1198. {
  1199. BOOL bSlow = FALSE;
  1200. if (PathIsUNC(pszFile))
  1201. {
  1202. DWORD speed = GetPathSpeed(pszFile);
  1203. bSlow = (speed != 0) && (speed <= SPEED_SLOW);
  1204. }
  1205. else if (CMtPt_IsSlow(PathGetDriveNumber(pszFile)))
  1206. bSlow = TRUE;
  1207. if (!bSlow)
  1208. bSlow = PathIsHighLatency(pszFile, dwFileAttr);
  1209. return bSlow;
  1210. }
  1211. STDAPI_(BOOL) PathIsSlowA(LPCSTR pszFile, DWORD dwFileAttr)
  1212. {
  1213. WCHAR szBuffer[MAX_PATH];
  1214. SHAnsiToUnicode(pszFile, szBuffer, ARRAYSIZE(szBuffer));
  1215. return PathIsSlowW(szBuffer, dwFileAttr);
  1216. }
  1217. /*----------------------------------------------------------------------------
  1218. / Purpose:
  1219. / Process the specified command line and generate a suitably quoted
  1220. / name, with arguments attached if required.
  1221. /
  1222. / Notes:
  1223. / - The destination buffer size can be determined if NULL is passed as a
  1224. / destination pointer.
  1225. / - If the source string is quoted then we assume that it exists on the
  1226. / filing system.
  1227. /
  1228. / In:
  1229. / lpSrc -> null terminate source path
  1230. / lpDest -> destination buffer / = NULL to return buffer size
  1231. / iMax = maximum number of characters to return into destination
  1232. / dwFlags =
  1233. / PPCF_ADDQUOTES = 1 => if path requires quotes then add them
  1234. / PPCF_ADDARGUMENTS = 1 => append trailing arguments to resulting string (forces ADDQUOTES)
  1235. / PPCF_NODIRECTORIES = 1 => don't match against directories, only file objects
  1236. / PPCF_NORELATIVEOBJECTQUALIFY = 1 => locate relative objects and return the full qualified path
  1237. / PPCF_LONGESTPOSSIBLE = 1 => always choose the longest possible executable name ex: d:\program files\fun.exe vs. d:\program.exe
  1238. / Out:
  1239. / > 0 if the call works
  1240. / < 0 if the call fails (object not found, buffer too small for resulting string)
  1241. /----------------------------------------------------------------------------*/
  1242. STDAPI_(LONG) PathProcessCommand(LPCTSTR lpSrc, LPTSTR lpDest, int iDestMax, DWORD dwFlags)
  1243. {
  1244. TCHAR szName[MAX_PATH];
  1245. TCHAR szLastChoice[MAX_PATH];
  1246. LPTSTR lpBuffer, lpBuffer2;
  1247. LPCTSTR lpArgs = NULL;
  1248. DWORD dwAttrib;
  1249. LONG i, iTotal;
  1250. LONG iResult = -1;
  1251. BOOL bAddQuotes = FALSE;
  1252. BOOL bQualify = FALSE;
  1253. BOOL bFound = FALSE;
  1254. BOOL bHitSpace = FALSE;
  1255. BOOL bRelative = FALSE;
  1256. LONG iLastChoice = 0;
  1257. RIPMSG(lpSrc && IS_VALID_STRING_PTR(lpSrc, -1), "PathProcessCommand: caller passed invalid lpSrc");
  1258. RIPMSG(!lpDest || (iDestMax > 0 && IS_VALID_WRITE_BUFFER(lpDest, TCHAR, iDestMax)), "PathProcessCommand: caller passed invalid lpDest,iDestMax");
  1259. // Process the given source string, attempting to find what is that path, and what is its
  1260. // arguments.
  1261. if (lpSrc)
  1262. {
  1263. // Extract the sub string, if its is realative then resolve (if required).
  1264. if (*lpSrc == TEXT('\"'))
  1265. {
  1266. for (lpSrc++, i=0 ; i<MAX_PATH && *lpSrc && *lpSrc!=TEXT('\"') ; i++, lpSrc++)
  1267. szName[i] = *lpSrc;
  1268. szName[i] = 0;
  1269. if (*lpSrc)
  1270. lpArgs = lpSrc+1;
  1271. if (((dwFlags & PPCF_FORCEQUALIFY) || PathIsRelative(szName))
  1272. && !(dwFlags & PPCF_NORELATIVEOBJECTQUALIFY))
  1273. {
  1274. if (!PathResolve(szName, NULL, PRF_TRYPROGRAMEXTENSIONS))
  1275. goto exit_gracefully;
  1276. }
  1277. bFound = TRUE;
  1278. }
  1279. else
  1280. {
  1281. // Is this a relative object, and then take each element upto a seperator
  1282. // and see if we hit an file system object. If not then we can
  1283. bRelative = PathIsRelative(lpSrc);
  1284. if (bRelative)
  1285. dwFlags &= ~PPCF_LONGESTPOSSIBLE;
  1286. bQualify = bRelative || ((dwFlags & PPCF_FORCEQUALIFY) != 0);
  1287. for (i=0; i < MAX_PATH; i++)
  1288. {
  1289. szName[i] = lpSrc[i];
  1290. // If we hit a space then the string either contains a LFN or we have
  1291. // some arguments. Therefore attempt to get the attributes for the string
  1292. // we have so far, if we are unable to then we can continue
  1293. // checking, if we hit then we know that the object exists and the
  1294. // trailing string are its arguments.
  1295. if (!szName[i] || szName[i] == TEXT(' '))
  1296. {
  1297. szName[i] = 0;
  1298. if (!bQualify || PathResolve(szName, NULL, PRF_TRYPROGRAMEXTENSIONS))
  1299. {
  1300. dwAttrib = GetFileAttributes(szName);
  1301. if ((dwAttrib != -1) && (! ((dwAttrib & FILE_ATTRIBUTE_DIRECTORY) && (dwFlags & PPCF_NODIRECTORIES))))
  1302. {
  1303. if (bQualify && (dwFlags & PPCF_NORELATIVEOBJECTQUALIFY))
  1304. *lstrcpyn(szName, lpSrc, i) = TEXT(' ');
  1305. bFound = TRUE; // success
  1306. lpArgs = &lpSrc[i];
  1307. if (dwFlags & PPCF_LONGESTPOSSIBLE)
  1308. {
  1309. lstrcpyn(szLastChoice, szName, i+1);
  1310. iLastChoice = i;
  1311. }
  1312. else
  1313. goto exit_gracefully;
  1314. }
  1315. }
  1316. if (bQualify)
  1317. memcpy(szName, lpSrc, (i+1)*sizeof(TCHAR));
  1318. else
  1319. szName[i]=lpSrc[i];
  1320. bHitSpace = TRUE;
  1321. }
  1322. if (!szName[i])
  1323. break;
  1324. }
  1325. }
  1326. }
  1327. exit_gracefully:
  1328. // Work out how big the temporary buffer should be, allocate it and
  1329. // build the returning string into it. Then compose the string
  1330. // to be returned.
  1331. if (bFound)
  1332. {
  1333. if ((dwFlags & PPCF_LONGESTPOSSIBLE) && iLastChoice)
  1334. {
  1335. lstrcpyn(szName, szLastChoice, iLastChoice+1);
  1336. lpArgs = &lpSrc[iLastChoice];
  1337. }
  1338. if (StrChr(szName, TEXT(' ')))
  1339. bAddQuotes = dwFlags & PPCF_ADDQUOTES;
  1340. iTotal = lstrlen(szName) + 1; // for terminator
  1341. iTotal += bAddQuotes ? 2 : 0;
  1342. iTotal += (dwFlags & PPCF_ADDARGUMENTS) && lpArgs ? lstrlen(lpArgs) : 0;
  1343. if (lpDest)
  1344. {
  1345. if (iTotal <= iDestMax)
  1346. {
  1347. lpBuffer = lpBuffer2 = (LPTSTR)LocalAlloc(LPTR, sizeof(TCHAR) * iTotal);
  1348. if (lpBuffer)
  1349. {
  1350. // First quote if required
  1351. if (bAddQuotes)
  1352. *lpBuffer2++ = TEXT('\"');
  1353. // Matching name
  1354. lstrcpy(lpBuffer2, szName);
  1355. // Closing quote if required
  1356. if (bAddQuotes)
  1357. lstrcat(lpBuffer2, TEXT("\""));
  1358. // Arguments (if requested)
  1359. if ((dwFlags & PPCF_ADDARGUMENTS) && lpArgs)
  1360. lstrcat(lpBuffer2, lpArgs);
  1361. // Then copy into callers buffer, and free out temporary buffer
  1362. lstrcpy(lpDest, lpBuffer);
  1363. LocalFree((HGLOBAL)lpBuffer);
  1364. // Return the length of the resulting string
  1365. iResult = iTotal;
  1366. }
  1367. }
  1368. }
  1369. else
  1370. {
  1371. // Resulting string is this big, although nothing returned (allows them to allocate a buffer)
  1372. iResult = iTotal;
  1373. }
  1374. }
  1375. return iResult;
  1376. }
  1377. // Gets the mounting point for the path passed in
  1378. //
  1379. // Return Value: TRUE: means that we found mountpoint, e.g. c:\ or c:\hostfolder\
  1380. // FALSE: for now means that the path is UNC or buffer too small
  1381. //
  1382. // Mounted volume Returned Path
  1383. //
  1384. // Passed in E:\MountPoint\path 1\path 2
  1385. // C:\ as E:\MountPoint E:\MountPoint
  1386. //
  1387. // Passed in E:\MountPoint\MountInter\path 1
  1388. // C:\ as D:\MountInter and D:\ as E:\MountPoint E:\MountPoint\MountInter
  1389. //
  1390. // Passed in E:\MountPoint\MountInter\path 1
  1391. // No mount E:\
  1392. BOOL PathGetMountPointFromPath(LPCTSTR pcszPath, LPTSTR pszMountPoint, int cchMountPoint)
  1393. {
  1394. BOOL bRet = FALSE;
  1395. RIPMSG(pcszPath && IS_VALID_STRING_PTR(pcszPath, -1), "PathGetMountPointFromPath: caller passed invalid pcszPath");
  1396. RIPMSG(pszMountPoint && cchMountPoint >= 0 && IS_VALID_WRITE_BUFFER(pszMountPoint, TCHAR, cchMountPoint), "PathGetMountPointFromPath: caller passed invalid pszMountPoint, cchMountPoint");
  1397. if (!PathIsUNC(pcszPath) && (cchMountPoint >= lstrlen(pcszPath) + 1))
  1398. {
  1399. lstrcpy(pszMountPoint, pcszPath);
  1400. // Is this only 'c:' or 'c:\'
  1401. if (lstrlen(pcszPath) > 3)
  1402. {
  1403. //no
  1404. LPTSTR pszNextComp = NULL;
  1405. LPTSTR pszBestChoice = NULL;
  1406. TCHAR cTmpChar;
  1407. PathAddBackslash(pszMountPoint);
  1408. //skip the first one, e.g. c:\
  1409. pszBestChoice = pszNextComp = PathFindNextComponent(pszMountPoint);
  1410. pszNextComp = PathFindNextComponent(pszNextComp);
  1411. while (pszNextComp)
  1412. {
  1413. cTmpChar = *pszNextComp;
  1414. *pszNextComp = 0;
  1415. if (GetVolumeInformation(pszMountPoint, NULL, 0, NULL, NULL, NULL, NULL, 0))
  1416. {//found something better than previous shorter path
  1417. pszBestChoice = pszNextComp;
  1418. }
  1419. *pszNextComp = cTmpChar;
  1420. pszNextComp = PathFindNextComponent(pszNextComp);
  1421. }
  1422. *pszBestChoice = 0;
  1423. }
  1424. bRet = TRUE;
  1425. }
  1426. else
  1427. *pszMountPoint = 0;
  1428. return bRet;
  1429. }
  1430. // Returns TRUE if the path is a shortcut to an installed program that can
  1431. // be found under Add/Remvoe Programs
  1432. // The current algorithm is just to make sure the target is an exe and is
  1433. // located under "program files"
  1434. STDAPI_(BOOL) PathIsShortcutToProgram(LPCTSTR pszFile)
  1435. {
  1436. BOOL bRet = FALSE;
  1437. if (PathIsShortcut(pszFile, -1))
  1438. {
  1439. TCHAR szTarget[MAX_PATH];
  1440. HRESULT hr = GetPathFromLinkFile(pszFile, szTarget, ARRAYSIZE(szTarget));
  1441. if (hr == S_OK)
  1442. {
  1443. if (PathIsExe(szTarget))
  1444. {
  1445. BOOL bSpecialApp = FALSE;
  1446. HKEY hkeySystemPrograms = NULL;
  1447. if (ERROR_SUCCESS == RegOpenKeyEx(HKEY_LOCAL_MACHINE, TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\App Management\\System Programs"),
  1448. 0, KEY_READ, &hkeySystemPrograms))
  1449. {
  1450. TCHAR szValue[MAX_PATH];
  1451. TCHAR szSystemPrograms[MAX_PATH];
  1452. DWORD cbSystemPrograms = sizeof(szSystemPrograms);
  1453. DWORD cchValue = ARRAYSIZE(szValue);
  1454. DWORD dwType;
  1455. LPTSTR pszFileName = PathFindFileName(szTarget);
  1456. int iValue = 0;
  1457. while (RegEnumValue(hkeySystemPrograms, iValue, szValue, &cchValue, NULL, &dwType,
  1458. (LPBYTE)szSystemPrograms, &cbSystemPrograms) == ERROR_SUCCESS)
  1459. {
  1460. if ((dwType == REG_SZ) && !StrCmpI(pszFileName, szSystemPrograms))
  1461. {
  1462. bSpecialApp = TRUE;
  1463. break;
  1464. }
  1465. cbSystemPrograms = sizeof(szSystemPrograms);
  1466. cchValue = ARRAYSIZE(szValue);
  1467. iValue++;
  1468. }
  1469. RegCloseKey(hkeySystemPrograms);
  1470. }
  1471. if (!bSpecialApp)
  1472. {
  1473. TCHAR szProgramFiles[MAX_PATH];
  1474. if (SHGetSpecialFolderPath(NULL, szProgramFiles, CSIDL_PROGRAM_FILES, FALSE))
  1475. {
  1476. if (PathIsPrefix(szProgramFiles, szTarget))
  1477. {
  1478. bRet = TRUE;
  1479. }
  1480. }
  1481. }
  1482. else
  1483. bRet = FALSE;
  1484. }
  1485. }
  1486. else if (hr == S_FALSE && szTarget[0])
  1487. {
  1488. // Darwin shortcuts, say yes
  1489. bRet = TRUE;
  1490. }
  1491. }
  1492. return bRet;
  1493. }
  1494. //
  1495. // needed because we export TCHAR versions of these functions that
  1496. // internal components still call
  1497. //
  1498. // Functions are forwarded to shlwapi
  1499. //
  1500. #undef PathMakePretty
  1501. STDAPI_(BOOL) PathMakePretty(LPTSTR pszPath)
  1502. {
  1503. SHELLSTATE ss;
  1504. SHGetSetSettings(&ss, SSF_DONTPRETTYPATH, FALSE);
  1505. if (ss.fDontPrettyPath)
  1506. return FALSE;
  1507. return PathMakePrettyW(pszPath);
  1508. }
  1509. #undef PathGetArgs
  1510. STDAPI_(LPTSTR) PathGetArgs(LPCTSTR pszPath)
  1511. {
  1512. return PathGetArgsW(pszPath);
  1513. }
  1514. #undef PathRemoveArgs
  1515. STDAPI_(void) PathRemoveArgs(LPTSTR pszPath)
  1516. {
  1517. PathRemoveArgsW(pszPath);
  1518. }
  1519. #undef PathFindOnPath
  1520. STDAPI_(BOOL) PathFindOnPath(LPTSTR pszFile, LPCTSTR *ppszOtherDirs)
  1521. {
  1522. return PathFindOnPathW(pszFile, ppszOtherDirs);
  1523. }
  1524. #undef PathFindExtension
  1525. STDAPI_(LPTSTR) PathFindExtension(LPCTSTR pszPath)
  1526. {
  1527. return PathFindExtensionW(pszPath);
  1528. }
  1529. #undef PathAddExtension
  1530. STDAPI_(BOOL) PathAddExtension(LPTSTR pszPath, LPCTSTR pszExtension)
  1531. {
  1532. return PathAddExtensionW(pszPath, pszExtension);
  1533. }
  1534. #undef PathRemoveExtension
  1535. STDAPI_(void) PathRemoveExtension(LPTSTR pszPath)
  1536. {
  1537. PathRemoveExtensionW(pszPath);
  1538. }
  1539. #undef PathRenameExtension
  1540. STDAPI_(BOOL) PathRenameExtension(LPTSTR pszPath, LPCTSTR pszExt)
  1541. {
  1542. return PathRenameExtensionW(pszPath, pszExt);
  1543. }
  1544. #undef PathCommonPrefix
  1545. STDAPI_(int) PathCommonPrefix(LPCTSTR pszFile1, LPCTSTR pszFile2, LPTSTR pszPath)
  1546. {
  1547. return PathCommonPrefixW(pszFile1, pszFile2, pszPath);
  1548. }
  1549. #undef PathIsPrefix
  1550. STDAPI_(BOOL) PathIsPrefix(IN LPCTSTR pszPrefix, IN LPCTSTR pszPath)
  1551. {
  1552. return PathIsPrefixW(pszPrefix, pszPath);
  1553. }
  1554. #undef PathRelativePathTo
  1555. STDAPI_(BOOL) PathRelativePathTo(LPTSTR pszPath,
  1556. LPCTSTR pszFrom, DWORD dwAttrFrom, LPCTSTR pszTo, DWORD dwAttrTo)
  1557. {
  1558. return PathRelativePathToW(pszPath, pszFrom, dwAttrFrom, pszTo, dwAttrTo);
  1559. }
  1560. #undef PathRemoveBlanks
  1561. STDAPI_(void) PathRemoveBlanks(LPTSTR pszString)
  1562. {
  1563. PathRemoveBlanksW(pszString);
  1564. }
  1565. #undef PathRemoveBackslash
  1566. STDAPI_(LPTSTR) PathRemoveBackslash(LPTSTR pszPath)
  1567. {
  1568. return PathRemoveBackslashW(pszPath);
  1569. }
  1570. #undef PathCanonicalize
  1571. STDAPI_(BOOL) PathCanonicalize(LPTSTR pszDst, LPCTSTR pszSrc)
  1572. {
  1573. return PathCanonicalizeW(pszDst, pszSrc);
  1574. }
  1575. #undef PathStripToRoot
  1576. STDAPI_(BOOL) PathStripToRoot(LPTSTR szRoot)
  1577. {
  1578. return PathStripToRootW(szRoot);
  1579. }
  1580. //CD-Autorun for Win9x called the TCHAR internal api's. So as a workaround we stub them through these function calls.
  1581. #undef PathRemoveFileSpec
  1582. STDAPI_(BOOL) PathRemoveFileSpec(LPTSTR pFile)
  1583. {
  1584. if (SHGetAppCompatFlags(ACF_ANSI) == ACF_ANSI)
  1585. return PathRemoveFileSpecA((LPSTR)pFile);
  1586. else
  1587. return PathRemoveFileSpecW(pFile);
  1588. }
  1589. #undef PathAddBackslash
  1590. STDAPI_(LPTSTR) PathAddBackslash(LPTSTR pszPath)
  1591. {
  1592. return PathAddBackslashW(pszPath);
  1593. }
  1594. #undef PathFindFileName
  1595. STDAPI_(LPTSTR) PathFindFileName(LPCTSTR pszPath)
  1596. {
  1597. return PathFindFileNameW(pszPath);
  1598. }
  1599. #undef PathIsFileSpec
  1600. STDAPI_(BOOL) PathIsFileSpec(LPCTSTR pszPath)
  1601. {
  1602. return PathIsFileSpecW(pszPath);
  1603. }
  1604. #undef PathIsUNCServer
  1605. STDAPI_(BOOL) PathIsUNCServer(LPCTSTR pszPath)
  1606. {
  1607. return PathIsUNCServerW(pszPath);
  1608. }
  1609. #undef PathIsUNCServerShare
  1610. STDAPI_(BOOL) PathIsUNCServerShare(LPCTSTR pszPath)
  1611. {
  1612. return PathIsUNCServerShareW(pszPath);
  1613. }
  1614. #undef PathStripPath
  1615. STDAPI_(void) PathStripPath(LPTSTR pszPath)
  1616. {
  1617. PathStripPathW(pszPath);
  1618. }
  1619. #undef PathSearchAndQualify
  1620. STDAPI_(BOOL) PathSearchAndQualify(LPCTSTR pcszPath, LPTSTR pszFullyQualifiedPath, UINT cchFullyQualifiedPath)
  1621. {
  1622. return PathSearchAndQualifyW(pcszPath, pszFullyQualifiedPath, cchFullyQualifiedPath);
  1623. }
  1624. // CD-Autorun for Win9x called the TCHAR internal api's. So as a workaround we stub them through these function calls.
  1625. #undef PathIsRoot
  1626. STDAPI_(BOOL) PathIsRoot(LPCTSTR pszPath)
  1627. {
  1628. if (SHGetAppCompatFlags(ACF_ANSI) == ACF_ANSI)
  1629. return PathIsRootA((LPCSTR)pszPath);
  1630. else
  1631. return PathIsRootW(pszPath);
  1632. }
  1633. #undef PathCompactPath
  1634. STDAPI_(BOOL) PathCompactPath(HDC hDC, LPTSTR pszPath, UINT dx)
  1635. {
  1636. return PathCompactPathW(hDC, pszPath, dx);
  1637. }
  1638. #undef PathCompactPathEx
  1639. STDAPI_(BOOL) PathCompactPathEx(LPTSTR pszOut, LPCTSTR pszSrc, UINT cchMax, DWORD dwFlags)
  1640. {
  1641. return PathCompactPathExW(pszOut, pszSrc, cchMax, dwFlags);
  1642. }
  1643. #undef PathSetDlgItemPath
  1644. STDAPI_(void) PathSetDlgItemPath(HWND hDlg, int id, LPCTSTR pszPath)
  1645. {
  1646. PathSetDlgItemPathW(hDlg, id, pszPath);
  1647. }
  1648. #undef PathUnquoteSpaces
  1649. STDAPI_(void) PathUnquoteSpaces(LPTSTR psz)
  1650. {
  1651. PathUnquoteSpacesW(psz);
  1652. }
  1653. #undef PathQuoteSpaces
  1654. STDAPI_(void) PathQuoteSpaces(LPTSTR psz)
  1655. {
  1656. PathQuoteSpacesW(psz);
  1657. }
  1658. #undef PathFindNextComponent
  1659. STDAPI_(LPTSTR) PathFindNextComponent(LPCTSTR pszPath)
  1660. {
  1661. return PathFindNextComponentW(pszPath);
  1662. }
  1663. #undef PathMatchSpec
  1664. STDAPI_(BOOL) PathMatchSpec(LPCTSTR pszFileParam, LPCTSTR pszSpec)
  1665. {
  1666. return PathMatchSpecW(pszFileParam, pszSpec);
  1667. }
  1668. #undef PathSkipRoot
  1669. STDAPI_(LPTSTR) PathSkipRoot(LPCTSTR pszPath)
  1670. {
  1671. return PathSkipRootW(pszPath);
  1672. }
  1673. #undef PathIsSameRoot
  1674. STDAPI_(BOOL) PathIsSameRoot(LPCTSTR pszPath1, LPCTSTR pszPath2)
  1675. {
  1676. return PathIsSameRootW(pszPath1, pszPath2);
  1677. }
  1678. #undef PathParseIconLocation
  1679. STDAPI_(int) PathParseIconLocation(IN OUT LPTSTR pszIconFile)
  1680. {
  1681. return PathParseIconLocationW(pszIconFile);
  1682. }
  1683. #undef PathIsURL
  1684. STDAPI_(BOOL) PathIsURL(IN LPCTSTR pszPath)
  1685. {
  1686. return PathIsURLW(pszPath);
  1687. }
  1688. #undef PathIsDirectory
  1689. STDAPI_(BOOL) PathIsDirectory(LPCTSTR pszPath)
  1690. {
  1691. return PathIsDirectoryW(pszPath);
  1692. }
  1693. // CD-Autorun for Win9x called the TCHAR internal api's. So as a workaround we stub them through these function calls.
  1694. #undef PathFileExists
  1695. STDAPI_(BOOL) PathFileExists(LPCTSTR pszPath)
  1696. {
  1697. if (SHGetAppCompatFlags(ACF_ANSI) == ACF_ANSI)
  1698. return PathFileExistsAndAttributesA((LPCSTR)pszPath, NULL);
  1699. else
  1700. return PathFileExistsAndAttributesW(pszPath, NULL);
  1701. }
  1702. #undef PathAppend
  1703. STDAPI_(BOOL) PathAppend(LPTSTR pszPath, LPCTSTR pszMore)
  1704. {
  1705. if (SHGetAppCompatFlags(ACF_ANSI) == ACF_ANSI)
  1706. return PathAppendA((LPSTR)pszPath, (LPCSTR)pszMore);
  1707. else
  1708. return PathAppendW(pszPath, pszMore);
  1709. }