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.

1166 lines
26 KiB

  1. //============================================================================
  2. //
  3. // DBCS aware string routines...
  4. //
  5. //
  6. //============================================================================
  7. //#if defined(UNIX) && !defined(UNICODE)
  8. //#define UNICODE
  9. //#endif
  10. #include "ctlspriv.h"
  11. #ifdef WINNT
  12. #include <winnlsp.h> // Get private NORM_ flag for StrEqIntl()
  13. #endif
  14. // for those of us who don't ssync to nt's build headers
  15. #ifndef NORM_STOP_ON_NULL
  16. #define NORM_STOP_ON_NULL 0x10000000
  17. #endif
  18. // WARNING: all of these APIs do not setup DS, so you can not access
  19. // any data in the default data seg of this DLL.
  20. //
  21. // do not create any global variables... talk to chrisg if you don't
  22. // understand thid
  23. #ifdef UNIX
  24. #ifdef BIG_ENDIAN
  25. #define READNATIVEWORD(x) MAKEWORD(*(char*)(x), *(char*)((char*)(x) + 1))
  26. #else
  27. #define READNATIVEWORD(x) MAKEWORD(*(char*)((char*)(x) + 1), *(char*)(x))
  28. #endif
  29. #else
  30. #define READNATIVEWORD(x) (*(UNALIGNED WORD *)x)
  31. #endif
  32. /*
  33. * StrEndN - Find the end of a string, but no more than n bytes
  34. * Assumes lpStart points to start of null terminated string
  35. * nBufSize is the maximum length
  36. * returns ptr to just after the last byte to be included
  37. */
  38. LPSTR lstrfns_StrEndNA(LPCSTR lpStart, int nBufSize)
  39. {
  40. LPCSTR lpEnd;
  41. for (lpEnd = lpStart + nBufSize; *lpStart && OFFSETOF(lpStart) < OFFSETOF(lpEnd);
  42. lpStart = AnsiNext(lpStart))
  43. continue; /* just getting to the end of the string */
  44. if (OFFSETOF(lpStart) > OFFSETOF(lpEnd))
  45. {
  46. /* We can only get here if the last byte before lpEnd was a lead byte
  47. */
  48. lpStart -= 2;
  49. }
  50. return((LPSTR)lpStart);
  51. }
  52. LPWSTR lstrfns_StrEndNW(LPCWSTR lpStart, int nBufSize)
  53. {
  54. #ifdef UNICODE
  55. LPCWSTR lpEnd;
  56. for (lpEnd = lpStart + nBufSize; *lpStart && (lpStart < lpEnd);
  57. lpStart++)
  58. continue; /* just getting to the end of the string */
  59. return((LPWSTR)lpStart);
  60. #else
  61. SetLastErrorEx(ERROR_CALL_NOT_IMPLEMENTED, SLE_WARNING);
  62. return NULL;
  63. #endif
  64. }
  65. // REVIEW WIN32 HACK - Convert to 32bit asm.
  66. #ifndef WIN32
  67. /*
  68. * ReverseScan - Find last occurrence of a byte in a string
  69. * Assumes lpSource points to first byte to check (end of the string)
  70. * uLen is the number of bytes to check
  71. * bMatch is the byte to match
  72. * returns ptr to the last occurrence of ch in str, NULL if not found.
  73. */
  74. LPSTR PASCAL ReverseScan(LPCSTR lpSource, UINT uLen, BYTE bMatch)
  75. {
  76. _asm
  77. {
  78. /* Load count */
  79. mov cx,uLen
  80. jcxz ReverseScanFail ; count is zero, return failure.
  81. /* Load up es:di, ax */
  82. les di,lpSource
  83. mov al,bMatch
  84. /* Set the direction flag based on bBackward
  85. * Perform the search; return 0 if we reached the end of the string
  86. * otherwise, return es:di+1
  87. */
  88. std
  89. repne scasb
  90. jne ReverseScanFail ; check result of last compare.
  91. inc di
  92. mov dx,es
  93. mov ax,di
  94. jmp ReverseScanExit
  95. ReverseScanFail:
  96. xor ax,ax
  97. xor dx,dx
  98. /* clear the direction flag and return
  99. */
  100. ReverseScanExit:
  101. cld
  102. }
  103. if (0) return 0; // suppress warning, optimized out
  104. }
  105. #endif
  106. /*
  107. * ChrCmp - Case sensitive character comparison for DBCS
  108. * Assumes w1, wMatch are characters to be compared
  109. * Return FALSE if they match, TRUE if no match
  110. */
  111. __inline BOOL ChrCmpA_inline(WORD w1, WORD wMatch)
  112. {
  113. /* Most of the time this won't match, so test it first for speed.
  114. */
  115. if (LOBYTE(w1) == LOBYTE(wMatch))
  116. {
  117. if (IsDBCSLeadByte(LOBYTE(w1)))
  118. {
  119. return(w1 != wMatch);
  120. }
  121. return FALSE;
  122. }
  123. return TRUE;
  124. }
  125. BOOL ChrCmpA(WORD w1, WORD wMatch)
  126. {
  127. return ChrCmpA_inline(w1, wMatch);
  128. }
  129. #ifdef UNICODE
  130. __inline BOOL ChrCmpW_inline(WCHAR w1, WCHAR wMatch)
  131. {
  132. return(!(w1 == wMatch));
  133. }
  134. #endif
  135. BOOL ChrCmpW(WCHAR w1, WCHAR wMatch)
  136. {
  137. #ifdef UNICODE
  138. return ChrCmpW_inline(w1, wMatch);
  139. #else
  140. SetLastErrorEx(ERROR_CALL_NOT_IMPLEMENTED, SLE_WARNING);
  141. return FALSE;
  142. #endif
  143. }
  144. /*
  145. * ChrCmpI - Case insensitive character comparison for DBCS
  146. * Assumes w1, wMatch are characters to be compared;
  147. * HIBYTE of wMatch is 0 if not a DBC
  148. * Return FALSE if match, TRUE if not
  149. */
  150. BOOL ChrCmpIA(WORD w1, WORD wMatch)
  151. {
  152. char sz1[3], sz2[3];
  153. if (IsDBCSLeadByte(sz1[0] = LOBYTE(w1)))
  154. {
  155. sz1[1] = HIBYTE(w1);
  156. sz1[2] = '\0';
  157. }
  158. else
  159. sz1[1] = '\0';
  160. #if defined(BIG_ENDIAN)
  161. sz2[0] = LOBYTE(wMatch);
  162. sz2[1] = HIBYTE(wMatch);
  163. #else
  164. *(WORD FAR *)sz2 = wMatch;
  165. #endif
  166. sz2[2] = '\0';
  167. return lstrcmpiA(sz1, sz2);
  168. }
  169. BOOL ChrCmpIW(WCHAR w1, WCHAR wMatch)
  170. {
  171. #ifdef UNICODE
  172. WCHAR sz1[2], sz2[2];
  173. sz1[0] = w1;
  174. sz1[1] = TEXT('\0');
  175. sz2[0] = wMatch;
  176. sz2[1] = TEXT('\0');
  177. return lstrcmpiW(sz1, sz2);
  178. #else
  179. SetLastErrorEx(ERROR_CALL_NOT_IMPLEMENTED, SLE_WARNING);
  180. return FALSE;
  181. #endif
  182. }
  183. LPWSTR StrCpyW(LPWSTR psz1, LPCWSTR psz2)
  184. {
  185. LPWSTR psz = psz1;
  186. do {
  187. *psz1 = *psz2;
  188. psz1++;
  189. } while(*psz2++);
  190. return psz;
  191. }
  192. LPWSTR StrCpyNW(LPWSTR psz1, LPCWSTR psz2, int cchMax)
  193. {
  194. LPWSTR psz = psz1;
  195. ASSERT(psz1);
  196. ASSERT(psz2);
  197. if (0 < cchMax)
  198. {
  199. // Leave room for the null terminator
  200. while (0 < --cchMax)
  201. {
  202. if ( !(*psz1++ = *psz2++) )
  203. break;
  204. }
  205. if (0 == cchMax)
  206. *psz1 = '\0';
  207. }
  208. return psz;
  209. }
  210. /*
  211. * StrChr - Find first occurrence of character in string
  212. * Assumes lpStart points to start of null terminated string
  213. * wMatch is the character to match
  214. * returns ptr to the first occurrence of ch in str, NULL if not found.
  215. */
  216. LPSTR FAR PASCAL StrChrA(LPCSTR lpStart, WORD wMatch)
  217. {
  218. for ( ; *lpStart; lpStart = AnsiNext(lpStart))
  219. {
  220. if (!ChrCmpA_inline(READNATIVEWORD(lpStart), wMatch))
  221. {
  222. return((LPSTR)lpStart);
  223. }
  224. }
  225. return (NULL);
  226. }
  227. #ifdef ALIGNMENT_SCENARIO
  228. LPWSTR StrChrSlowW(const UNALIGNED WCHAR *lpStart, WCHAR wMatch)
  229. {
  230. for ( ; *lpStart; lpStart++)
  231. {
  232. if (!ChrCmpW_inline(*lpStart, wMatch))
  233. {
  234. return((LPWSTR)lpStart);
  235. }
  236. }
  237. return NULL;
  238. }
  239. #endif
  240. LPWSTR FAR PASCAL StrChrW(LPCWSTR lpStart, WCHAR wMatch)
  241. {
  242. #ifdef UNICODE
  243. //
  244. // BUGBUG raymondc
  245. // Apparently, somebody is passing unaligned strings to StrChrW.
  246. // Find out who and make them stop.
  247. //
  248. ASSERT(!((ULONG_PTR)lpStart & 1)); // Assert alignedness
  249. #ifdef ALIGNMENT_SCENARIO
  250. //
  251. // Since unaligned strings arrive so rarely, put the slow
  252. // version in a separate function so the common case stays
  253. // fast. Believe it or not, we call StrChrW so often that
  254. // it is now a performance-sensitive function!
  255. //
  256. if ((ULONG_PTR)lpStart & 1)
  257. return StrChrSlowW(lpStart, wMatch);
  258. #endif
  259. for ( ; *lpStart; lpStart++)
  260. {
  261. if (!ChrCmpW_inline(*lpStart, wMatch))
  262. {
  263. return((LPWSTR)lpStart);
  264. }
  265. }
  266. return (NULL);
  267. #else
  268. SetLastErrorEx(ERROR_CALL_NOT_IMPLEMENTED, SLE_WARNING);
  269. return NULL;
  270. #endif
  271. }
  272. /*
  273. * StrRChr - Find last occurrence of character in string
  274. * Assumes lpStart points to start of string
  275. * lpEnd points to end of string (NOT included in search)
  276. * wMatch is the character to match
  277. * returns ptr to the last occurrence of ch in str, NULL if not found.
  278. */
  279. LPSTR FAR PASCAL StrRChrA(LPCSTR lpStart, LPCSTR lpEnd, WORD wMatch)
  280. {
  281. LPCSTR lpFound = NULL;
  282. if (!lpEnd)
  283. lpEnd = lpStart + lstrlenA(lpStart);
  284. for ( ; OFFSETOF(lpStart) < OFFSETOF(lpEnd); lpStart = AnsiNext(lpStart))
  285. {
  286. if (!ChrCmpA_inline(READNATIVEWORD(lpStart), wMatch))
  287. lpFound = lpStart;
  288. }
  289. return ((LPSTR)lpFound);
  290. }
  291. LPWSTR FAR PASCAL StrRChrW(LPCWSTR lpStart, LPCWSTR lpEnd, WCHAR wMatch)
  292. {
  293. #ifdef UNICODE
  294. LPCWSTR lpFound = NULL;
  295. if (!lpEnd)
  296. lpEnd = lpStart + lstrlenW(lpStart);
  297. for ( ; lpStart < lpEnd; lpStart++)
  298. {
  299. if (!ChrCmpW_inline(*lpStart, wMatch))
  300. lpFound = lpStart;
  301. }
  302. return ((LPWSTR)lpFound);
  303. #else
  304. SetLastErrorEx(ERROR_CALL_NOT_IMPLEMENTED, SLE_WARNING);
  305. return NULL;
  306. #endif
  307. }
  308. /*
  309. * StrRChrI - Find last occurrence of character in string, case insensitive
  310. * Assumes lpStart points to start of string
  311. * lpEnd points to end of string (NOT included in search)
  312. * wMatch is the character to match
  313. * returns ptr to the last occurrence of ch in str, NULL if not found.
  314. */
  315. LPSTR FAR PASCAL StrRChrIA(LPCSTR lpStart, LPCSTR lpEnd, WORD wMatch)
  316. {
  317. LPCSTR lpFound = NULL;
  318. if (!lpEnd)
  319. lpEnd = lpStart + lstrlenA(lpStart);
  320. wMatch = (UINT)(IsDBCSLeadByte(LOBYTE(wMatch)) ? wMatch : LOBYTE(wMatch));
  321. for ( ; OFFSETOF(lpStart) < OFFSETOF(lpEnd); lpStart = AnsiNext(lpStart))
  322. {
  323. if (!ChrCmpIA(READNATIVEWORD(lpStart), wMatch))
  324. lpFound = lpStart;
  325. }
  326. return ((LPSTR)lpFound);
  327. }
  328. LPWSTR FAR PASCAL StrRChrIW(LPCWSTR lpStart, LPCWSTR lpEnd, WCHAR wMatch)
  329. {
  330. #ifdef UNICODE
  331. LPCWSTR lpFound = NULL;
  332. if (!lpEnd)
  333. lpEnd = lpStart + lstrlenW(lpStart);
  334. for ( ; lpStart < lpEnd; lpStart++)
  335. {
  336. if (!ChrCmpIW(*lpStart, wMatch))
  337. lpFound = lpStart;
  338. }
  339. return ((LPWSTR)lpFound);
  340. #else
  341. SetLastErrorEx(ERROR_CALL_NOT_IMPLEMENTED, SLE_WARNING);
  342. return NULL;
  343. #endif
  344. }
  345. // StrCSpn: return index to first char of lpStr that is present in lpSet.
  346. // Includes the NUL in the comparison; if no lpSet chars are found, returns
  347. // the index to the NUL in lpStr.
  348. // Just like CRT strcspn.
  349. //
  350. int FAR PASCAL StrCSpnA(LPCSTR lpStr, LPCSTR lpSet)
  351. {
  352. // nature of the beast: O(lpStr*lpSet) work
  353. LPCSTR lp = lpStr;
  354. if (!lpStr || !lpSet)
  355. return 0;
  356. while (*lp)
  357. {
  358. if (StrChrA(lpSet, READNATIVEWORD(lp)))
  359. return (int)(lp-lpStr);
  360. lp = AnsiNext(lp);
  361. }
  362. return (int)(lp-lpStr); // ==lstrlen(lpStr)
  363. }
  364. int FAR PASCAL StrCSpnW(LPCWSTR lpStr, LPCWSTR lpSet)
  365. {
  366. #ifdef UNICODE
  367. // nature of the beast: O(lpStr*lpSet) work
  368. LPCWSTR lp = lpStr;
  369. if (!lpStr || !lpSet)
  370. return 0;
  371. while (*lp)
  372. {
  373. if (StrChrW(lpSet, *lp))
  374. return (int)(lp-lpStr);
  375. lp++;
  376. }
  377. return (int)(lp-lpStr); // ==lstrlen(lpStr)
  378. #else
  379. SetLastErrorEx(ERROR_CALL_NOT_IMPLEMENTED, SLE_WARNING);
  380. return -1;
  381. #endif
  382. }
  383. // StrCSpnI: case-insensitive version of StrCSpn.
  384. //
  385. int FAR PASCAL StrCSpnIA(LPCSTR lpStr, LPCSTR lpSet)
  386. {
  387. // nature of the beast: O(lpStr*lpSet) work
  388. LPCSTR lp = lpStr;
  389. if (!lpStr || !lpSet)
  390. return 0;
  391. while (*lp)
  392. {
  393. if (StrChrIA(lpSet, READNATIVEWORD(lp)))
  394. return (int)(lp-lpStr);
  395. lp = AnsiNext(lp);
  396. }
  397. return (int)(lp-lpStr); // ==lstrlen(lpStr)
  398. }
  399. int FAR PASCAL StrCSpnIW(LPCWSTR lpStr, LPCWSTR lpSet)
  400. {
  401. #ifdef UNICODE
  402. // nature of the beast: O(lpStr*lpSet) work
  403. LPCWSTR lp = lpStr;
  404. if (!lpStr || !lpSet)
  405. return 0;
  406. while (*lp)
  407. {
  408. if (StrChrIW(lpSet, *lp))
  409. return (int)(lp-lpStr);
  410. lp++;
  411. }
  412. return (int)(lp-lpStr); // ==lstrlen(lpStr)
  413. #else
  414. SetLastErrorEx(ERROR_CALL_NOT_IMPLEMENTED, SLE_WARNING);
  415. return -1;
  416. #endif
  417. }
  418. /*
  419. * StrCmpN - Compare n bytes
  420. *
  421. * returns See lstrcmp return values.
  422. * BUGBUG, won't work if source strings are in ROM
  423. */
  424. int FAR PASCAL StrCmpNA(LPCSTR lpStr1, LPCSTR lpStr2, int nChar)
  425. {
  426. char sz1[4];
  427. char sz2[4];
  428. LPCSTR lpszEnd = lpStr1 + nChar;
  429. //DebugMsg(DM_TRACE, "StrCmpN: %s %s %d returns:", lpStr1, lpStr2, nChar);
  430. for ( ; (lpszEnd > lpStr1) && (*lpStr1 || *lpStr2); lpStr1 = AnsiNext(lpStr1), lpStr2 = AnsiNext(lpStr2)) {
  431. WORD wMatch;
  432. wMatch = (WORD) (*lpStr2 | (*(lpStr2+1)<<8));
  433. if (ChrCmpA_inline(READNATIVEWORD(lpStr1), wMatch)) {
  434. int iRet;
  435. (*(WORD FAR *)sz1) = READNATIVEWORD(lpStr1);
  436. (*(WORD FAR *)sz2) = wMatch;
  437. #ifndef UNIX
  438. *AnsiNext(sz1) = 0;
  439. *AnsiNext(sz2) = 0;
  440. #else
  441. *AnsiNext((LPWORD)sz1) = 0;
  442. *AnsiNext((LPWORD)sz2) = 0;
  443. #endif
  444. iRet = lstrcmpA(sz1, sz2);
  445. //DebugMsg(DM_TRACE, ".................... %d", iRet);
  446. return iRet;
  447. }
  448. }
  449. //DebugMsg(DM_TRACE, ".................... 0");
  450. return 0;
  451. }
  452. int FAR PASCAL StrCmpNW(LPCWSTR lpStr1, LPCWSTR lpStr2, int nChar)
  453. {
  454. #ifdef UNICODE
  455. WCHAR sz1[2];
  456. WCHAR sz2[2];
  457. int i;
  458. LPCWSTR lpszEnd = lpStr1 + nChar;
  459. //DebugMsg(DM_TRACE, "StrCmpN: %s %s %d returns:", lpStr1, lpStr2, nChar);
  460. for ( ; (lpszEnd > lpStr1) && (*lpStr1 || *lpStr2); lpStr1++, lpStr2++) {
  461. i = ChrCmpW_inline(*lpStr1, *lpStr2);
  462. if (i) {
  463. int iRet;
  464. sz1[0] = *lpStr1;
  465. sz2[0] = *lpStr2;
  466. sz1[1] = TEXT('\0');
  467. sz2[1] = TEXT('\0');
  468. iRet = lstrcmpW(sz1, sz2);
  469. //DebugMsg(DM_TRACE, ".................... %d", iRet);
  470. return iRet;
  471. }
  472. }
  473. //DebugMsg(DM_TRACE, ".................... 0");
  474. return 0;
  475. #else
  476. SetLastErrorEx(ERROR_CALL_NOT_IMPLEMENTED, SLE_WARNING);
  477. return -1;
  478. #endif
  479. }
  480. /*
  481. * StrCmpNI - Compare n bytes, case insensitive
  482. *
  483. * returns See lstrcmpi return values.
  484. */
  485. #ifndef WINNT
  486. __inline BOOL IsAsciiA(char ch)
  487. {
  488. return !(ch & 0x80);
  489. }
  490. __inline char Ascii_ToLowerA(char ch)
  491. {
  492. return (ch >= 'A' && ch <= 'Z') ? (ch - 'A' + 'a') : ch;
  493. }
  494. #ifdef UNICODE
  495. __inline BOOL IsAsciiW(WCHAR ch)
  496. {
  497. return ch < 128;
  498. }
  499. __inline WCHAR Ascii_ToLowerW(WCHAR ch)
  500. {
  501. return (ch >= L'A' && ch <= L'Z') ? (ch - L'A' + L'a') : ch;
  502. }
  503. #endif
  504. int FAR PASCAL StrCmpNIA(LPCSTR lpStr1, LPCSTR lpStr2, int nChar)
  505. {
  506. int i;
  507. LPCSTR lpszEnd = lpStr1 + nChar;
  508. //DebugMsg(DM_TRACE, "StrCmpNI: %s %s %d returns:", lpStr1, lpStr2, nChar);
  509. for ( ; (lpszEnd > lpStr1) && (*lpStr1 || *lpStr2); (lpStr1 = AnsiNext(lpStr1)), (lpStr2 = AnsiNext(lpStr2))) {
  510. WORD wMatch;
  511. if (IsAsciiA(*lpStr1) && IsAsciiA(*lpStr2))
  512. {
  513. i = Ascii_ToLowerA(*lpStr1) - Ascii_ToLowerA(*lpStr2);
  514. }
  515. else
  516. {
  517. #ifndef UNIX
  518. wMatch = (UINT)(IsDBCSLeadByte(*lpStr2)) ? *(WORD FAR *)lpStr2 : (WORD)(BYTE)(*lpStr2);
  519. #else
  520. wMatch = (UINT)(IsDBCSLeadByte(*lpStr2)) ? (*lpStr2 | (*(lpStr2+1)<<8)) : (WORD)(BYTE)(*lpStr2);
  521. #endif
  522. i = ChrCmpIA(READNATIVEWORD(lpStr1), wMatch);
  523. }
  524. if (i) {
  525. //DebugMsg(DM_TRACE, ".................... %d", i);
  526. if (i < 0)
  527. {
  528. return -1;
  529. }
  530. else
  531. {
  532. return 1;
  533. }
  534. }
  535. }
  536. //DebugMsg(DM_TRACE, ".................... 0");
  537. return 0;
  538. }
  539. int FAR PASCAL StrCmpNIW(LPCWSTR lpStr1, LPCWSTR lpStr2, int nChar)
  540. {
  541. #ifdef UNICODE
  542. int i;
  543. LPCWSTR lpszEnd = lpStr1 + nChar;
  544. //DebugMsg(DM_TRACE, "StrCmpNI: %s %s %d returns:", lpStr1, lpStr2, nChar);
  545. for ( ; (lpszEnd > lpStr1) && (*lpStr1 || *lpStr2); lpStr1++, lpStr2++) {
  546. if (IsAsciiW(*lpStr1) && IsAsciiW(*lpStr2))
  547. {
  548. i = Ascii_ToLowerW(*lpStr1) - Ascii_ToLowerW(*lpStr2);
  549. }
  550. else
  551. {
  552. i = ChrCmpIW(*lpStr1, *lpStr2);
  553. }
  554. if (i) {
  555. //DebugMsg(DM_TRACE, ".................... %d", i);
  556. if (i < 0)
  557. {
  558. return -1;
  559. }
  560. else
  561. {
  562. return 1;
  563. }
  564. return i;
  565. }
  566. }
  567. //DebugMsg(DM_TRACE, ".................... 0");
  568. return 0;
  569. #else
  570. SetLastErrorEx(ERROR_CALL_NOT_IMPLEMENTED, SLE_WARNING);
  571. return -1;
  572. #endif
  573. }
  574. #else // WINNT
  575. int StrCmpNIA(LPCSTR lpStr1, LPCSTR lpStr2, int nChar)
  576. {
  577. int i;
  578. // Win95 doesn't support NORM_STOP_ON_NULL
  579. i = CompareStringA(GetThreadLocale(), NORM_IGNORECASE | NORM_STOP_ON_NULL,
  580. lpStr1, nChar, lpStr2, nChar);
  581. if (!i)
  582. {
  583. i = CompareStringA(LOCALE_SYSTEM_DEFAULT, NORM_IGNORECASE | NORM_STOP_ON_NULL,
  584. lpStr1, nChar, lpStr2, nChar);
  585. }
  586. return i - CSTR_EQUAL;
  587. }
  588. int StrCmpNIW(LPCWSTR lpStr1, LPCWSTR lpStr2, int nChar)
  589. {
  590. int i;
  591. // Win95 doesn't support NORM_STOP_ON_NULL
  592. i = CompareStringW(GetThreadLocale(), NORM_IGNORECASE | NORM_STOP_ON_NULL,
  593. lpStr1, nChar, lpStr2, nChar);
  594. if (!i)
  595. {
  596. i = CompareStringW(LOCALE_SYSTEM_DEFAULT, NORM_IGNORECASE | NORM_STOP_ON_NULL,
  597. lpStr1, nChar, lpStr2, nChar);
  598. }
  599. return i - CSTR_EQUAL;
  600. }
  601. #endif // WINNT
  602. /*
  603. * IntlStrEq
  604. *
  605. * returns TRUE if strings are equal, FALSE if not
  606. */
  607. BOOL IntlStrEqWorkerA(BOOL fCaseSens, LPCSTR lpString1, LPCSTR lpString2, int nChar) {
  608. int retval;
  609. DWORD dwFlags = fCaseSens ? LOCALE_USE_CP_ACP : (NORM_IGNORECASE | LOCALE_USE_CP_ACP);
  610. #ifdef WINNT
  611. //
  612. // On NT we can tell CompareString to stop at a '\0' if one is found before nChar chars
  613. //
  614. dwFlags |= NORM_STOP_ON_NULL;
  615. #else
  616. //
  617. // On Win9x we have to do the check manually
  618. //
  619. if (nChar != -1) {
  620. LPCSTR psz1, psz2;
  621. int cch = 0;
  622. psz1 = lpString1;
  623. psz2 = lpString2;
  624. while( *psz1 != '\0' && *psz2 != '\0' && cch < nChar) {
  625. #ifdef DBCS
  626. psz1 = CharNextA(psz1);
  627. psz2 = CharNextA(psz2);
  628. cch = min(psz1 - lpString1, psz2 - lpString2);
  629. #else
  630. psz1++;
  631. psz2++;
  632. cch++;
  633. #endif
  634. }
  635. // add one in for terminating '\0'
  636. cch++;
  637. if (cch < nChar) {
  638. nChar = cch;
  639. }
  640. }
  641. #endif
  642. retval = CompareStringA( GetThreadLocale(),
  643. dwFlags,
  644. lpString1,
  645. nChar,
  646. lpString2,
  647. nChar );
  648. if (retval == 0)
  649. {
  650. //
  651. // The caller is not expecting failure. Try the system
  652. // default locale id.
  653. //
  654. retval = CompareStringA( GetSystemDefaultLCID(),
  655. dwFlags,
  656. lpString1,
  657. nChar,
  658. lpString2,
  659. nChar );
  660. }
  661. if (retval == 0)
  662. {
  663. if (lpString1 && lpString2)
  664. {
  665. //
  666. // The caller is not expecting failure. We've never had a
  667. // failure indicator before. We'll do a best guess by calling
  668. // the C runtimes to do a non-locale sensitive compare.
  669. //
  670. if (fCaseSens)
  671. retval = StrCmpNA(lpString1, lpString2, nChar) + 2;
  672. else {
  673. retval = StrCmpNIA(lpString1, lpString2, nChar) + 2;
  674. }
  675. }
  676. else
  677. {
  678. retval = 2;
  679. }
  680. }
  681. return (retval == 2);
  682. }
  683. BOOL IntlStrEqWorkerW(BOOL fCaseSens, LPCWSTR lpString1, LPCWSTR lpString2, int nChar) {
  684. #ifdef UNICODE
  685. int retval;
  686. DWORD dwFlags = fCaseSens ? 0 : NORM_IGNORECASE;
  687. #ifdef WINNT
  688. //
  689. // On NT we can tell CompareString to stop at a '\0' if one is found before nChar chars
  690. //
  691. dwFlags |= NORM_STOP_ON_NULL;
  692. #else
  693. //
  694. // On Win9x we have to do the check manually
  695. //
  696. if (nChar != -1) {
  697. LPCWSTR psz1, psz2;
  698. int cch = 0;
  699. psz1 = lpString1;
  700. psz2 = lpString2;
  701. while( *psz1 != TEXT('\0') && *psz2 != TEXT('\0') && cch < nChar) {
  702. psz1++;
  703. psz2++;
  704. cch++;
  705. }
  706. // add one in for terminating '\0'
  707. cch++;
  708. if (cch < nChar) {
  709. nChar = cch;
  710. }
  711. }
  712. #endif
  713. retval = CompareStringW( GetThreadLocale(),
  714. dwFlags,
  715. lpString1,
  716. nChar,
  717. lpString2,
  718. nChar );
  719. if (retval == 0)
  720. {
  721. //
  722. // The caller is not expecting failure. Try the system
  723. // default locale id.
  724. //
  725. retval = CompareStringW( GetSystemDefaultLCID(),
  726. dwFlags,
  727. lpString1,
  728. nChar,
  729. lpString2,
  730. nChar );
  731. }
  732. if (retval == 0)
  733. {
  734. if (lpString1 && lpString2)
  735. {
  736. //
  737. // The caller is not expecting failure. We've never had a
  738. // failure indicator before. We'll do a best guess by calling
  739. // the C runtimes to do a non-locale sensitive compare.
  740. //
  741. if (fCaseSens)
  742. retval = StrCmpNW(lpString1, lpString2, nChar) + 2;
  743. else {
  744. retval = StrCmpNIW(lpString1, lpString2, nChar) + 2;
  745. }
  746. }
  747. else
  748. {
  749. retval = 2;
  750. }
  751. }
  752. return (retval == 2);
  753. #else
  754. SetLastErrorEx(ERROR_CALL_NOT_IMPLEMENTED, SLE_WARNING);
  755. return FALSE;
  756. #endif
  757. }
  758. /*
  759. * StrRStrI - Search for last occurrence of a substring
  760. *
  761. * Assumes lpSource points to the null terminated source string
  762. * lpLast points to where to search from in the source string
  763. * lpLast is not included in the search
  764. * lpSrch points to string to search for
  765. * returns last occurrence of string if successful; NULL otherwise
  766. */
  767. LPSTR FAR PASCAL StrRStrIA(LPCSTR lpSource, LPCSTR lpLast, LPCSTR lpSrch)
  768. {
  769. LPCSTR lpFound = NULL;
  770. LPSTR lpEnd;
  771. char cHold;
  772. if (!lpLast)
  773. lpLast = lpSource + lstrlenA(lpSource);
  774. if (lpSource >= lpLast || *lpSrch == 0)
  775. return NULL;
  776. lpEnd = lstrfns_StrEndNA(lpLast, (UINT)(lstrlenA(lpSrch)-1));
  777. cHold = *lpEnd;
  778. *lpEnd = 0;
  779. while ((lpSource = StrStrIA(lpSource, lpSrch))!=0 &&
  780. OFFSETOF(lpSource) < OFFSETOF(lpLast))
  781. {
  782. lpFound = lpSource;
  783. lpSource = AnsiNext(lpSource);
  784. }
  785. *lpEnd = cHold;
  786. return((LPSTR)lpFound);
  787. }
  788. LPWSTR FAR PASCAL StrRStrIW(LPCWSTR lpSource, LPCWSTR lpLast, LPCWSTR lpSrch)
  789. {
  790. #ifdef UNICODE
  791. LPCWSTR lpFound = NULL;
  792. LPWSTR lpEnd;
  793. WCHAR cHold;
  794. if (!lpLast)
  795. lpLast = lpSource + lstrlenW(lpSource);
  796. if (lpSource >= lpLast || *lpSrch == 0)
  797. return NULL;
  798. lpEnd = lstrfns_StrEndNW(lpLast, (UINT)(lstrlenW(lpSrch)-1));
  799. cHold = *lpEnd;
  800. *lpEnd = 0;
  801. while ((lpSource = StrStrIW(lpSource, lpSrch))!=0 &&
  802. lpSource < lpLast)
  803. {
  804. lpFound = lpSource;
  805. lpSource++;
  806. }
  807. *lpEnd = cHold;
  808. return((LPWSTR)lpFound);
  809. #else
  810. SetLastErrorEx(ERROR_CALL_NOT_IMPLEMENTED, SLE_WARNING);
  811. return NULL;
  812. #endif
  813. }
  814. /*
  815. * StrStr - Search for first occurrence of a substring
  816. *
  817. * Assumes lpSource points to source string
  818. * lpSrch points to string to search for
  819. * returns first occurrence of string if successful; NULL otherwise
  820. */
  821. LPSTR FAR PASCAL StrStrA(LPCSTR lpFirst, LPCSTR lpSrch)
  822. {
  823. UINT uLen;
  824. WORD wMatch;
  825. uLen = (UINT)lstrlenA(lpSrch);
  826. wMatch = READNATIVEWORD(lpSrch);
  827. for ( ; (lpFirst=StrChrA(lpFirst, wMatch))!=0 && !IntlStrEqNA(lpFirst, lpSrch, uLen);
  828. lpFirst=AnsiNext(lpFirst))
  829. continue; /* continue until we hit the end of the string or get a match */
  830. return((LPSTR)lpFirst);
  831. }
  832. LPWSTR FAR PASCAL StrStrW(LPCWSTR lpFirst, LPCWSTR lpSrch)
  833. {
  834. #ifdef UNICODE
  835. UINT uLen;
  836. WCHAR wMatch;
  837. uLen = (UINT)lstrlenW(lpSrch);
  838. wMatch = *lpSrch;
  839. for ( ; (lpFirst=StrChrW(lpFirst, wMatch))!=0 && !IntlStrEqNW(lpFirst, lpSrch, uLen);
  840. lpFirst++)
  841. continue; /* continue until we hit the end of the string or get a match */
  842. return((LPWSTR)lpFirst);
  843. #else
  844. SetLastErrorEx(ERROR_CALL_NOT_IMPLEMENTED, SLE_WARNING);
  845. return NULL;
  846. #endif
  847. }
  848. /*
  849. * StrChrI - Find first occurrence of character in string, case insensitive
  850. * Assumes lpStart points to start of null terminated string
  851. * wMatch is the character to match
  852. * returns ptr to the first occurrence of ch in str, NULL if not found.
  853. */
  854. LPSTR FAR PASCAL StrChrIA(LPCSTR lpStart, WORD wMatch)
  855. {
  856. wMatch = (UINT)(IsDBCSLeadByte(LOBYTE(wMatch)) ? wMatch : LOBYTE(wMatch));
  857. for ( ; *lpStart; lpStart = AnsiNext(lpStart))
  858. {
  859. if (!ChrCmpIA(READNATIVEWORD(lpStart), wMatch))
  860. return((LPSTR)lpStart);
  861. }
  862. return (NULL);
  863. }
  864. LPWSTR FAR PASCAL StrChrIW(LPCWSTR lpStart, WCHAR wMatch)
  865. {
  866. for ( ; *lpStart; lpStart++)
  867. {
  868. if (!ChrCmpIW(*lpStart, wMatch))
  869. return((LPWSTR)lpStart);
  870. }
  871. return (NULL);
  872. }
  873. /*
  874. * StrStrI - Search for first occurrence of a substring, case insensitive
  875. *
  876. * Assumes lpFirst points to source string
  877. * lpSrch points to string to search for
  878. * returns first occurrence of string if successful; NULL otherwise
  879. */
  880. LPSTR FAR PASCAL StrStrIA(LPCSTR lpFirst, LPCSTR lpSrch)
  881. {
  882. UINT uLen;
  883. WORD wMatch;
  884. uLen = (UINT)lstrlenA(lpSrch);
  885. wMatch = READNATIVEWORD(lpSrch);
  886. for ( ; (lpFirst = StrChrIA(lpFirst, wMatch)) != 0 && !IntlStrEqNIA(lpFirst, lpSrch, uLen);
  887. lpFirst=AnsiNext(lpFirst))
  888. continue; /* continue until we hit the end of the string or get a match */
  889. return((LPSTR)lpFirst);
  890. }
  891. LPWSTR FAR PASCAL StrStrIW(LPCWSTR lpFirst, LPCWSTR lpSrch)
  892. {
  893. #ifdef UNICODE
  894. UINT uLen;
  895. WCHAR wMatch;
  896. uLen = (UINT)lstrlenW(lpSrch);
  897. wMatch = *lpSrch;
  898. for ( ; (lpFirst = StrChrIW(lpFirst, wMatch)) != 0 && !IntlStrEqNIW(lpFirst, lpSrch, uLen);
  899. lpFirst++)
  900. continue; /* continue until we hit the end of the string or get a match */
  901. return((LPWSTR)lpFirst);
  902. #else
  903. SetLastErrorEx(ERROR_CALL_NOT_IMPLEMENTED, SLE_WARNING);
  904. return NULL;
  905. #endif
  906. }
  907. #ifndef UNICODE
  908. // TruncateString - bugbug: the same logic in shdocvw
  909. //
  910. // purpose: cut a string at the given length in dbcs safe manner
  911. // the string may be truncated at cch-1 if the sz[cch] points
  912. // to a lead byte that would result in cutting in the middle
  913. // of double byte character.
  914. // It is caller's responsibility to reserve enough buffer for
  915. // sz so we can put sz[cch]=0 safely.
  916. //
  917. // *Note this logic is not much perf hit when called with sbcs
  918. // string, as it just bails out at the tail character always.
  919. //
  920. void TruncateString(char *sz, int cchBufferSize)
  921. {
  922. int cch = cchBufferSize - 1;
  923. LPSTR psz = &sz[cch];
  924. if (!sz || cchBufferSize <= 0) return;
  925. while (psz > sz)
  926. {
  927. psz--;
  928. if (!IsDBCSLeadByte(*psz))
  929. {
  930. // Found non-leadbyte for the first time.
  931. // This is either a trail byte of double byte char
  932. // or a single byte character we've first seen.
  933. // Thus, the next pointer must be at either of a leadbyte
  934. // or &sz[cch]
  935. psz++;
  936. break;
  937. }
  938. }
  939. if (((&sz[cch] - psz) & 1) && cch > 0)
  940. {
  941. // we're truncating the string in the middle of dbcs
  942. cch--;
  943. }
  944. sz[cch] = '\0';
  945. return;
  946. }
  947. #endif