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.

697 lines
18 KiB

  1. //============================================================================
  2. //
  3. // DBCS aware string routines...
  4. //
  5. //
  6. //============================================================================
  7. #include "ctlspriv.h"
  8. #include <winnlsp.h> // Get private NORM_ flag for StrEqIntl()
  9. // for those of us who don't ssync to nt's build headers
  10. #ifndef NORM_STOP_ON_NULL
  11. #define NORM_STOP_ON_NULL 0x10000000
  12. #endif
  13. // WARNING: all of these APIs do not setup DS, so you can not access
  14. // any data in the default data seg of this DLL.
  15. //
  16. // do not create any global variables... talk to chrisg if you don't
  17. // understand thid
  18. #define READNATIVEWORD(x) (*(UNALIGNED WORD *)x)
  19. /*
  20. * StrEndN - Find the end of a string, but no more than n bytes
  21. * Assumes lpStart points to start of null terminated string
  22. * nBufSize is the maximum length
  23. * returns ptr to just after the last byte to be included
  24. */
  25. LPSTR lstrfns_StrEndNA(LPCSTR lpStart, int nBufSize)
  26. {
  27. LPCSTR lpEnd;
  28. for (lpEnd = lpStart + nBufSize; *lpStart && OFFSETOF(lpStart) < OFFSETOF(lpEnd);
  29. lpStart = AnsiNext(lpStart))
  30. continue; /* just getting to the end of the string */
  31. if (OFFSETOF(lpStart) > OFFSETOF(lpEnd))
  32. {
  33. /* We can only get here if the last byte before lpEnd was a lead byte
  34. */
  35. lpStart -= 2;
  36. }
  37. return((LPSTR)lpStart);
  38. }
  39. LPWSTR lstrfns_StrEndNW(LPCWSTR lpStart, int nBufSize)
  40. {
  41. LPCWSTR lpEnd;
  42. for (lpEnd = lpStart + nBufSize; *lpStart && (lpStart < lpEnd);
  43. lpStart++)
  44. continue; /* just getting to the end of the string */
  45. return((LPWSTR)lpStart);
  46. }
  47. /*
  48. * ChrCmp - Case sensitive character comparison for DBCS
  49. * Assumes w1, wMatch are characters to be compared
  50. * Return FALSE if they match, TRUE if no match
  51. */
  52. __inline BOOL ChrCmpA_inline(WORD w1, WORD wMatch)
  53. {
  54. /* Most of the time this won't match, so test it first for speed.
  55. */
  56. if (LOBYTE(w1) == LOBYTE(wMatch))
  57. {
  58. if (IsDBCSLeadByte(LOBYTE(w1)))
  59. {
  60. return(w1 != wMatch);
  61. }
  62. return FALSE;
  63. }
  64. return TRUE;
  65. }
  66. __inline BOOL ChrCmpW_inline(WCHAR w1, WCHAR wMatch)
  67. {
  68. return(!(w1 == wMatch));
  69. }
  70. /*
  71. * ChrCmpI - Case insensitive character comparison for DBCS
  72. * Assumes w1, wMatch are characters to be compared;
  73. * HIBYTE of wMatch is 0 if not a DBC
  74. * Return FALSE if match, TRUE if not
  75. */
  76. BOOL ChrCmpIA(WORD w1, WORD wMatch)
  77. {
  78. char sz1[3], sz2[3];
  79. if (IsDBCSLeadByte(sz1[0] = LOBYTE(w1)))
  80. {
  81. sz1[1] = HIBYTE(w1);
  82. sz1[2] = '\0';
  83. }
  84. else
  85. sz1[1] = '\0';
  86. *(WORD *)sz2 = wMatch;
  87. sz2[2] = '\0';
  88. return lstrcmpiA(sz1, sz2);
  89. }
  90. BOOL ChrCmpIW(WCHAR w1, WCHAR wMatch)
  91. {
  92. WCHAR sz1[2], sz2[2];
  93. sz1[0] = w1;
  94. sz1[1] = TEXT('\0');
  95. sz2[0] = wMatch;
  96. sz2[1] = TEXT('\0');
  97. return lstrcmpiW(sz1, sz2);
  98. }
  99. /*
  100. * StrChr - Find first occurrence of character in string
  101. * Assumes lpStart points to start of null terminated string
  102. * wMatch is the character to match
  103. * returns ptr to the first occurrence of ch in str, NULL if not found.
  104. */
  105. LPSTR StrChrA(LPCSTR lpStart, WORD wMatch)
  106. {
  107. for ( ; *lpStart; lpStart = AnsiNext(lpStart))
  108. {
  109. if (!ChrCmpA_inline(READNATIVEWORD(lpStart), wMatch))
  110. {
  111. return((LPSTR)lpStart);
  112. }
  113. }
  114. return (NULL);
  115. }
  116. LPWSTR StrChrW(LPCWSTR lpStart, WCHAR wMatch)
  117. {
  118. //
  119. // Apparently, somebody is passing unaligned strings to StrChrW.
  120. // Find out who and make them stop.
  121. //
  122. ASSERT(!((ULONG_PTR)lpStart & 1)); // Assert alignedness
  123. for ( ; *lpStart; lpStart++)
  124. {
  125. if (!ChrCmpW_inline(*lpStart, wMatch))
  126. {
  127. return((LPWSTR)lpStart);
  128. }
  129. }
  130. return (NULL);
  131. }
  132. /*
  133. * StrRChr - Find last occurrence of character in string
  134. * Assumes lpStart points to start of string
  135. * lpEnd points to end of string (NOT included in search)
  136. * wMatch is the character to match
  137. * returns ptr to the last occurrence of ch in str, NULL if not found.
  138. */
  139. LPSTR StrRChrA(LPCSTR lpStart, LPCSTR lpEnd, WORD wMatch)
  140. {
  141. LPCSTR lpFound = NULL;
  142. if (!lpEnd)
  143. lpEnd = lpStart + lstrlenA(lpStart);
  144. for ( ; OFFSETOF(lpStart) < OFFSETOF(lpEnd); lpStart = AnsiNext(lpStart))
  145. {
  146. if (!ChrCmpA_inline(READNATIVEWORD(lpStart), wMatch))
  147. lpFound = lpStart;
  148. }
  149. return ((LPSTR)lpFound);
  150. }
  151. LPWSTR StrRChrW(LPCWSTR lpStart, LPCWSTR lpEnd, WCHAR wMatch)
  152. {
  153. LPCWSTR lpFound = NULL;
  154. if (!lpEnd)
  155. lpEnd = lpStart + lstrlenW(lpStart);
  156. for ( ; lpStart < lpEnd; lpStart++)
  157. {
  158. if (!ChrCmpW_inline(*lpStart, wMatch))
  159. lpFound = lpStart;
  160. }
  161. return ((LPWSTR)lpFound);
  162. }
  163. /*
  164. * StrRChrI - Find last occurrence of character in string, case insensitive
  165. * Assumes lpStart points to start of string
  166. * lpEnd points to end of string (NOT included in search)
  167. * wMatch is the character to match
  168. * returns ptr to the last occurrence of ch in str, NULL if not found.
  169. */
  170. LPSTR StrRChrIA(LPCSTR lpStart, LPCSTR lpEnd, WORD wMatch)
  171. {
  172. LPCSTR lpFound = NULL;
  173. if (!lpEnd)
  174. lpEnd = lpStart + lstrlenA(lpStart);
  175. wMatch = (UINT)(IsDBCSLeadByte(LOBYTE(wMatch)) ? wMatch : LOBYTE(wMatch));
  176. for ( ; OFFSETOF(lpStart) < OFFSETOF(lpEnd); lpStart = AnsiNext(lpStart))
  177. {
  178. if (!ChrCmpIA(READNATIVEWORD(lpStart), wMatch))
  179. lpFound = lpStart;
  180. }
  181. return ((LPSTR)lpFound);
  182. }
  183. LPWSTR StrRChrIW(LPCWSTR lpStart, LPCWSTR lpEnd, WCHAR wMatch)
  184. {
  185. LPCWSTR lpFound = NULL;
  186. if (!lpEnd)
  187. lpEnd = lpStart + lstrlenW(lpStart);
  188. for ( ; lpStart < lpEnd; lpStart++)
  189. {
  190. if (!ChrCmpIW(*lpStart, wMatch))
  191. lpFound = lpStart;
  192. }
  193. return ((LPWSTR)lpFound);
  194. }
  195. // StrCSpn: return index to first char of lpStr that is present in lpSet.
  196. // Includes the NUL in the comparison; if no lpSet chars are found, returns
  197. // the index to the NUL in lpStr.
  198. // Just like CRT strcspn.
  199. //
  200. int StrCSpnA(LPCSTR lpStr, LPCSTR lpSet)
  201. {
  202. // nature of the beast: O(lpStr*lpSet) work
  203. LPCSTR lp = lpStr;
  204. if (!lpStr || !lpSet)
  205. return 0;
  206. while (*lp)
  207. {
  208. if (StrChrA(lpSet, READNATIVEWORD(lp)))
  209. return (int)(lp-lpStr);
  210. lp = AnsiNext(lp);
  211. }
  212. return (int)(lp-lpStr); // ==lstrlen(lpStr)
  213. }
  214. int StrCSpnW(LPCWSTR lpStr, LPCWSTR lpSet)
  215. {
  216. // nature of the beast: O(lpStr*lpSet) work
  217. LPCWSTR lp = lpStr;
  218. if (!lpStr || !lpSet)
  219. return 0;
  220. while (*lp)
  221. {
  222. if (StrChrW(lpSet, *lp))
  223. return (int)(lp-lpStr);
  224. lp++;
  225. }
  226. return (int)(lp-lpStr); // ==lstrlen(lpStr)
  227. }
  228. // StrCSpnI: case-insensitive version of StrCSpn.
  229. //
  230. int StrCSpnIA(LPCSTR lpStr, LPCSTR lpSet)
  231. {
  232. // nature of the beast: O(lpStr*lpSet) work
  233. LPCSTR lp = lpStr;
  234. if (!lpStr || !lpSet)
  235. return 0;
  236. while (*lp)
  237. {
  238. if (StrChrIA(lpSet, READNATIVEWORD(lp)))
  239. return (int)(lp-lpStr);
  240. lp = AnsiNext(lp);
  241. }
  242. return (int)(lp-lpStr); // ==lstrlen(lpStr)
  243. }
  244. int StrCSpnIW(LPCWSTR lpStr, LPCWSTR lpSet)
  245. {
  246. // nature of the beast: O(lpStr*lpSet) work
  247. LPCWSTR lp = lpStr;
  248. if (!lpStr || !lpSet)
  249. return 0;
  250. while (*lp)
  251. {
  252. if (StrChrIW(lpSet, *lp))
  253. return (int)(lp-lpStr);
  254. lp++;
  255. }
  256. return (int)(lp-lpStr); // ==lstrlen(lpStr)
  257. }
  258. /*
  259. * StrCmpN - Compare n bytes
  260. *
  261. * returns See lstrcmp return values.
  262. */
  263. int StrCmpNA(LPCSTR lpStr1, LPCSTR lpStr2, int nChar)
  264. {
  265. char sz1[4];
  266. char sz2[4];
  267. LPCSTR lpszEnd = lpStr1 + nChar;
  268. //DebugMsg(DM_TRACE, "StrCmpN: %s %s %d returns:", lpStr1, lpStr2, nChar);
  269. for ( ; (lpszEnd > lpStr1) && (*lpStr1 || *lpStr2); lpStr1 = AnsiNext(lpStr1), lpStr2 = AnsiNext(lpStr2)) {
  270. WORD wMatch;
  271. wMatch = (WORD) (*lpStr2 | (*(lpStr2+1)<<8));
  272. if (ChrCmpA_inline(READNATIVEWORD(lpStr1), wMatch))
  273. {
  274. int iRet;
  275. (*(WORD *)sz1) = READNATIVEWORD(lpStr1);
  276. (*(WORD *)sz2) = wMatch;
  277. *AnsiNext(sz1) = 0;
  278. *AnsiNext(sz2) = 0;
  279. iRet = lstrcmpA(sz1, sz2);
  280. //DebugMsg(DM_TRACE, ".................... %d", iRet);
  281. return iRet;
  282. }
  283. }
  284. //DebugMsg(DM_TRACE, ".................... 0");
  285. return 0;
  286. }
  287. int StrCmpNW(LPCWSTR lpStr1, LPCWSTR lpStr2, int nChar)
  288. {
  289. WCHAR sz1[2];
  290. WCHAR sz2[2];
  291. int i;
  292. LPCWSTR lpszEnd = lpStr1 + nChar;
  293. //DebugMsg(DM_TRACE, "StrCmpN: %s %s %d returns:", lpStr1, lpStr2, nChar);
  294. for ( ; (lpszEnd > lpStr1) && (*lpStr1 || *lpStr2); lpStr1++, lpStr2++)
  295. {
  296. i = ChrCmpW_inline(*lpStr1, *lpStr2);
  297. if (i)
  298. {
  299. int iRet;
  300. sz1[0] = *lpStr1;
  301. sz2[0] = *lpStr2;
  302. sz1[1] = TEXT('\0');
  303. sz2[1] = TEXT('\0');
  304. iRet = lstrcmpW(sz1, sz2);
  305. //DebugMsg(DM_TRACE, ".................... %d", iRet);
  306. return iRet;
  307. }
  308. }
  309. //DebugMsg(DM_TRACE, ".................... 0");
  310. return 0;
  311. }
  312. /*
  313. * StrCmpNI - Compare n bytes, case insensitive
  314. *
  315. * returns See lstrcmpi return values.
  316. */
  317. int StrCmpNIA(LPCSTR lpStr1, LPCSTR lpStr2, int nChar)
  318. {
  319. int i;
  320. // Win95 doesn't support NORM_STOP_ON_NULL
  321. i = CompareStringA(GetThreadLocale(), NORM_IGNORECASE | NORM_STOP_ON_NULL,
  322. lpStr1, nChar, lpStr2, nChar);
  323. if (!i)
  324. {
  325. i = CompareStringA(LOCALE_SYSTEM_DEFAULT, NORM_IGNORECASE | NORM_STOP_ON_NULL,
  326. lpStr1, nChar, lpStr2, nChar);
  327. }
  328. return i - CSTR_EQUAL;
  329. }
  330. int StrCmpNIW(LPCWSTR lpStr1, LPCWSTR lpStr2, int nChar)
  331. {
  332. int i;
  333. // Win95 doesn't support NORM_STOP_ON_NULL
  334. i = CompareStringW(GetThreadLocale(), NORM_IGNORECASE | NORM_STOP_ON_NULL,
  335. lpStr1, nChar, lpStr2, nChar);
  336. if (!i)
  337. {
  338. i = CompareStringW(LOCALE_SYSTEM_DEFAULT, NORM_IGNORECASE | NORM_STOP_ON_NULL,
  339. lpStr1, nChar, lpStr2, nChar);
  340. }
  341. return i - CSTR_EQUAL;
  342. }
  343. /*
  344. * IntlStrEq
  345. *
  346. * returns TRUE if strings are equal, FALSE if not
  347. */
  348. BOOL IntlStrEqWorkerA(BOOL fCaseSens, LPCSTR lpString1, LPCSTR lpString2, int nChar)
  349. {
  350. int retval;
  351. DWORD dwFlags = fCaseSens ? LOCALE_USE_CP_ACP : (NORM_IGNORECASE | LOCALE_USE_CP_ACP);
  352. //
  353. // On NT we can tell CompareString to stop at a '\0' if one is found before nChar chars
  354. //
  355. dwFlags |= NORM_STOP_ON_NULL;
  356. retval = CompareStringA( GetThreadLocale(),
  357. dwFlags,
  358. lpString1,
  359. nChar,
  360. lpString2,
  361. nChar );
  362. if (retval == 0)
  363. {
  364. //
  365. // The caller is not expecting failure. Try the system
  366. // default locale id.
  367. //
  368. retval = CompareStringA( GetSystemDefaultLCID(),
  369. dwFlags,
  370. lpString1,
  371. nChar,
  372. lpString2,
  373. nChar );
  374. }
  375. if (retval == 0)
  376. {
  377. if (lpString1 && lpString2)
  378. {
  379. //
  380. // The caller is not expecting failure. We've never had a
  381. // failure indicator before. We'll do a best guess by calling
  382. // the C runtimes to do a non-locale sensitive compare.
  383. //
  384. if (fCaseSens)
  385. retval = StrCmpNA(lpString1, lpString2, nChar) + 2;
  386. else {
  387. retval = StrCmpNIA(lpString1, lpString2, nChar) + 2;
  388. }
  389. }
  390. else
  391. {
  392. retval = 2;
  393. }
  394. }
  395. return (retval == 2);
  396. }
  397. BOOL IntlStrEqWorkerW(BOOL fCaseSens, LPCWSTR lpString1, LPCWSTR lpString2, int nChar)
  398. {
  399. int retval;
  400. DWORD dwFlags = fCaseSens ? 0 : NORM_IGNORECASE;
  401. //
  402. // On NT we can tell CompareString to stop at a '\0' if one is found before nChar chars
  403. //
  404. dwFlags |= NORM_STOP_ON_NULL;
  405. retval = CompareStringW( GetThreadLocale(),
  406. dwFlags,
  407. lpString1,
  408. nChar,
  409. lpString2,
  410. nChar );
  411. if (retval == 0)
  412. {
  413. //
  414. // The caller is not expecting failure. Try the system
  415. // default locale id.
  416. //
  417. retval = CompareStringW( GetSystemDefaultLCID(),
  418. dwFlags,
  419. lpString1,
  420. nChar,
  421. lpString2,
  422. nChar );
  423. }
  424. if (retval == 0)
  425. {
  426. if (lpString1 && lpString2)
  427. {
  428. //
  429. // The caller is not expecting failure. We've never had a
  430. // failure indicator before. We'll do a best guess by calling
  431. // the C runtimes to do a non-locale sensitive compare.
  432. //
  433. if (fCaseSens)
  434. retval = StrCmpNW(lpString1, lpString2, nChar) + 2;
  435. else {
  436. retval = StrCmpNIW(lpString1, lpString2, nChar) + 2;
  437. }
  438. }
  439. else
  440. {
  441. retval = 2;
  442. }
  443. }
  444. return (retval == 2);
  445. }
  446. /*
  447. * StrRStrI - Search for last occurrence of a substring
  448. *
  449. * Assumes lpSource points to the null terminated source string
  450. * lpLast points to where to search from in the source string
  451. * lpLast is not included in the search
  452. * lpSrch points to string to search for
  453. * returns last occurrence of string if successful; NULL otherwise
  454. */
  455. LPSTR StrRStrIA(LPCSTR lpSource, LPCSTR lpLast, LPCSTR lpSrch)
  456. {
  457. LPCSTR lpFound = NULL;
  458. LPSTR lpEnd;
  459. char cHold;
  460. if (!lpLast)
  461. lpLast = lpSource + lstrlenA(lpSource);
  462. if (lpSource >= lpLast || *lpSrch == 0)
  463. return NULL;
  464. lpEnd = lstrfns_StrEndNA(lpLast, (UINT)(lstrlenA(lpSrch)-1));
  465. cHold = *lpEnd;
  466. *lpEnd = 0;
  467. while ((lpSource = StrStrIA(lpSource, lpSrch))!=0 &&
  468. OFFSETOF(lpSource) < OFFSETOF(lpLast))
  469. {
  470. lpFound = lpSource;
  471. lpSource = AnsiNext(lpSource);
  472. }
  473. *lpEnd = cHold;
  474. return((LPSTR)lpFound);
  475. }
  476. LPWSTR StrRStrIW(LPCWSTR lpSource, LPCWSTR lpLast, LPCWSTR lpSrch)
  477. {
  478. LPCWSTR lpFound = NULL;
  479. LPWSTR lpEnd;
  480. WCHAR cHold;
  481. if (!lpLast)
  482. lpLast = lpSource + lstrlenW(lpSource);
  483. if (lpSource >= lpLast || *lpSrch == 0)
  484. return NULL;
  485. lpEnd = lstrfns_StrEndNW(lpLast, (UINT)(lstrlenW(lpSrch)-1));
  486. cHold = *lpEnd;
  487. *lpEnd = 0;
  488. while ((lpSource = StrStrIW(lpSource, lpSrch))!=0 &&
  489. lpSource < lpLast)
  490. {
  491. lpFound = lpSource;
  492. lpSource++;
  493. }
  494. *lpEnd = cHold;
  495. return((LPWSTR)lpFound);
  496. }
  497. /*
  498. * StrStr - Search for first occurrence of a substring
  499. *
  500. * Assumes lpSource points to source string
  501. * lpSrch points to string to search for
  502. * returns first occurrence of string if successful; NULL otherwise
  503. */
  504. LPSTR StrStrA(LPCSTR lpFirst, LPCSTR lpSrch)
  505. {
  506. UINT uLen;
  507. WORD wMatch;
  508. uLen = (UINT)lstrlenA(lpSrch);
  509. wMatch = READNATIVEWORD(lpSrch);
  510. for ( ; (lpFirst=StrChrA(lpFirst, wMatch))!=0 && !IntlStrEqNA(lpFirst, lpSrch, uLen);
  511. lpFirst=AnsiNext(lpFirst))
  512. continue; /* continue until we hit the end of the string or get a match */
  513. return((LPSTR)lpFirst);
  514. }
  515. LPWSTR StrStrW(LPCWSTR lpFirst, LPCWSTR lpSrch)
  516. {
  517. UINT uLen;
  518. WCHAR wMatch;
  519. uLen = (UINT)lstrlenW(lpSrch);
  520. wMatch = *lpSrch;
  521. for ( ; (lpFirst=StrChrW(lpFirst, wMatch))!=0 && !IntlStrEqNW(lpFirst, lpSrch, uLen);
  522. lpFirst++)
  523. continue; /* continue until we hit the end of the string or get a match */
  524. return((LPWSTR)lpFirst);
  525. }
  526. /*
  527. * StrChrI - Find first occurrence of character in string, case insensitive
  528. * Assumes lpStart points to start of null terminated string
  529. * wMatch is the character to match
  530. * returns ptr to the first occurrence of ch in str, NULL if not found.
  531. */
  532. LPSTR StrChrIA(LPCSTR lpStart, WORD wMatch)
  533. {
  534. wMatch = (UINT)(IsDBCSLeadByte(LOBYTE(wMatch)) ? wMatch : LOBYTE(wMatch));
  535. for ( ; *lpStart; lpStart = AnsiNext(lpStart))
  536. {
  537. if (!ChrCmpIA(READNATIVEWORD(lpStart), wMatch))
  538. return((LPSTR)lpStart);
  539. }
  540. return (NULL);
  541. }
  542. LPWSTR StrChrIW(LPCWSTR lpStart, WCHAR wMatch)
  543. {
  544. for ( ; *lpStart; lpStart++)
  545. {
  546. if (!ChrCmpIW(*lpStart, wMatch))
  547. return((LPWSTR)lpStart);
  548. }
  549. return (NULL);
  550. }
  551. /*
  552. * StrStrI - Search for first occurrence of a substring, case insensitive
  553. *
  554. * Assumes lpFirst points to source string
  555. * lpSrch points to string to search for
  556. * returns first occurrence of string if successful; NULL otherwise
  557. */
  558. LPSTR StrStrIA(LPCSTR lpFirst, LPCSTR lpSrch)
  559. {
  560. UINT uLen;
  561. WORD wMatch;
  562. uLen = (UINT)lstrlenA(lpSrch);
  563. wMatch = READNATIVEWORD(lpSrch);
  564. for ( ; (lpFirst = StrChrIA(lpFirst, wMatch)) != 0 && !IntlStrEqNIA(lpFirst, lpSrch, uLen);
  565. lpFirst=AnsiNext(lpFirst))
  566. continue; /* continue until we hit the end of the string or get a match */
  567. return((LPSTR)lpFirst);
  568. }
  569. LPWSTR StrStrIW(LPCWSTR lpFirst, LPCWSTR lpSrch)
  570. {
  571. UINT uLen;
  572. WCHAR wMatch;
  573. uLen = (UINT)lstrlenW(lpSrch);
  574. wMatch = *lpSrch;
  575. for ( ; (lpFirst = StrChrIW(lpFirst, wMatch)) != 0 && !IntlStrEqNIW(lpFirst, lpSrch, uLen);
  576. lpFirst++)
  577. continue; /* continue until we hit the end of the string or get a match */
  578. return((LPWSTR)lpFirst);
  579. }