Leaked source code of windows server 2003
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.

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