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.

489 lines
12 KiB

  1. #include "priv.h"
  2. #include "advpub.h"
  3. #include "sdsutils.h"
  4. #include "utils.h"
  5. #ifdef WINNT_ENV
  6. #include <winnlsp.h> // Get private NORM_ flag for StrEqIntl()
  7. #endif
  8. #ifndef NORM_STOP_ON_NULL // Until we sync up with nt headers again...
  9. #define NORM_STOP_ON_NULL 0x10000000 /* stop at the null termination */
  10. #endif
  11. #define StrIntlEqNI( s1, s2, nChar) StrIsIntlEqualA( TRUE, s1, s2, nChar)
  12. static const TCHAR c_szPATH[] = TEXT("PATH");
  13. static const TCHAR c_szEllipses[] = TEXT("...");
  14. static const TCHAR c_szColonSlash[] = TEXT(":\\");
  15. //
  16. // Inline function to check for a double-backslash at the
  17. // beginning of a string
  18. //
  19. static __inline BOOL DBL_BSLASH(LPCTSTR psz)
  20. {
  21. return (psz[0] == TEXT('\\') && psz[1] == TEXT('\\'));
  22. }
  23. BOOL RunningOnNT(void)
  24. {
  25. OSVERSIONINFO VerInfo;
  26. VerInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
  27. GetVersionEx(&VerInfo);
  28. return (VerInfo.dwPlatformId == VER_PLATFORM_WIN32_NT);
  29. }
  30. // rips the last part of the path off including the backslash
  31. // C:\foo -> C:\ ;
  32. // C:\foo\bar -> C:\foo
  33. // C:\foo\ -> C:\foo
  34. // \\x\y\x -> \\x\y
  35. // \\x\y -> \\x
  36. // \\x -> ?? (test this)
  37. // \foo -> \ (Just the slash!)
  38. //
  39. // in/out:
  40. // pFile fully qualified path name
  41. // returns:
  42. // TRUE we stripped something
  43. // FALSE didn't strip anything (root directory case)
  44. //
  45. BOOL PathRemoveFileSpec(LPTSTR pFile)
  46. {
  47. LPTSTR pT;
  48. LPTSTR pT2 = pFile;
  49. for (pT = pT2; *pT2; pT2 = CharNext(pT2)) {
  50. if (*pT2 == TEXT('\\'))
  51. pT = pT2; // last "\" found, (we will strip here)
  52. else if (*pT2 == TEXT(':')) { // skip ":\" so we don't
  53. if (pT2[1] ==TEXT('\\')) // strip the "\" from "C:\"
  54. pT2++;
  55. pT = pT2 + 1;
  56. }
  57. }
  58. if (*pT == 0)
  59. return FALSE; // didn't strip anything
  60. //
  61. // handle the \foo case
  62. //
  63. else if ((pT == pFile) && (*pT == TEXT('\\'))) {
  64. // Is it just a '\'?
  65. if (*(pT+1) != TEXT('\0')) {
  66. // Nope.
  67. *(pT+1) = TEXT('\0');
  68. return TRUE; // stripped something
  69. }
  70. else {
  71. // Yep.
  72. return FALSE;
  73. }
  74. }
  75. else {
  76. *pT = 0;
  77. return TRUE; // stripped something
  78. }
  79. }
  80. // Returns a pointer to the last component of a path string.
  81. //
  82. // in:
  83. // path name, either fully qualified or not
  84. //
  85. // returns:
  86. // pointer into the path where the path is. if none is found
  87. // returns a poiter to the start of the path
  88. //
  89. // c:\foo\bar -> bar
  90. // c:\foo -> foo
  91. // c:\foo\ -> c:\foo\ (REVIEW: is this case busted?)
  92. // c:\ -> c:\ (REVIEW: this case is strange)
  93. // c: -> c:
  94. // foo -> foo
  95. LPTSTR PathFindFileName(LPCTSTR pPath)
  96. {
  97. LPCTSTR pT;
  98. for (pT = pPath; *pPath; pPath = CharNext(pPath)) {
  99. if ((pPath[0] == TEXT('\\') || pPath[0] == TEXT(':') || pPath[0] == TEXT('/'))
  100. && pPath[1] && pPath[1] != TEXT('\\') && pPath[1] != TEXT('/'))
  101. pT = pPath + 1;
  102. }
  103. return (LPTSTR)pT; // const -> non const
  104. }
  105. //---------------------------------------------------------------------------
  106. // Returns TRUE if the given string is a UNC path.
  107. //
  108. // TRUE
  109. // "\\foo\bar"
  110. // "\\foo" <- careful
  111. // "\\"
  112. // FALSE
  113. // "\foo"
  114. // "foo"
  115. // "c:\foo"
  116. //
  117. // Cond: Note that SHELL32 implements its own copy of this
  118. // function.
  119. BOOL PathIsUNC(LPCTSTR pszPath)
  120. {
  121. return DBL_BSLASH(pszPath);
  122. }
  123. //---------------------------------------------------------------------------
  124. // Returns 0 through 25 (corresponding to 'A' through 'Z') if the path has
  125. // a drive letter, otherwise returns -1.
  126. //
  127. //
  128. // Cond: Note that SHELL32 implements its own copy of this
  129. // function.
  130. int PathGetDriveNumber(LPCTSTR lpsz)
  131. {
  132. if (!IsDBCSLeadByte(lpsz[0]) && lpsz[1] == TEXT(':'))
  133. {
  134. if (lpsz[0] >= TEXT('a') && lpsz[0] <= TEXT('z'))
  135. return (lpsz[0] - TEXT('a'));
  136. else if (lpsz[0] >= TEXT('A') && lpsz[0] <= TEXT('Z'))
  137. return (lpsz[0] - TEXT('A'));
  138. }
  139. return -1;
  140. }
  141. //---------------------------------------------------------------------------
  142. // Returns TRUE if the given string is a UNC path to a server only (no share name).
  143. //
  144. // TRUE
  145. // "\\foo" <- careful
  146. // "\\"
  147. // FALSE
  148. // "\\foo\bar"
  149. // "\foo"
  150. // "foo"
  151. // "c:\foo"
  152. BOOL PathIsUNCServer(LPCTSTR pszPath)
  153. {
  154. if (DBL_BSLASH(pszPath))
  155. {
  156. int i = 0;
  157. LPTSTR szTmp;
  158. for (szTmp = (LPTSTR)pszPath; szTmp && *szTmp; szTmp = CharNext(szTmp) )
  159. {
  160. if (*szTmp==TEXT('\\'))
  161. {
  162. i++;
  163. }
  164. }
  165. return (i == 2);
  166. }
  167. return FALSE;
  168. }
  169. /*----------------------------------------------------------
  170. Purpose: Determines if pszPath is a directory. "C:\" is
  171. considered a directory too.
  172. Returns: TRUE if it is
  173. Cond: Note that SHELL32 implements its own copy of this
  174. function.
  175. */
  176. BOOL PathIsDirectory(LPCTSTR pszPath)
  177. {
  178. DWORD dwAttribs;
  179. // SHELL32's PathIsDirectory also handles server/share
  180. // paths, but calls WNet APIs, which we cannot call.
  181. if (PathIsUNCServer(pszPath))
  182. {
  183. return FALSE;
  184. }
  185. else
  186. {
  187. dwAttribs = GetFileAttributes(pszPath);
  188. if (dwAttribs != (DWORD)-1)
  189. return (BOOL)(dwAttribs & FILE_ATTRIBUTE_DIRECTORY);
  190. }
  191. return FALSE;
  192. }
  193. // check if a path is a root
  194. //
  195. // returns:
  196. // TRUE for "\" "X:\" "\\foo\asdf" "\\foo\"
  197. // FALSE for others
  198. BOOL PathIsRoot(LPCTSTR pPath)
  199. {
  200. if (!IsDBCSLeadByte(*pPath))
  201. {
  202. if (!lstrcmpi(pPath + 1, c_szColonSlash)) // "X:\" case
  203. return TRUE;
  204. }
  205. if ((*pPath == TEXT('\\')) && (*(pPath + 1) == 0)) // "\" case
  206. return TRUE;
  207. if (DBL_BSLASH(pPath)) // smells like UNC name
  208. {
  209. LPCTSTR p;
  210. int cBackslashes = 0;
  211. for (p = pPath + 2; *p; p = CharNext(p)) {
  212. if (*p == TEXT('\\') && (++cBackslashes > 1))
  213. return FALSE; /* not a bare UNC name, therefore not a root dir */
  214. }
  215. return TRUE; /* end of string with only 1 more backslash */
  216. /* must be a bare UNC, which looks like a root dir */
  217. }
  218. return FALSE;
  219. }
  220. // Removes a trailing backslash from a path
  221. //
  222. // in:
  223. // lpszPath (A:\, C:\foo\, etc)
  224. //
  225. // out:
  226. // lpszPath (A:\, C:\foo, etc)
  227. //
  228. // returns:
  229. // ponter to NULL that replaced the backslash
  230. // or the pointer to the last character if it isn't a backslash.
  231. LPTSTR PathRemoveBackslash( LPTSTR lpszPath )
  232. {
  233. int len = lstrlen(lpszPath)-1;
  234. if (IsDBCSLeadByte(*CharPrev(lpszPath,lpszPath+len+1)))
  235. len--;
  236. if (!PathIsRoot(lpszPath) && lpszPath[len] == TEXT('\\'))
  237. lpszPath[len] = TEXT('\0');
  238. return lpszPath + len;
  239. }
  240. // find the next slash or null terminator
  241. static LPCTSTR StrSlash(LPCTSTR psz)
  242. {
  243. for (; *psz && *psz != TEXT('\\'); psz = CharNext(psz));
  244. return psz;
  245. }
  246. /*
  247. * IntlStrEq
  248. *
  249. * returns TRUE if strings are equal, FALSE if not
  250. */
  251. BOOL StrIsIntlEqualA(BOOL fCaseSens, LPCSTR lpString1, LPCSTR lpString2, int nChar) {
  252. int retval;
  253. DWORD dwFlags = fCaseSens ? LOCALE_USE_CP_ACP : (NORM_IGNORECASE | LOCALE_USE_CP_ACP);
  254. if ( RunningOnNT() )
  255. {
  256. // On NT we can tell CompareString to stop at a '\0' if one is found before nChar chars
  257. //
  258. dwFlags |= NORM_STOP_ON_NULL;
  259. }
  260. else if (nChar != -1)
  261. {
  262. // On Win9x we have to do the check manually
  263. //
  264. LPCSTR psz1, psz2;
  265. int cch = 0;
  266. psz1 = lpString1;
  267. psz2 = lpString2;
  268. while( *psz1 != '\0' && *psz2 != '\0' && cch < nChar) {
  269. psz1 = CharNextA(psz1);
  270. psz2 = CharNextA(psz2);
  271. cch = min((int)(psz1 - lpString1), (int)(psz2 - lpString2));
  272. }
  273. // add one in for terminating '\0'
  274. cch++;
  275. if (cch < nChar) {
  276. nChar = cch;
  277. }
  278. }
  279. retval = CompareStringA( GetThreadLocale(),
  280. dwFlags,
  281. lpString1,
  282. nChar,
  283. lpString2,
  284. nChar );
  285. if (retval == 0)
  286. {
  287. //
  288. // The caller is not expecting failure. Try the system
  289. // default locale id.
  290. //
  291. retval = CompareStringA( LOCALE_SYSTEM_DEFAULT,
  292. dwFlags,
  293. lpString1,
  294. nChar,
  295. lpString2,
  296. nChar );
  297. }
  298. return (retval == 2);
  299. }
  300. //
  301. // in:
  302. // pszFile1 -- fully qualified path name to file #1.
  303. // pszFile2 -- fully qualified path name to file #2.
  304. //
  305. // out:
  306. // pszPath -- pointer to a string buffer (may be NULL)
  307. //
  308. // returns:
  309. // length of output buffer not including the NULL
  310. //
  311. // examples:
  312. // c:\win\desktop\foo.txt
  313. // c:\win\tray\bar.txt
  314. // -> c:\win
  315. //
  316. // c:\ ;
  317. // c:\ ;
  318. // -> c:\ NOTE, includes slash
  319. //
  320. // Returns:
  321. // Length of the common prefix string usually does NOT include
  322. // trailing slash, BUT for roots it does.
  323. //
  324. int
  325. PathCommonPrefix(
  326. LPCTSTR pszFile1,
  327. LPCTSTR pszFile2,
  328. LPTSTR pszPath)
  329. {
  330. LPCTSTR psz1, psz2, pszNext1, pszNext2, pszCommon;
  331. int cch;
  332. pszCommon = NULL;
  333. if (pszPath)
  334. *pszPath = TEXT('\0');
  335. psz1 = pszFile1;
  336. psz2 = pszFile2;
  337. // special cases for UNC, don't allow "\\" to be a common prefix
  338. if (DBL_BSLASH(pszFile1))
  339. {
  340. if (!DBL_BSLASH(pszFile2))
  341. return 0;
  342. psz1 = pszFile1 + 2;
  343. }
  344. if (DBL_BSLASH(pszFile2))
  345. {
  346. if (!DBL_BSLASH(pszFile1))
  347. return 0;
  348. psz2 = pszFile2 + 2;
  349. }
  350. while (1)
  351. {
  352. //ASSERT(*psz1 != TEXT('\\') && *psz2 != TEXT('\\'));
  353. pszNext1 = StrSlash(psz1);
  354. pszNext2 = StrSlash(psz2);
  355. cch = (int)(pszNext1 - psz1);
  356. if (cch != (pszNext2 - psz2))
  357. break; // lengths of segments not equal
  358. if (StrIntlEqNI(psz1, psz2, cch))
  359. pszCommon = pszNext1;
  360. else
  361. break;
  362. //ASSERT(*pszNext1 == TEXT('\0') || *pszNext1 == TEXT('\\'));
  363. //ASSERT(*pszNext2 == TEXT('\0') || *pszNext2 == TEXT('\\'));
  364. if (*pszNext1 == TEXT('\0'))
  365. break;
  366. psz1 = pszNext1 + 1;
  367. if (*pszNext2 == TEXT('\0'))
  368. break;
  369. psz2 = pszNext2 + 1;
  370. }
  371. if (pszCommon)
  372. {
  373. cch = (int)(pszCommon - pszFile1);
  374. // special case the root to include the slash
  375. if (cch == 2)
  376. {
  377. //ASSERT(pszFile1[1] == TEXT(':'));
  378. cch++;
  379. }
  380. }
  381. else
  382. cch = 0;
  383. if (pszPath)
  384. {
  385. CopyMemory(pszPath, pszFile1, cch * sizeof(TCHAR));
  386. pszPath[cch] = TEXT('\0');
  387. }
  388. return cch;
  389. }
  390. /*----------------------------------------------------------
  391. Purpose: Returns TRUE if pszPrefix is the full prefix of pszPath.
  392. Returns:
  393. Cond: --
  394. */
  395. BOOL PathIsPrefix( LPCTSTR pszPrefix, LPCTSTR pszPath)
  396. {
  397. int cch = PathCommonPrefix(pszPath, pszPrefix, NULL);
  398. return (lstrlen(pszPrefix) == cch);
  399. }