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.

681 lines
18 KiB

  1. //---------------------------------------------------------------------------
  2. //
  3. // Copyright (c) Microsoft Corporation 1993-1994
  4. //
  5. // File: path.c
  6. //
  7. // This files contains the path whacking code.
  8. //
  9. // History:
  10. // 01-31-94 ScottH Moved from shellext.c
  11. //
  12. //---------------------------------------------------------------------------
  13. ///////////////////////////////////////////////////// INCLUDES
  14. #include "brfprv.h" // common headers
  15. #include "res.h"
  16. /*----------------------------------------------------------
  17. Purpose: Removes the trailing backslash from a path.
  18. A:\ --> A:\
  19. C:\foo\ --> C:\foo
  20. \\Pyrex\User\ --> \\Pyrex\User
  21. Returns: pointer to NULL that replaced the backslash or
  22. the pointer to the last character if it isn't
  23. a backslash
  24. Cond: pimped this code from the shell
  25. */
  26. LPTSTR PUBLIC MyPathRemoveBackslash(
  27. LPTSTR lpszPath)
  28. {
  29. int len = lstrlen(lpszPath)-1;
  30. if (IsDBCSLeadByte((BYTE)*CharPrev(lpszPath,lpszPath+len+1)))
  31. len--;
  32. if (!PathIsRoot(lpszPath) && lpszPath[len] == TEXT('\\'))
  33. lpszPath[len] = TEXT('\0');
  34. return lpszPath + len;
  35. }
  36. #ifdef NOTUSED
  37. /*----------------------------------------------------------
  38. Purpose: copies the path without the extension into the buffer
  39. Returns: new path
  40. Cond: --
  41. */
  42. LPTSTR PUBLIC PathRemoveExt(
  43. LPCTSTR pszPath,
  44. LPTSTR pszBuf)
  45. {
  46. LPTSTR psz;
  47. LPTSTR pszMark = NULL;
  48. ASSERT(pszPath);
  49. ASSERT(pszBuf);
  50. psz = pszBuf;
  51. while (*pszPath)
  52. {
  53. *psz = *pszPath;
  54. pszPath = CharNext(pszPath);
  55. if (TEXT('.') == *psz)
  56. pszMark = psz;
  57. else if (TEXT('\\') == *psz)
  58. pszMark = NULL;
  59. psz = CharNext(psz);
  60. }
  61. *psz = TEXT('\0');
  62. if (pszMark)
  63. *pszMark = TEXT('\0');
  64. return pszBuf;
  65. }
  66. #endif
  67. /*----------------------------------------------------------
  68. Purpose: Convert a file spec to make it look a bit better
  69. if it is all upper case chars.
  70. Returns: --
  71. Cond: --
  72. */
  73. BOOL PRIVATE PathMakeComponentPretty(LPTSTR lpPath)
  74. {
  75. LPTSTR lp;
  76. // REVIEW: INTL need to deal with lower case chars in (>127) range?
  77. // check for all uppercase
  78. for (lp = lpPath; *lp; lp = CharNext(lp)) {
  79. if ((*lp >= TEXT('a')) && (*lp <= TEXT('z')))
  80. return FALSE; // this is a LFN, dont mess with it
  81. }
  82. CharLower(lpPath);
  83. CharUpperBuff(lpPath, 1);
  84. return TRUE; // did the conversion
  85. }
  86. //---------------------------------------------------------------------------
  87. // Given a pointer to a point in a path - return a ptr the start of the
  88. // next path component. Path components are delimted by slashes or the
  89. // null at the end.
  90. // There's special handling for UNC names.
  91. // This returns NULL if you pass in a pointer to a NULL ie if you're about
  92. // to go off the end of the path.
  93. LPTSTR PUBLIC PathFindNextComponentI(LPCTSTR lpszPath)
  94. {
  95. LPTSTR lpszLastSlash;
  96. // Are we at the end of a path.
  97. if (!*lpszPath)
  98. {
  99. // Yep, quit.
  100. return NULL;
  101. }
  102. // Find the next slash.
  103. // REVIEW UNDONE - can slashes be quoted?
  104. lpszLastSlash = StrChr(lpszPath, TEXT('\\'));
  105. // Is there a slash?
  106. if (!lpszLastSlash)
  107. {
  108. // No - Return a ptr to the NULL.
  109. return (LPTSTR) (lpszPath+lstrlen(lpszPath));
  110. }
  111. else
  112. {
  113. // Is it a UNC style name?
  114. if (TEXT('\\') == *(lpszLastSlash+1))
  115. {
  116. // Yep, skip over the second slash.
  117. return lpszLastSlash+2;
  118. }
  119. else
  120. {
  121. // Nope. just skip over one slash.
  122. return lpszLastSlash+1;
  123. }
  124. }
  125. }
  126. /*----------------------------------------------------------
  127. Purpose: Takes the path and makes it presentable.
  128. The rules are:
  129. If the LFN name is simply the short name (all caps),
  130. then convert to lowercase with first letter capitalized
  131. Returns: --
  132. Cond: --
  133. */
  134. void PUBLIC PathMakePresentable(
  135. LPTSTR pszPath)
  136. {
  137. LPTSTR pszComp; // pointers to begining and
  138. LPTSTR pszEnd; // end of path component
  139. LPTSTR pch;
  140. int cComponent = 0;
  141. BOOL bUNCPath;
  142. TCHAR ch;
  143. bUNCPath = PathIsUNC(pszPath);
  144. pszComp = pszPath;
  145. while (pszEnd = PathFindNextComponentI(pszComp))
  146. {
  147. // pszEnd may be pointing to the right of the backslash
  148. // beyond the path component, so back up one
  149. //
  150. ch = *pszEnd;
  151. *pszEnd = 0; // temporary null
  152. // pszComp points to the path component
  153. //
  154. pch = CharNext(pszComp);
  155. if (TEXT(':') == *pch)
  156. {
  157. // Simply capitalize the drive-portion of the path
  158. //
  159. CharUpper(pszComp);
  160. }
  161. else if (bUNCPath && cComponent++ < 3)
  162. {
  163. // Network server or share name
  164. // FEATURE: handle LFN network names
  165. //
  166. CharUpper(pszComp);
  167. PathMakeComponentPretty(pszComp);
  168. }
  169. else
  170. {
  171. // Normal path component
  172. //
  173. PathMakeComponentPretty(pszComp);
  174. }
  175. *pszEnd = ch;
  176. pszComp = pszEnd;
  177. }
  178. }
  179. #ifdef NOTUSED
  180. /*----------------------------------------------------------
  181. Purpose: Takes the path and pretties up each component of
  182. the path.
  183. The rules are:
  184. Use the LFN name of the component
  185. If the LFN name is simply the short name (all caps),
  186. then convert to lowercase with first letter capitalized
  187. Returns: --
  188. Cond: --
  189. */
  190. void PRIVATE PathGetCompleteLFN(
  191. LPCTSTR pszPath,
  192. LPTSTR pszLong,
  193. int cbLong)
  194. {
  195. TCHAR sz[MAX_PATH];
  196. TCHAR szPath[MAX_PATH+1];
  197. LPTSTR pszComp; // pointers to begining and end of path component
  198. LPTSTR pszEnd;
  199. int cbPath;
  200. int cb;
  201. BOOL bAtEnd = FALSE;
  202. int cComponent = 0;
  203. BOOL bUNCPath;
  204. TCHAR ch;
  205. // REARCHITECT: this is broken for double-byte characters for sure
  206. // For each component in string, get the LFN and add it to
  207. // the pszLong buffer.
  208. //
  209. cbPath = lstrlen(pszPath) * sizeof(TCHAR);
  210. ASSERT(cbPath+1 <= sizeof(szPath));
  211. lstrcpy(szPath, pszPath);
  212. bUNCPath = PathIsUNC(szPath);
  213. *pszLong = NULL_CHAR;
  214. cb = 0;
  215. pszComp = szPath;
  216. while (pszEnd = PathFindNextComponentI(pszComp))
  217. {
  218. // pszEnd may be pointing to the right of the backslash beyond the
  219. // path component, so back up one
  220. //
  221. if (0 == *pszEnd)
  222. bAtEnd = TRUE;
  223. else
  224. {
  225. if (!bUNCPath || cComponent > 0)
  226. pszEnd--; // not the server or share portions of a UNC path
  227. ch = *pszEnd;
  228. *pszEnd = 0; // temporary null
  229. }
  230. // pszComp points to the path component now
  231. //
  232. if (TEXT(':') == *(pszEnd-1) || TEXT(':') == *(pszEnd-2))
  233. {
  234. // Simply capitalize the drive-portion of the path
  235. //
  236. CharUpper(szPath);
  237. }
  238. else if (bUNCPath && cComponent++ < 3)
  239. {
  240. // Network server or share name
  241. // FEATURE: handle LFN network names
  242. //
  243. CharUpper(pszComp);
  244. PathMakeComponentPretty(pszComp);
  245. }
  246. else
  247. {
  248. int ib;
  249. // Try to get the LFN
  250. //
  251. *sz = NULL_CHAR;
  252. PathGetLongName(szPath, sz, ARRAYSIZE(sz));
  253. // If an LFN does not exist, keep the path component
  254. // as it is. (Sometimes the path component can be
  255. // something like "Link to Foo.txt")
  256. //
  257. if (*sz)
  258. {
  259. // Make pszComp point to the same offset in sz now
  260. // (the components in each are the same offsets)
  261. //
  262. ib = pszComp - (LPTSTR)szPath;
  263. pszComp = &sz[ib];
  264. }
  265. PathMakeComponentPretty(pszComp);
  266. }
  267. // Save new LFN-ized component to buffer
  268. //
  269. cb += lstrlen(pszComp) * sizeof(TCHAR);
  270. if (cbLong <= cb)
  271. break; // reached end of pszLong buffer
  272. lstrcat(pszLong, pszComp);
  273. if (!bAtEnd)
  274. {
  275. PathAddBackslash(pszLong);
  276. *pszEnd = ch;
  277. if (bUNCPath && 1 == cComponent)
  278. pszComp = pszEnd; // pointing to share portion of path
  279. else
  280. pszComp = pszEnd+1; // Move component pointer to next part
  281. }
  282. else
  283. pszComp = pszEnd;
  284. }
  285. }
  286. #endif
  287. /*----------------------------------------------------------
  288. Purpose: Returns TRUE if the combined path of pszFolder and
  289. pszName is greater than MAX_PATH.
  290. Returns: see above
  291. Cond: --
  292. */
  293. BOOL PUBLIC PathsTooLong(
  294. LPCTSTR pszFolder,
  295. LPCTSTR pszName)
  296. {
  297. // +1 for possible '\' between the two path components
  298. return lstrlen(pszFolder) + lstrlen(pszName) + 1 >= MAX_PATH;
  299. }
  300. /*----------------------------------------------------------
  301. Purpose: Fully qualifies a path
  302. Returns: --
  303. Cond: --
  304. */
  305. void PUBLIC BrfPathCanonicalize(
  306. LPCTSTR pszPath,
  307. LPTSTR pszBuf) // Must be sizeof(MAX_PATH)
  308. {
  309. DWORD dwcPathLen;
  310. dwcPathLen = GetFullPathName(pszPath, MAX_PATH, pszBuf, NULL);
  311. if (! dwcPathLen || dwcPathLen >= MAX_PATH)
  312. lstrcpy(pszBuf, pszPath);
  313. // If pszBuf won't cover losslessly to ANSI, use the short name instead
  314. #if defined(UNICODE)
  315. {
  316. CHAR szAnsi[MAX_PATH];
  317. WCHAR szUnicode[MAX_PATH];
  318. szUnicode[0] = L'\0';
  319. WideCharToMultiByte(CP_ACP, 0, pszBuf, -1, szAnsi, ARRAYSIZE(szAnsi), NULL, NULL);
  320. MultiByteToWideChar(CP_ACP, 0, szAnsi, -1, szUnicode, ARRAYSIZE(szUnicode));
  321. if (lstrcmp(szUnicode, pszBuf))
  322. {
  323. // Cannot convert losslessly from Unicode -> Ansi, so get the short path
  324. lstrcpy(szUnicode, pszBuf);
  325. SheShortenPath(szUnicode, TRUE);
  326. lstrcpy(pszBuf, szUnicode);
  327. }
  328. }
  329. #endif
  330. PathMakePresentable(pszBuf);
  331. ASSERT(lstrlen(pszBuf) < MAX_PATH);
  332. }
  333. /*----------------------------------------------------------
  334. Purpose: Gets the displayable filename of the path. The filename
  335. is placed in the provided buffer.
  336. Returns: pointer to buffer
  337. Cond: --
  338. */
  339. LPTSTR PUBLIC PathGetDisplayName(
  340. LPCTSTR pszPath,
  341. LPTSTR pszBuf)
  342. {
  343. SHFILEINFO sfi;
  344. if (SHGetFileInfo(pszPath, 0, &sfi, sizeof(sfi), SHGFI_DISPLAYNAME))
  345. lstrcpy(pszBuf, sfi.szDisplayName);
  346. else
  347. lstrcpy(pszBuf, PathFindFileName(pszPath));
  348. return pszBuf;
  349. }
  350. /*----------------------------------------------------------
  351. Purpose: Checks if the attributes of the path. If it is a
  352. directory and has the system bit set, and if the brfcase.dat
  353. file exists in the directory, then return TRUE.
  354. Worst case: performs two GetFileAttributes.
  355. Returns: see above
  356. Cond: --
  357. */
  358. BOOL PUBLIC PathCheckForBriefcase(
  359. LPCTSTR pszPath,
  360. DWORD dwAttrib) // if -1, then function gets the attributes
  361. {
  362. ASSERT(pszPath);
  363. if (0xFFFFFFFF == dwAttrib)
  364. {
  365. dwAttrib = GetFileAttributes(pszPath);
  366. if (0xFFFFFFFF == dwAttrib)
  367. return FALSE;
  368. }
  369. if (IsFlagSet(dwAttrib, FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_READONLY) ||
  370. IsFlagSet(dwAttrib, FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_SYSTEM))
  371. {
  372. TCHAR szT[MAX_PATH];
  373. LPCTSTR pszDBName;
  374. // Check for the existence of the brfcase.dat file.
  375. //
  376. if (IsLFNDrive(pszPath))
  377. pszDBName = g_szDBName;
  378. else
  379. pszDBName = g_szDBNameShort;
  380. if (PathsTooLong(pszPath, pszDBName))
  381. return FALSE;
  382. else
  383. {
  384. PathCombine(szT, pszPath, pszDBName);
  385. return PathExists(szT);
  386. }
  387. }
  388. return FALSE;
  389. }
  390. /*----------------------------------------------------------
  391. Purpose: Returns TRUE if the path is to a briefcase root.
  392. This function may hit the file-system to achieve
  393. its goal.
  394. Worst case: performs two GetFileAttributes.
  395. Returns: TRUE if the path refers to a briefcase root.
  396. Cond: --
  397. */
  398. BOOL PUBLIC PathIsBriefcase(
  399. LPCTSTR pszPath)
  400. {
  401. UINT uRet;
  402. ASSERT(pszPath);
  403. // We perform our search by first looking in our cache
  404. // of known briefcase paths (CPATH). If we don't find
  405. // anything, then we proceed to iterate thru each
  406. // component of the path, checking for these two things:
  407. //
  408. // 1) A directory with the system attribute
  409. // 2) The existence of a brfcase.dat file in the directory.
  410. //
  411. uRet = CPATH_GetLocality(pszPath, NULL);
  412. if (PL_FALSE == uRet)
  413. {
  414. uRet = PathCheckForBriefcase(pszPath, (DWORD)-1) ? PL_ROOT : PL_FALSE;
  415. if (PL_ROOT == uRet)
  416. {
  417. int atom;
  418. // Add this path to the briefcase path cache.
  419. //
  420. atom = Atom_Add(pszPath);
  421. if (ATOM_ERR != atom)
  422. CPATH_Replace(atom);
  423. }
  424. }
  425. return PL_ROOT == uRet;
  426. }
  427. /*----------------------------------------------------------
  428. Purpose: Gets the locality of the path, relative to any
  429. briefcase. If PL_ROOT or PL_INSIDE is returned,
  430. pszBuf will contain the path to the root of the
  431. briefcase.
  432. This function may hit the file-system to achieve
  433. its goal.
  434. Worst case: performs 2*n GetFileAttributes, where
  435. n is the number of components in pszPath.
  436. Returns: Path locality (PL_FALSE, PL_ROOT, PL_INSIDE)
  437. Cond: --
  438. */
  439. UINT PUBLIC PathGetLocality(
  440. LPCTSTR pszPath,
  441. LPTSTR pszBuf) // Buffer for root path
  442. {
  443. UINT uRet;
  444. ASSERT(pszPath);
  445. ASSERT(pszBuf);
  446. *pszBuf = NULL_CHAR;
  447. // pszPath may be:
  448. // 1) a path to the briefcase folder itself
  449. // 2) a path to a file or folder beneath the briefcase
  450. // 3) a path to something unrelated to a briefcase
  451. // We perform our search by first looking in our cache
  452. // of known briefcase paths (CPATH). If we don't find
  453. // anything, then we proceed to iterate thru each
  454. // component of the path, checking for these two things:
  455. //
  456. // 1) A directory with the system attribute
  457. // 2) The existence of a brfcase.dat file in the directory.
  458. //
  459. uRet = CPATH_GetLocality(pszPath, pszBuf);
  460. if (PL_FALSE == uRet)
  461. {
  462. int cnt = 0;
  463. lstrcpy(pszBuf, pszPath);
  464. do
  465. {
  466. if (PathCheckForBriefcase(pszBuf, (DWORD)-1))
  467. {
  468. int atom;
  469. uRet = cnt > 0 ? PL_INSIDE : PL_ROOT;
  470. // Add this briefcase path to our cache
  471. //
  472. atom = Atom_Add(pszBuf);
  473. if (ATOM_ERR != atom)
  474. CPATH_Replace(atom);
  475. break; // Done
  476. }
  477. cnt++;
  478. } while (PathRemoveFileSpec(pszBuf));
  479. if (PL_FALSE == uRet)
  480. *pszBuf = NULL_CHAR;
  481. }
  482. return uRet;
  483. }
  484. /*----------------------------------------------------------
  485. Purpose: Returns TRUE if the file/directory exists.
  486. Returns: see above
  487. Cond: --
  488. */
  489. BOOL PUBLIC PathExists(
  490. LPCTSTR pszPath)
  491. {
  492. return GetFileAttributes(pszPath) != 0xFFFFFFFF;
  493. }
  494. /*----------------------------------------------------------
  495. Purpose: Finds the end of the root specification in a path.
  496. input path output string
  497. ---------- -------------
  498. c: <empty string>
  499. c:\ <empty string>
  500. c:\foo foo
  501. c:\foo\bar foo\bar
  502. \\pyrex\user <empty string>
  503. \\pyrex\user\ <empty string>
  504. \\pyrex\user\foo foo
  505. \\pyrex\user\foo\bar foo\bar
  506. Returns: pointer to first character after end of root spec.
  507. Cond: --
  508. */
  509. LPCTSTR PUBLIC PathFindEndOfRoot(
  510. LPCTSTR pszPath)
  511. {
  512. LPCTSTR psz;
  513. ASSERT(pszPath);
  514. if (TEXT(':') == pszPath[1])
  515. {
  516. if (TEXT('\\') == pszPath[2])
  517. psz = &pszPath[3];
  518. else
  519. psz = &pszPath[2];
  520. }
  521. else if (PathIsUNC(pszPath))
  522. {
  523. psz = PathFindNextComponentI(pszPath); // hop double-slash
  524. psz = PathFindNextComponentI(psz); // hop server name
  525. if (psz)
  526. psz = PathFindNextComponentI(psz); // hop share name
  527. if (!psz)
  528. {
  529. ASSERT(0); // There is no share name
  530. psz = pszPath;
  531. }
  532. }
  533. else
  534. {
  535. ASSERT(0);
  536. psz = pszPath;
  537. }
  538. return psz;
  539. }
  540. /*----------------------------------------------------------
  541. Purpose: Sends a notify message to the shell regarding a file-status
  542. change.
  543. Returns: --
  544. Cond: --
  545. */
  546. void PUBLIC PathNotifyShell(
  547. LPCTSTR pszPath,
  548. NOTIFYSHELLEVENT nse,
  549. BOOL bDoNow) // TRUE: force the event to be processed right away
  550. {
  551. static LONG const rgShEvents[] =
  552. { SHCNE_CREATE, SHCNE_MKDIR, SHCNE_UPDATEITEM, SHCNE_UPDATEDIR };
  553. ASSERT(pszPath);
  554. ASSERT(nse < ARRAYSIZE(rgShEvents));
  555. SHChangeNotify(rgShEvents[nse], SHCNF_PATH, pszPath, NULL);
  556. if (bDoNow)
  557. {
  558. SHChangeNotify(0, SHCNF_FLUSHNOWAIT, NULL, NULL);
  559. }
  560. }