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.

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