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.

2840 lines
77 KiB

  1. //============================================================================
  2. //
  3. // DBCS and UNICODE aware string routines
  4. //
  5. //
  6. //============================================================================
  7. #include "priv.h"
  8. #include "ids.h"
  9. #include <winnlsp.h> // Get private NORM_ flag for StrEqIntl()
  10. #include <shlobj.h> // STRRET LPCITEMIDLIST
  11. #ifdef _X86_
  12. #include <w95wraps.h>
  13. #endif
  14. #include <mluisupp.h>
  15. #include "varutil.h"
  16. BOOL UnicodeFromAnsi(LPWSTR *, LPCSTR, LPWSTR, int);
  17. #define IS_DIGITA(ch) InRange(ch, '0', '9')
  18. #define IS_DIGITW(ch) InRange(ch, L'0', L'9')
  19. #define DM_INTERVAL 0
  20. #ifdef UNIX
  21. #ifdef BIG_ENDIAN
  22. #define READNATIVEWORD(x) MAKEWORD(*(char*)(x), *(char*)((char*)(x) + 1))
  23. #else
  24. #define READNATIVEWORD(x) MAKEWORD(*(char*)((char*)(x) + 1), *(char*)(x))
  25. #endif
  26. #else
  27. #define READNATIVEWORD(x) (*(UNALIGNED WORD *)x)
  28. #endif
  29. __inline BOOL IsAsciiA(char ch)
  30. {
  31. return !(ch & 0x80);
  32. }
  33. __inline BOOL IsAsciiW(WCHAR ch)
  34. {
  35. return ch < 128;
  36. }
  37. __inline char Ascii_ToLowerA(char ch)
  38. {
  39. return (ch >= 'A' && ch <= 'Z') ? (ch - 'A' + 'a') : ch;
  40. }
  41. __inline WCHAR Ascii_ToLowerW(WCHAR ch)
  42. {
  43. return (ch >= L'A' && ch <= L'Z') ? (ch - L'A' + L'a') : ch;
  44. }
  45. // WARNING: all of these APIs do not setup DS, so you can not access
  46. // any data in the default data seg of this DLL.
  47. //
  48. // do not create any global variables... talk to chrisg if you don't
  49. // understand thid
  50. /*
  51. * StrEndN - Find the end of a string, but no more than n bytes
  52. * Assumes lpStart points to start of null terminated string
  53. * nBufSize is the maximum length
  54. * returns ptr to just after the last byte to be included
  55. */
  56. LPSTR
  57. lstrfns_StrEndNA(
  58. LPCSTR lpStart,
  59. int nBufSize)
  60. {
  61. LPCSTR lpEnd;
  62. for (lpEnd = lpStart + nBufSize; *lpStart && lpStart < lpEnd;
  63. lpStart = AnsiNext(lpStart))
  64. continue; /* just getting to the end of the string */
  65. if (lpStart > lpEnd)
  66. {
  67. /* We can only get here if the last byte before lpEnd was a lead byte
  68. */
  69. lpStart -= 2;
  70. }
  71. return((LPSTR)lpStart);
  72. }
  73. LPWSTR lstrfns_StrEndNW(LPCWSTR lpStart, int nBufSize)
  74. {
  75. LPCWSTR lpEnd;
  76. for (lpEnd = lpStart + nBufSize; *lpStart && (lpStart < lpEnd);
  77. lpStart++)
  78. continue; /* just getting to the end of the string */
  79. return((LPWSTR)lpStart);
  80. }
  81. /*
  82. * ChrCmp - Case sensitive character comparison for DBCS
  83. * Assumes w1, wMatch are characters to be compared
  84. * Return FALSE if they match, TRUE if no match
  85. */
  86. __inline BOOL ChrCmpA_inline(WORD w1, WORD wMatch)
  87. {
  88. /* Most of the time this won't match, so test it first for speed.
  89. */
  90. if (LOBYTE(w1) == LOBYTE(wMatch))
  91. {
  92. if (IsDBCSLeadByte(LOBYTE(w1)))
  93. {
  94. return(w1 != wMatch);
  95. }
  96. return FALSE;
  97. }
  98. return TRUE;
  99. }
  100. __inline BOOL ChrCmpW_inline(WCHAR w1, WCHAR wMatch)
  101. {
  102. return(!(w1 == wMatch));
  103. }
  104. /*
  105. * ChrCmpI - Case insensitive character comparison for DBCS
  106. * Assumes w1, wMatch are characters to be compared;
  107. * HIBYTE of wMatch is 0 if not a DBC
  108. * Return FALSE if match, TRUE if not
  109. */
  110. BOOL ChrCmpIA(WORD w1, WORD wMatch)
  111. {
  112. char sz1[3], sz2[3];
  113. if (IsDBCSLeadByte(sz1[0] = LOBYTE(w1)))
  114. {
  115. sz1[1] = HIBYTE(w1);
  116. sz1[2] = '\0';
  117. }
  118. else
  119. sz1[1] = '\0';
  120. #if defined(MWBIG_ENDIAN)
  121. sz2[0] = LOBYTE(wMatch);
  122. sz2[1] = HIBYTE(wMatch);
  123. #else
  124. *(WORD *)sz2 = wMatch;
  125. #endif
  126. sz2[2] = '\0';
  127. return lstrcmpiA(sz1, sz2);
  128. }
  129. BOOL ChrCmpIW(WCHAR w1, WCHAR wMatch)
  130. {
  131. WCHAR sz1[2], sz2[2];
  132. sz1[0] = w1;
  133. sz1[1] = '\0';
  134. sz2[0] = wMatch;
  135. sz2[1] = '\0';
  136. return StrCmpIW(sz1, sz2);
  137. }
  138. LPWSTR StrCpyW(LPWSTR pszDst, LPCWSTR pszSrc)
  139. {
  140. LPWSTR psz = pszDst;
  141. RIPMSG(NULL!=pszDst, "StrCpyW: Caller passed invalid pszDst");
  142. RIPMSG(pszSrc && IS_VALID_STRING_PTRW(pszSrc, -1), "StrCpyW: Caller passed invalid pszSrc");
  143. if (pszDst && pszSrc)
  144. {
  145. while (*pszDst++ = *pszSrc++)
  146. ;
  147. }
  148. return psz;
  149. }
  150. //*** StrCpyNX[AW] -- just like StrCpyN[AW], but returns ptr to EOS
  151. // NOTES
  152. // do we really need 'A' version? (for now we do for shell32 on 'old'
  153. // platforms that we test on but don't ship)
  154. LPSTR StrCpyNXA(LPSTR pszDst, LPCSTR pszSrc, int cchMax)
  155. {
  156. RIPMSG(cchMax >= 0, "StrCpyNXA: Caller passed bad cchMax");
  157. RIPMSG(cchMax < 0 || (pszDst && IS_VALID_WRITE_BUFFER(pszDst, char, cchMax)), "StrCpyNXA: Caller passed bad pszDst");
  158. RIPMSG(pszSrc && IS_VALID_STRING_PTRA(pszSrc, -1), "StrCpyNXA: Caller passed bad pszSrc");
  159. // NOTE: Cannot use DEBUGWhackPathBuffer before copying because src and
  160. // dest might overlap. Must delay whacking until after we're done.
  161. if (0 < cchMax)
  162. {
  163. if (!pszSrc)
  164. goto NullItOut;
  165. // Leave room for the null terminator
  166. while (0 < --cchMax)
  167. {
  168. if (!(*pszDst++ = *pszSrc++))
  169. {
  170. --pszDst;
  171. break;
  172. }
  173. }
  174. cchMax++;
  175. // in the cchMax>1 case, pszDst already points at the NULL, but reassigning it doesn't hurt
  176. NullItOut:
  177. // Whack the unused part of the buffer
  178. DEBUGWhackPathBufferA(pszDst, cchMax);
  179. *pszDst = '\0';
  180. }
  181. return pszDst;
  182. }
  183. LPWSTR StrCpyNXW(LPWSTR pszDst, LPCWSTR pszSrc, int cchMax)
  184. {
  185. RIPMSG(cchMax >= 0, "StrCpyNXW: Caller passed bad cchMax");
  186. RIPMSG(cchMax < 0 || (pszDst && IS_VALID_WRITE_BUFFER(pszDst, WCHAR, cchMax)), "StrCpyNXW: Caller passed bad pszDst");
  187. RIPMSG(pszSrc && IS_VALID_STRING_PTRW(pszSrc, -1), "StrCpyNXW: Caller passed bad pszSrc");
  188. // NOTE: Cannot use DEBUGWhackPathBuffer before copying because src and
  189. // dest might overlap. Must delay whacking until after we're done.
  190. if (0 < cchMax)
  191. {
  192. if (!pszSrc) // a test app passed in a NULL src ptr and we faulted, let's not fault here.
  193. goto NullItOut;
  194. // Leave room for the null terminator
  195. while (0 < --cchMax)
  196. {
  197. if (!(*pszDst++ = *pszSrc++))
  198. {
  199. --pszDst;
  200. break;
  201. }
  202. }
  203. cchMax++;
  204. // in the cchMax>1 case, pszDst already points at the NULL, but reassigning it doesn't hurt
  205. NullItOut:
  206. // Whack the unused part of the buffer
  207. DEBUGWhackPathBufferW(pszDst, cchMax);
  208. *pszDst = L'\0';
  209. }
  210. return pszDst;
  211. }
  212. LPWSTR StrCpyNW(LPWSTR pszDst, LPCWSTR pszSrc, int cchMax)
  213. {
  214. StrCpyNXW(pszDst, pszSrc, cchMax);
  215. return pszDst;
  216. }
  217. LPWSTR StrCatW(LPWSTR pszDst, LPCWSTR pszSrc)
  218. {
  219. LPWSTR psz = pszDst;
  220. RIPMSG(pszDst && IS_VALID_STRING_PTRW(pszDst, -1), "StrCatW: Caller passed invalid pszDst");
  221. RIPMSG(pszSrc && IS_VALID_STRING_PTRW(pszSrc, -1), "StrCatW: Caller passed invalid pszSrc");
  222. if (pszDst && pszSrc)
  223. {
  224. while (0 != *pszDst)
  225. pszDst++;
  226. while (*pszDst++ = *pszSrc++)
  227. ;
  228. }
  229. return psz;
  230. }
  231. LWSTDAPI_(LPWSTR) StrCatBuffW(LPWSTR pszDest, LPCWSTR pszSrc, int cchDestBuffSize)
  232. {
  233. RIPMSG(pszDest && IS_VALID_STRING_PTRW(pszDest, -1), "StrCatBuffW: Caller passed invalid pszDest");
  234. RIPMSG(pszSrc && IS_VALID_STRING_PTRW(pszSrc, -1), "StrCatBuffW: Caller passed invalid pszSrc");
  235. RIPMSG(cchDestBuffSize >= 0, "StrCatBuffW: Caller passed invalid cchDestBuffSize");
  236. RIPMSG(!(pszDest && IS_VALID_STRING_PTRW(pszDest, -1)) || cchDestBuffSize<0 || lstrlenW(pszDest)<cchDestBuffSize, "StrCatBuffW: Caller passed odd pszDest - string larger than cchDestBuffSize!");
  237. DEBUGWhackPathStringW(pszDest, cchDestBuffSize);
  238. if (pszDest && pszSrc)
  239. {
  240. LPWSTR psz = pszDest;
  241. // we walk forward till we find the end of pszDest, subtracting
  242. // from cchDestBuffSize as we go.
  243. while (*psz)
  244. {
  245. psz++;
  246. cchDestBuffSize--;
  247. }
  248. if (cchDestBuffSize > 0)
  249. {
  250. // call the shlwapi function here because win95 does not have lstrcpynW
  251. StrCpyNW(psz, pszSrc, cchDestBuffSize);
  252. }
  253. }
  254. return pszDest;
  255. }
  256. LWSTDAPI_(LPSTR) StrCatBuffA(LPSTR pszDest, LPCSTR pszSrc, int cchDestBuffSize)
  257. {
  258. RIPMSG(pszDest && IS_VALID_STRING_PTRA(pszDest, -1), "StrCatBuffA: Caller passed invalid pszDest");
  259. RIPMSG(pszSrc && IS_VALID_STRING_PTRA(pszSrc, -1), "StrCatBuffA: Caller passed invalid pszSrc");
  260. RIPMSG(cchDestBuffSize >= 0, "StrCatBuffA: Caller passed invalid cchDestBuffSize");
  261. RIPMSG(!(pszDest && IS_VALID_STRING_PTRA(pszDest, -1)) || cchDestBuffSize<0 || lstrlen(pszDest)<cchDestBuffSize, "StrCatBuffA: Caller passed odd pszDest - string larger than cchDestBuffSize!");
  262. DEBUGWhackPathStringA(pszDest, cchDestBuffSize);
  263. if (pszDest && pszSrc)
  264. {
  265. LPSTR psz = pszDest;
  266. // we walk forward till we find the end of pszDest, subtracting
  267. // from cchDestBuffSize as we go.
  268. while (*psz)
  269. {
  270. psz++;
  271. cchDestBuffSize--;
  272. }
  273. if (cchDestBuffSize > 0)
  274. {
  275. // Let kernel do the work for us.
  276. //
  277. // WARNING: We might generate a truncated DBCS sting becuase kernel's lstrcpynA
  278. // dosent check for this. Ask me if I care.
  279. lstrcpynA(psz, pszSrc, cchDestBuffSize);
  280. }
  281. }
  282. return pszDest;
  283. }
  284. /* StrNCat(front, back, count) - append count chars of back onto front
  285. */
  286. LPSTR StrNCatA(LPSTR front, LPCSTR back, int cchMax)
  287. {
  288. LPSTR start = front;
  289. RIPMSG(front && IS_VALID_STRING_PTRA(front, -1), "StrNCatA: Caller passed invalid front");
  290. RIPMSG(back && IS_VALID_STRING_PTRA(front, cchMax), "StrNCatA: Caller passed invalid back");
  291. RIPMSG(cchMax >= 0, "StrNCatA: Caller passed invalid cchMax");
  292. if (front && back)
  293. {
  294. while (*front++)
  295. ;
  296. front--;
  297. lstrcpyn(front, back, cchMax);
  298. }
  299. return(start);
  300. }
  301. LPWSTR StrNCatW(LPWSTR front, LPCWSTR back, int cchMax)
  302. {
  303. LPWSTR start = front;
  304. RIPMSG(front && IS_VALID_STRING_PTRW(front, -1), "StrNCatW: Caller passed invalid front");
  305. RIPMSG(back && IS_VALID_STRING_PTRW(front, cchMax), "StrNCatW: Caller passed invalid back");
  306. RIPMSG(cchMax >= 0, "StrNCatW: Caller passed invalid cchMax");
  307. if (front && back)
  308. {
  309. while (*front++)
  310. ;
  311. front--;
  312. StrCpyNW(front, back, cchMax);
  313. }
  314. return(start);
  315. }
  316. /*
  317. * StrChr - Find first occurrence of character in string
  318. * Assumes lpStart points to start of null terminated string
  319. * wMatch is the character to match
  320. * returns ptr to the first occurrence of ch in str, NULL if not found.
  321. */
  322. LPSTR _StrChrA(LPCSTR lpStart, WORD wMatch, BOOL fMBCS)
  323. {
  324. if (fMBCS) {
  325. for ( ; *lpStart; lpStart = AnsiNext(lpStart))
  326. {
  327. if (!ChrCmpA_inline(READNATIVEWORD(lpStart), wMatch))
  328. return((LPSTR)lpStart);
  329. }
  330. } else {
  331. for ( ; *lpStart; lpStart++)
  332. {
  333. if ((BYTE)*lpStart == LOBYTE(wMatch)) {
  334. return((LPSTR)lpStart);
  335. }
  336. }
  337. }
  338. return (NULL);
  339. }
  340. LPSTR StrChrA(LPCSTR lpStart, WORD wMatch)
  341. {
  342. CPINFO cpinfo;
  343. RIPMSG(lpStart && IS_VALID_STRING_PTR(lpStart, -1), "StrChrA: caller passed bad lpStart");
  344. if (!lpStart)
  345. return NULL;
  346. return _StrChrA(lpStart, wMatch, GetCPInfo(CP_ACP, &cpinfo) && cpinfo.LeadByte[0]);
  347. }
  348. #ifdef ALIGNMENT_SCENARIO
  349. LPWSTR StrChrSlowW(const UNALIGNED WCHAR *lpStart, WCHAR wMatch)
  350. {
  351. for ( ; *lpStart; lpStart++)
  352. {
  353. if (!ChrCmpW_inline(*lpStart, wMatch))
  354. {
  355. return((LPWSTR)lpStart);
  356. }
  357. }
  358. return NULL;
  359. }
  360. #endif
  361. LPWSTR StrChrW(LPCWSTR lpStart, WCHAR wMatch)
  362. {
  363. RIPMSG(lpStart && IS_VALID_STRING_PTRW(lpStart, -1), "StrChrW: caller passed bad lpStart");
  364. if (!lpStart)
  365. return NULL;
  366. //
  367. // raymondc
  368. // Apparently, somebody is passing unaligned strings to StrChrW.
  369. // Find out who and make them stop.
  370. //
  371. RIPMSG(!((ULONG_PTR)lpStart & 1), "StrChrW: caller passed UNALIGNED lpStart"); // Assert alignedness
  372. #ifdef ALIGNMENT_SCENARIO
  373. //
  374. // Since unaligned strings arrive so rarely, put the slow
  375. // version in a separate function so the common case stays
  376. // fast. Believe it or not, we call StrChrW so often that
  377. // it is now a performance-sensitive function!
  378. //
  379. if ((ULONG_PTR)lpStart & 1)
  380. return StrChrSlowW(lpStart, wMatch);
  381. #endif
  382. for ( ; *lpStart; lpStart++)
  383. {
  384. if (!ChrCmpW_inline(*lpStart, wMatch))
  385. {
  386. return((LPWSTR)lpStart);
  387. }
  388. }
  389. return (NULL);
  390. }
  391. /*
  392. * StrChrN - Find first occurrence of character in string
  393. * Assumes lpStart points to start of null terminated string
  394. * wMatch is the character to match
  395. * returns ptr to the first occurrence of ch in str, NULL if not found.
  396. */
  397. #ifdef ALIGNMENT_SCENARIO
  398. LPWSTR StrChrSlowNW(const UNALIGNED WCHAR *lpStart, WCHAR wMatch, UINT cchMax)
  399. {
  400. LPCWSTR lpSentinel = lpStart + cchMax;
  401. for ( ; *lpStart && lpStart < lpSentinel; lpStart++)
  402. {
  403. if (!ChrCmpW_inline(*lpStart, wMatch))
  404. {
  405. return((LPWSTR)lpStart);
  406. }
  407. }
  408. }
  409. #endif
  410. LPWSTR StrChrNW(LPCWSTR lpStart, WCHAR wMatch, UINT cchMax)
  411. {
  412. LPCWSTR lpSentinel = lpStart + cchMax;
  413. RIPMSG(lpStart && IS_VALID_STRING_PTRW(lpStart, -1), "StrChrNW: caller passed bad lpStart");
  414. if (!lpStart)
  415. return NULL;
  416. //
  417. // raymondc
  418. // Apparently, somebody is passing unaligned strings to StrChrW.
  419. // Find out who and make them stop.
  420. //
  421. RIPMSG(!((ULONG_PTR)lpStart & 1), "StrChrNW: caller passed UNALIGNED lpStart"); // Assert alignedness
  422. #ifdef ALIGNMENT_SCENARIO
  423. //
  424. // Since unaligned strings arrive so rarely, put the slow
  425. // version in a separate function so the common case stays
  426. // fast. Believe it or not, we call StrChrW so often that
  427. // it is now a performance-sensitive function!
  428. //
  429. if ((ULONG_PTR)lpStart & 1)
  430. return StrChrSlowNW(lpStart, wMatch, cchMax);
  431. #endif
  432. for ( ; *lpStart && lpStart<lpSentinel; lpStart++)
  433. {
  434. if (!ChrCmpW_inline(*lpStart, wMatch))
  435. {
  436. return((LPWSTR)lpStart);
  437. }
  438. }
  439. return (NULL);
  440. }
  441. /*
  442. * StrRChr - Find last occurrence of character in string
  443. * Assumes lpStart points to start of string
  444. * lpEnd points to end of string (NOT included in search)
  445. * wMatch is the character to match
  446. * returns ptr to the last occurrence of ch in str, NULL if not found.
  447. */
  448. LPSTR StrRChrA(LPCSTR lpStart, LPCSTR lpEnd, WORD wMatch)
  449. {
  450. LPCSTR lpFound = NULL;
  451. RIPMSG(lpStart && IS_VALID_STRING_PTR(lpStart, -1), "StrRChrA: caller passed bad lpStart");
  452. RIPMSG(!lpEnd || lpEnd <= lpStart + lstrlenA(lpStart), "StrRChrA: caller passed bad lpEnd");
  453. // don't need to check for NULL lpStart
  454. if (!lpEnd)
  455. lpEnd = lpStart + lstrlenA(lpStart);
  456. for ( ; lpStart < lpEnd; lpStart = AnsiNext(lpStart))
  457. {
  458. // (ChrCmp returns FALSE when characters match)
  459. if (!ChrCmpA_inline(READNATIVEWORD(lpStart), wMatch))
  460. lpFound = lpStart;
  461. }
  462. return ((LPSTR)lpFound);
  463. }
  464. LPWSTR StrRChrW(LPCWSTR lpStart, LPCWSTR lpEnd, WCHAR wMatch)
  465. {
  466. LPCWSTR lpFound = NULL;
  467. RIPMSG(lpStart && IS_VALID_STRING_PTRW(lpStart, -1), "StrRChrW: caller passed bad lpStart");
  468. RIPMSG(!lpEnd || lpEnd <= lpStart + lstrlenW(lpStart), "StrRChrW: caller passed bad lpEnd");
  469. // don't need to check for NULL lpStart
  470. if (!lpEnd)
  471. lpEnd = lpStart + lstrlenW(lpStart);
  472. for ( ; lpStart < lpEnd; lpStart++)
  473. {
  474. if (!ChrCmpW_inline(*lpStart, wMatch))
  475. lpFound = lpStart;
  476. }
  477. return ((LPWSTR)lpFound);
  478. }
  479. /*
  480. * StrChrI - Find first occurrence of character in string, case insensitive
  481. * Assumes lpStart points to start of null terminated string
  482. * wMatch is the character to match
  483. * returns ptr to the first occurrence of ch in str, NULL if not found.
  484. */
  485. LPSTR StrChrIA(LPCSTR lpStart, WORD wMatch)
  486. {
  487. RIPMSG(lpStart && IS_VALID_STRING_PTRA(lpStart, -1), "StrChrIA: caller passed bad lpStart");
  488. if (lpStart)
  489. {
  490. wMatch = (UINT)(IsDBCSLeadByte(LOBYTE(wMatch)) ? wMatch : LOBYTE(wMatch));
  491. for ( ; *lpStart; lpStart = AnsiNext(lpStart))
  492. {
  493. if (!ChrCmpIA(READNATIVEWORD(lpStart), wMatch))
  494. return((LPSTR)lpStart);
  495. }
  496. }
  497. return (NULL);
  498. }
  499. LPWSTR StrChrIW(LPCWSTR lpStart, WCHAR wMatch)
  500. {
  501. RIPMSG(lpStart && IS_VALID_STRING_PTRW(lpStart, -1), "StrChrIW: caller passed bad lpStart");
  502. if (lpStart)
  503. {
  504. for ( ; *lpStart; lpStart++)
  505. {
  506. if (!ChrCmpIW(*lpStart, wMatch))
  507. return((LPWSTR)lpStart);
  508. }
  509. }
  510. return (NULL);
  511. }
  512. /*
  513. * StrChrNI - Find first occurrence of character in string, case insensitive, counted
  514. *
  515. */
  516. LPWSTR StrChrNIW(LPCWSTR lpStart, WCHAR wMatch, UINT cchMax)
  517. {
  518. RIPMSG(lpStart && IS_VALID_STRING_PTRW(lpStart, -1), "StrChrNIW: caller passed bad lpStart");
  519. if (lpStart)
  520. {
  521. LPCWSTR lpSentinel = lpStart + cchMax;
  522. for ( ; *lpStart && lpStart < lpSentinel; lpStart++)
  523. {
  524. if (!ChrCmpIW(*lpStart, wMatch))
  525. return((LPWSTR)lpStart);
  526. }
  527. }
  528. return (NULL);
  529. }
  530. /*
  531. * StrRChrI - Find last occurrence of character in string, case insensitive
  532. * Assumes lpStart points to start of string
  533. * lpEnd points to end of string (NOT included in search)
  534. * wMatch is the character to match
  535. * returns ptr to the last occurrence of ch in str, NULL if not found.
  536. */
  537. LPSTR StrRChrIA(LPCSTR lpStart, LPCSTR lpEnd, WORD wMatch)
  538. {
  539. LPCSTR lpFound = NULL;
  540. RIPMSG(lpStart && IS_VALID_STRING_PTRA(lpStart, -1), "StrRChrIA: caller passed bad lpStart");
  541. RIPMSG(!lpEnd || lpEnd <= lpStart + lstrlenA(lpStart), "StrRChrIA: caller passed bad lpEnd");
  542. if (!lpEnd)
  543. lpEnd = lpStart + lstrlenA(lpStart);
  544. wMatch = (UINT)(IsDBCSLeadByte(LOBYTE(wMatch)) ? wMatch : LOBYTE(wMatch));
  545. for ( ; lpStart < lpEnd; lpStart = AnsiNext(lpStart))
  546. {
  547. if (!ChrCmpIA(READNATIVEWORD(lpStart), wMatch))
  548. lpFound = lpStart;
  549. }
  550. return ((LPSTR)lpFound);
  551. }
  552. LPWSTR StrRChrIW(LPCWSTR lpStart, LPCWSTR lpEnd, WCHAR wMatch)
  553. {
  554. LPCWSTR lpFound = NULL;
  555. RIPMSG(lpStart && IS_VALID_STRING_PTRW(lpStart, -1), "StrRChrIW: caller passed bad lpStart");
  556. RIPMSG(!lpEnd || lpEnd <= lpStart + lstrlenW(lpStart), "StrRChrIW: caller passed bad lpEnd");
  557. if (!lpEnd)
  558. lpEnd = lpStart + lstrlenW(lpStart);
  559. for ( ; lpStart < lpEnd; lpStart++)
  560. {
  561. if (!ChrCmpIW(*lpStart, wMatch))
  562. lpFound = lpStart;
  563. }
  564. return ((LPWSTR)lpFound);
  565. }
  566. /*----------------------------------------------------------
  567. Purpose: Returns a pointer to the first occurrence of a character
  568. in psz that belongs to the set of characters in pszSet.
  569. The search does not include the null terminator.
  570. If psz contains no characters that are in the set of
  571. characters in pszSet, this function returns NULL.
  572. This function is DBCS-safe.
  573. Returns: see above
  574. Cond: --
  575. */
  576. LPSTR StrPBrkA(LPCSTR psz, LPCSTR pszSet)
  577. {
  578. RIPMSG(psz && IS_VALID_STRING_PTRA(psz, -1), "StrPBrkA: caller passed bad psz");
  579. RIPMSG(pszSet && IS_VALID_STRING_PTRA(pszSet, -1), "StrPBrkA: caller passed bad pszSet");
  580. if (psz && pszSet)
  581. {
  582. while (*psz)
  583. {
  584. LPCSTR pszSetT;
  585. for (pszSetT = pszSet; *pszSetT; pszSetT = CharNextA(pszSetT))
  586. {
  587. if (*psz == *pszSetT)
  588. {
  589. // Found first character that matches
  590. return (LPSTR)psz; // Const -> non-const
  591. }
  592. }
  593. psz = CharNextA(psz);
  594. }
  595. }
  596. return NULL;
  597. }
  598. /*----------------------------------------------------------
  599. Purpose: Returns a pointer to the first occurrence of a character
  600. in psz that belongs to the set of characters in pszSet.
  601. The search does not include the null terminator.
  602. Returns: see above
  603. Cond: --
  604. */
  605. LPWSTR WINAPI StrPBrkW(LPCWSTR psz, LPCWSTR pszSet)
  606. {
  607. RIPMSG(psz && IS_VALID_STRING_PTRW(psz, -1), "StrPBrkA: caller passed bad psz");
  608. RIPMSG(pszSet && IS_VALID_STRING_PTRW(pszSet, -1), "StrPBrkA: caller passed bad pszSet");
  609. if (psz && pszSet)
  610. {
  611. while (*psz)
  612. {
  613. LPCWSTR pszSetT;
  614. for (pszSetT = pszSet; *pszSetT; pszSetT++)
  615. {
  616. if (*psz == *pszSetT)
  617. {
  618. // Found first character that matches
  619. return (LPWSTR)psz; // Const -> non-const
  620. }
  621. }
  622. psz++;
  623. }
  624. }
  625. return NULL;
  626. }
  627. int WINAPI StrToIntA(LPCSTR lpSrc)
  628. {
  629. RIPMSG(lpSrc && IS_VALID_STRING_PTRA(lpSrc, -1), "StrToIntA: Caller passed bad lpSrc");
  630. if (lpSrc)
  631. {
  632. int n = 0;
  633. BOOL bNeg = FALSE;
  634. if (*lpSrc == '-')
  635. {
  636. bNeg = TRUE;
  637. lpSrc++;
  638. }
  639. while (IS_DIGITA(*lpSrc))
  640. {
  641. n *= 10;
  642. n += *lpSrc - '0';
  643. lpSrc++;
  644. }
  645. return bNeg ? -n : n;
  646. }
  647. return 0;
  648. }
  649. int WINAPI StrToIntW(LPCWSTR lpSrc)
  650. {
  651. RIPMSG(lpSrc && IS_VALID_STRING_PTRW(lpSrc, -1), "StrToIntW: Caller passed bad lpSrc");
  652. if (lpSrc)
  653. {
  654. int n = 0;
  655. BOOL bNeg = FALSE;
  656. if (*lpSrc == L'-')
  657. {
  658. bNeg = TRUE;
  659. lpSrc++;
  660. }
  661. while (IS_DIGITW(*lpSrc))
  662. {
  663. n *= 10;
  664. n += *lpSrc - L'0';
  665. lpSrc++;
  666. }
  667. return bNeg ? -n : n;
  668. }
  669. return 0;
  670. }
  671. /*----------------------------------------------------------
  672. Purpose: Special verion of atoi. Supports hexadecimal too.
  673. If this function returns FALSE, *phRet is set to 0.
  674. Returns: TRUE if the string is a number, or contains a partial number
  675. FALSE if the string is not a number
  676. dwFlags are STIF_ bitfield
  677. Cond: --
  678. */
  679. BOOL WINAPI StrToInt64ExW(LPCWSTR pszString, DWORD dwFlags, LONGLONG *pllRet)
  680. {
  681. BOOL bRet;
  682. RIPMSG(pszString && IS_VALID_STRING_PTRW(pszString, -1), "StrToInt64ExW: caller passed bad pszString");
  683. if (pszString)
  684. {
  685. LONGLONG n;
  686. BOOL bNeg = FALSE;
  687. LPCWSTR psz;
  688. LPCWSTR pszAdj;
  689. // Skip leading whitespace
  690. //
  691. for (psz = pszString; *psz == L' ' || *psz == L'\n' || *psz == L'\t'; psz++)
  692. ;
  693. // Determine possible explicit signage
  694. //
  695. if (*psz == L'+' || *psz == L'-')
  696. {
  697. bNeg = (*psz == L'+') ? FALSE : TRUE;
  698. psz++;
  699. }
  700. // Or is this hexadecimal?
  701. //
  702. pszAdj = psz+1;
  703. if ((STIF_SUPPORT_HEX & dwFlags) &&
  704. *psz == L'0' && (*pszAdj == L'x' || *pszAdj == L'X'))
  705. {
  706. // Yes
  707. // (Never allow negative sign with hexadecimal numbers)
  708. bNeg = FALSE;
  709. psz = pszAdj+1;
  710. pszAdj = psz;
  711. // Do the conversion
  712. //
  713. for (n = 0; ; psz++)
  714. {
  715. if (IS_DIGITW(*psz))
  716. n = 0x10 * n + *psz - L'0';
  717. else
  718. {
  719. WCHAR ch = *psz;
  720. int n2;
  721. if (ch >= L'a')
  722. ch -= L'a' - L'A';
  723. n2 = ch - L'A' + 0xA;
  724. if (n2 >= 0xA && n2 <= 0xF)
  725. n = 0x10 * n + n2;
  726. else
  727. break;
  728. }
  729. }
  730. // Return TRUE if there was at least one digit
  731. bRet = (psz != pszAdj);
  732. }
  733. else
  734. {
  735. // No
  736. pszAdj = psz;
  737. // Do the conversion
  738. for (n = 0; IS_DIGITW(*psz); psz++)
  739. n = 10 * n + *psz - L'0';
  740. // Return TRUE if there was at least one digit
  741. bRet = (psz != pszAdj);
  742. }
  743. if (pllRet)
  744. {
  745. *pllRet = bNeg ? -n : n;
  746. }
  747. }
  748. else
  749. {
  750. bRet = FALSE;
  751. }
  752. return bRet;
  753. }
  754. /*----------------------------------------------------------
  755. Purpose: ansi wrapper for StrToInt64ExW.
  756. Returns: see StrToInt64ExW
  757. Cond: --
  758. */
  759. BOOL WINAPI StrToInt64ExA(
  760. LPCSTR pszString,
  761. DWORD dwFlags, // STIF_ bitfield
  762. LONGLONG FAR * pllRet)
  763. {
  764. BOOL bRet;
  765. RIPMSG(pszString && IS_VALID_STRING_PTRA(pszString, -1), "StrToInt64ExA: caller passed bad pszString");
  766. if (pszString)
  767. {
  768. // Most strings will simply use this temporary buffer, but UnicodeFromAnsi
  769. // will allocate a buffer if the supplied string is bigger.
  770. WCHAR szBuf[MAX_PATH];
  771. LPWSTR pwszString;
  772. bRet = UnicodeFromAnsi(&pwszString, pszString, szBuf, SIZECHARS(szBuf));
  773. if (bRet)
  774. {
  775. bRet = StrToInt64ExW(pwszString, dwFlags, pllRet);
  776. UnicodeFromAnsi(&pwszString, NULL, szBuf, 0);
  777. }
  778. }
  779. else
  780. {
  781. bRet = FALSE;
  782. }
  783. return bRet;
  784. }
  785. /*----------------------------------------------------------
  786. Purpose: Calls StrToInt64ExA (the real work horse), and
  787. then casts down to an int.
  788. Returns: see StrToInt64ExA
  789. */
  790. BOOL WINAPI StrToIntExA(
  791. LPCSTR pszString,
  792. DWORD dwFlags,
  793. int *piRet)
  794. {
  795. LONGLONG llVal;
  796. BOOL fReturn;
  797. RIPMSG(pszString && IS_VALID_STRING_PTRA(pszString, -1), "StrToIntExA: caller passed bad pszString");
  798. fReturn = StrToInt64ExA(pszString, dwFlags, &llVal);
  799. *piRet = fReturn ? (int)llVal : 0;
  800. return(fReturn);
  801. }
  802. /*----------------------------------------------------------
  803. Purpose: Calls StrToInt64ExW (the real work horse), and
  804. then casts down to an int.
  805. Returns: see StrToInt64ExW
  806. */
  807. BOOL WINAPI StrToIntExW(
  808. LPCWSTR pwszString,
  809. DWORD dwFlags, // STIF_ bitfield
  810. int FAR * piRet)
  811. {
  812. LONGLONG llVal;
  813. BOOL fReturn;
  814. RIPMSG(pwszString && IS_VALID_STRING_PTRW(pwszString, -1), "StrToIntExW: caller passed bad pwszString");
  815. fReturn = StrToInt64ExW(pwszString, dwFlags, &llVal);
  816. *piRet = fReturn ? (int)llVal : 0;
  817. return(fReturn);
  818. }
  819. /*----------------------------------------------------------
  820. Purpose: Returns an integer value specifying the length of
  821. the substring in psz that consists entirely of
  822. characters in pszSet. If psz begins with a character
  823. not in pszSet, then this function returns 0.
  824. This is a DBCS-safe version of the CRT strspn().
  825. Returns: see above
  826. Cond: --
  827. */
  828. int StrSpnA(LPCSTR psz, LPCSTR pszSet)
  829. {
  830. LPCSTR pszT = psz;
  831. RIPMSG(psz && IS_VALID_STRING_PTRA(psz, -1), "StrSpnA: caller passed bad psz");
  832. RIPMSG(pszSet && IS_VALID_STRING_PTRA(pszSet, -1), "StrSpnA: caller passed bad pszSet");
  833. if (psz && pszSet)
  834. {
  835. // Go thru the string to be inspected
  836. for ( ; *pszT; pszT = CharNextA(pszT))
  837. {
  838. LPCSTR pszSetT;
  839. // Go thru the char set
  840. for (pszSetT = pszSet; *pszSetT; pszSetT = CharNextA(pszSetT))
  841. {
  842. if (*pszSetT == *pszT)
  843. {
  844. if ( !IsDBCSLeadByte(*pszSetT) )
  845. {
  846. break; // Chars match
  847. }
  848. else if (pszSetT[1] == pszT[1])
  849. {
  850. break; // Chars match
  851. }
  852. }
  853. }
  854. // End of char set?
  855. if (0 == *pszSetT)
  856. {
  857. break; // Yes, no match on this inspected char
  858. }
  859. }
  860. }
  861. return (int)(pszT - psz);
  862. }
  863. /*----------------------------------------------------------
  864. Purpose: Returns an integer value specifying the length of
  865. the substring in psz that consists entirely of
  866. characters in pszSet. If psz begins with a character
  867. not in pszSet, then this function returns 0.
  868. This is a DBCS-safe version of the CRT strspn().
  869. Returns: see above
  870. Cond: --
  871. */
  872. STDAPI_(int) StrSpnW(LPCWSTR psz, LPCWSTR pszSet)
  873. {
  874. LPCWSTR pszT = psz;
  875. RIPMSG(psz && IS_VALID_STRING_PTRW(psz, -1), "StrSpnW: caller passed bad psz");
  876. RIPMSG(pszSet && IS_VALID_STRING_PTRW(pszSet, -1), "StrSpnW: caller passed bad pszSet");
  877. if (psz && pszSet)
  878. {
  879. // Go thru the string to be inspected
  880. for ( ; *pszT; pszT++)
  881. {
  882. LPCWSTR pszSetT;
  883. // Go thru the char set
  884. for (pszSetT = pszSet; *pszSetT != *pszT; pszSetT++)
  885. {
  886. if (0 == *pszSetT)
  887. {
  888. // Reached end of char set without finding a match
  889. return (int)(pszT - psz);
  890. }
  891. }
  892. }
  893. }
  894. return (int)(pszT - psz);
  895. }
  896. // StrCSpn: return index to first char of lpStr that is present in lpSet.
  897. // Includes the NUL in the comparison; if no lpSet chars are found, returns
  898. // the index to the NUL in lpStr.
  899. // Just like CRT strcspn.
  900. //
  901. int StrCSpnA(LPCSTR lpStr, LPCSTR lpSet)
  902. {
  903. LPCSTR lp = lpStr;
  904. RIPMSG(lpStr && IS_VALID_STRING_PTRA(lpStr, -1), "StrCSpnA: Caller passed bad lpStr");
  905. RIPMSG(lpSet && IS_VALID_STRING_PTRA(lpSet, -1), "StrCSpnA: Caller passed bad lpSet");
  906. if (lpStr && lpSet)
  907. {
  908. // nature of the beast: O(lpStr*lpSet) work
  909. while (*lp)
  910. {
  911. if (StrChrA(lpSet, READNATIVEWORD(lp)))
  912. return (int)(lp-lpStr);
  913. lp = AnsiNext(lp);
  914. }
  915. }
  916. return (int)(lp-lpStr); // ==lstrlen(lpStr)
  917. }
  918. int StrCSpnW(LPCWSTR lpStr, LPCWSTR lpSet)
  919. {
  920. LPCWSTR lp = lpStr;
  921. RIPMSG(lpStr && IS_VALID_STRING_PTRW(lpStr, -1), "StrCSpnW: Caller passed bad lpStr");
  922. RIPMSG(lpSet && IS_VALID_STRING_PTRW(lpSet, -1), "StrCSpnW: Caller passed bad lpSet");
  923. if (lpStr && lpSet)
  924. {
  925. // nature of the beast: O(lpStr*lpSet) work
  926. while (*lp)
  927. {
  928. if (StrChrW(lpSet, *lp))
  929. return (int)(lp-lpStr);
  930. lp++;
  931. }
  932. }
  933. return (int)(lp-lpStr); // ==lstrlen(lpStr)
  934. }
  935. // StrCSpnI: case-insensitive version of StrCSpn.
  936. //
  937. int StrCSpnIA(LPCSTR lpStr, LPCSTR lpSet)
  938. {
  939. LPCSTR lp = lpStr;
  940. RIPMSG(lpStr && IS_VALID_STRING_PTRA(lpStr, -1), "StrCSpnIA: Caller passed bad lpStr");
  941. RIPMSG(lpSet && IS_VALID_STRING_PTRA(lpSet, -1), "StrCSpnIA: Caller passed bad lpSet");
  942. if (lpStr && lpSet)
  943. {
  944. // nature of the beast: O(lpStr*lpSet) work
  945. while (*lp)
  946. {
  947. if (StrChrIA(lpSet, READNATIVEWORD(lp)))
  948. return (int)(lp-lpStr);
  949. lp = AnsiNext(lp);
  950. }
  951. }
  952. return (int)(lp-lpStr); // ==lstrlen(lpStr)
  953. }
  954. int StrCSpnIW(LPCWSTR lpStr, LPCWSTR lpSet)
  955. {
  956. LPCWSTR lp = lpStr;
  957. RIPMSG(lpStr && IS_VALID_STRING_PTRW(lpStr, -1), "StrCSpnIW: Caller passed bad lpStr");
  958. RIPMSG(lpSet && IS_VALID_STRING_PTRW(lpSet, -1), "StrCSpnIW: Caller passed bad lpSet");
  959. if (lpStr && lpSet)
  960. {
  961. // nature of the beast: O(lpStr*lpSet) work
  962. while (*lp)
  963. {
  964. if (StrChrIW(lpSet, *lp))
  965. return (int)(lp-lpStr);
  966. lp++;
  967. }
  968. }
  969. return (int)(lp-lpStr); // ==lstrlen(lpStr)
  970. }
  971. /*
  972. * StrCmpN - Compare n bytes
  973. *
  974. * returns See lstrcmp return values.
  975. */
  976. int _StrCmpNA(LPCSTR lpStr1, LPCSTR lpStr2, int nChar, BOOL fMBCS)
  977. {
  978. if (lpStr1 && lpStr2)
  979. {
  980. LPCSTR lpszEnd = lpStr1 + nChar;
  981. char sz1[4];
  982. char sz2[4];
  983. if (fMBCS) {
  984. for ( ; (lpszEnd > lpStr1) && (*lpStr1 || *lpStr2); lpStr1 = AnsiNext(lpStr1), lpStr2 = AnsiNext(lpStr2)) {
  985. WORD w1;
  986. WORD w2;
  987. // If either pointer is at the null terminator already,
  988. // we want to copy just one byte to make sure we don't read
  989. // past the buffer (might be at a page boundary).
  990. w1 = (*lpStr1) ? READNATIVEWORD(lpStr1) : 0;
  991. w2 = (*lpStr2) ? READNATIVEWORD(lpStr2) : 0;
  992. // (ChrCmpA returns FALSE if the characters match)
  993. // Do the characters match?
  994. if (ChrCmpA_inline(w1, w2))
  995. {
  996. // No; determine the lexical value of the comparison
  997. // (since ChrCmp just returns true/false).
  998. // Since the character may be a DBCS character; we
  999. // copy two bytes into each temporary buffer
  1000. // (in preparation for the lstrcmp call).
  1001. (*(WORD *)sz1) = w1;
  1002. (*(WORD *)sz2) = w2;
  1003. // Add null terminators to temp buffers
  1004. *AnsiNext(sz1) = 0;
  1005. *AnsiNext(sz2) = 0;
  1006. return lstrcmpA(sz1, sz2);
  1007. }
  1008. }
  1009. } else {
  1010. for ( ; (lpszEnd > lpStr1) && (*lpStr1 || *lpStr2); lpStr1++, lpStr2++) {
  1011. if (*lpStr1 != *lpStr2) {
  1012. // No; determine the lexical value of the comparison
  1013. // (since ChrCmp just returns true/false).
  1014. sz1[0] = *lpStr1;
  1015. sz2[0] = *lpStr2;
  1016. sz1[1] = sz2[1] = '\0';
  1017. return lstrcmpA(sz1, sz2);
  1018. }
  1019. }
  1020. }
  1021. }
  1022. return 0;
  1023. }
  1024. STDAPI_(int) StrCmpNA(LPCSTR psz1, LPCSTR psz2, int nChar)
  1025. {
  1026. CPINFO cpinfo;
  1027. RIPMSG(nChar == 0 || (psz1 && IS_VALID_STRING_PTRA(psz1, nChar)), "StrCmpNA: Caller passed bad psz1");
  1028. RIPMSG(nChar == 0 || (psz2 && IS_VALID_STRING_PTRA(psz2, nChar)), "StrCmpNA: Caller passed bad psz2");
  1029. RIPMSG(nChar >= 0, "StrCmpNA: caller passed bad nChar");
  1030. return _StrCmpNA(psz1, psz2, nChar, GetCPInfo(CP_ACP, &cpinfo) && cpinfo.LeadByte[0]);
  1031. }
  1032. // cch1 and cch2 are the maximum # of chars to compare
  1033. int _StrCmpLocaleW(DWORD dwFlags, LPCWSTR psz1, int cch1, LPCWSTR psz2, int cch2)
  1034. {
  1035. int i = CompareStringW(GetThreadLocale(), dwFlags, psz1, cch1, psz2, cch2);
  1036. if (!i)
  1037. {
  1038. i = CompareStringW(LOCALE_SYSTEM_DEFAULT, dwFlags, psz1, cch1, psz2, cch2);
  1039. }
  1040. return i - CSTR_EQUAL;
  1041. }
  1042. int _StrCmpLocaleA(DWORD dwFlags, LPCSTR psz1, int cch1, LPCSTR psz2, int cch2)
  1043. {
  1044. int i = CompareStringA(GetThreadLocale(), dwFlags, psz1, cch1, psz2, cch2);
  1045. if (!i)
  1046. {
  1047. i = CompareStringA(LOCALE_SYSTEM_DEFAULT, dwFlags, psz1, cch1, psz2, cch2);
  1048. }
  1049. return i - CSTR_EQUAL;
  1050. }
  1051. STDAPI_(int) StrCmpNW(LPCWSTR psz1, LPCWSTR psz2, int nChar)
  1052. {
  1053. RIPMSG(nChar==0 || (psz1 && IS_VALID_STRING_PTRW(psz1, nChar)), "StrCmpNW: Caller passed bad psz1");
  1054. RIPMSG(nChar==0 || (psz2 && IS_VALID_STRING_PTRW(psz2, nChar)), "StrCmpNW: Caller passed bad psz2");
  1055. RIPMSG(nChar>=0, "StrCmpNA: caller passed bad nChar");
  1056. return _StrCmpLocaleW(NORM_STOP_ON_NULL, psz1, nChar, psz2, nChar);
  1057. }
  1058. /*
  1059. * Compare n bytes, case insensitive
  1060. *
  1061. * returns See lstrcmpi return values.
  1062. */
  1063. int StrCmpNIA(LPCSTR psz1, LPCSTR psz2, int nChar)
  1064. {
  1065. RIPMSG(nChar==0 || (psz1 && IS_VALID_STRING_PTRA(psz1, nChar)), "StrCmpNIA: Caller passed bad psz1");
  1066. RIPMSG(nChar==0 || (psz2 && IS_VALID_STRING_PTRA(psz2, nChar)), "StrCmpNIA: Caller passed bad psz2");
  1067. RIPMSG(nChar>=0, "StrCmpNIA: caller passed bad nChar");
  1068. // Include the (nChar && (!psz1 || !psz2)) cases here so we go through the
  1069. // validation layer and return the appropriate invalid parameter error code
  1070. // instead of faulting on Win95.
  1071. //
  1072. // NOTE! That this means that StrCmpNI(NULL, NULL, 0) on NT returns -2
  1073. // but StrCmpNI(NULL, NULL, 0) on Win9x returns 0. This has always been
  1074. // the case -- changing it is too scary for app compat reasons.
  1075. //
  1076. // Actually neither Win95 nor NT support NORM_STOP_ON_NULL for
  1077. // StrCmpLocaleA. Unfortunately, the failure modes are different
  1078. // so we still have to be careful.
  1079. //
  1080. if (g_bRunningOnNT)
  1081. {
  1082. int nChar1, nChar2;
  1083. if (nChar && (!psz1 || !psz2))
  1084. {
  1085. // This is the error scenario we are forcing through
  1086. nChar1 = nChar;
  1087. nChar2 = nChar;
  1088. }
  1089. else
  1090. {
  1091. // nChar1 = min(nChar, lstrlen(psz1))
  1092. // except that the "for" loop will not read more than nChar
  1093. // characters from psz1 because psz1 might not be NULL-terminated
  1094. for (nChar1 = 0; nChar1 < nChar && psz1[nChar1]; nChar1++) { }
  1095. // And similarly for nChar2
  1096. for (nChar2 = 0; nChar2 < nChar && psz2[nChar2]; nChar2++) { }
  1097. }
  1098. return _StrCmpLocaleA(NORM_IGNORECASE, psz1, nChar1, psz2, nChar2);
  1099. }
  1100. else if (nChar && (!psz1 || !psz2))
  1101. {
  1102. return _StrCmpLocaleA(NORM_IGNORECASE | NORM_STOP_ON_NULL, psz1, nChar, psz2, nChar);
  1103. }
  1104. else
  1105. {
  1106. int i;
  1107. LPCSTR lpszEnd = psz1 + nChar;
  1108. for ( ; (lpszEnd > psz1) && (*psz1 || *psz2); (psz1 = AnsiNext(psz1)), (psz2 = AnsiNext(psz2)))
  1109. {
  1110. WORD w1, w2;
  1111. // If either pointer is at the null terminator already,
  1112. // we want to copy just one byte to make sure we don't read
  1113. // past the buffer (might be at a page boundary).
  1114. if (IsAsciiA(*psz1) && IsAsciiA(*psz2))
  1115. {
  1116. i = Ascii_ToLowerA(*psz1) - Ascii_ToLowerA(*psz2);
  1117. }
  1118. else
  1119. {
  1120. w1 = (*psz1) ? READNATIVEWORD(psz1) : 0;
  1121. w2 = (UINT)(IsDBCSLeadByte(*psz2)) ? (UINT)READNATIVEWORD(psz2) : (WORD)(BYTE)(*psz2);
  1122. i = ChrCmpIA(w1, w2);
  1123. }
  1124. if (i)
  1125. {
  1126. if (i < 0)
  1127. return -1;
  1128. else
  1129. return 1;
  1130. }
  1131. }
  1132. return 0;
  1133. }
  1134. }
  1135. int StrCmpNIW(LPCWSTR psz1, LPCWSTR psz2, int nChar)
  1136. {
  1137. RIPMSG(nChar==0 || (psz1 && IS_VALID_STRING_PTRW(psz1, nChar)), "StrCmpNIW: Caller passed bad psz1");
  1138. RIPMSG(nChar==0 || (psz2 && IS_VALID_STRING_PTRW(psz2, nChar)), "StrCmpNIW: Caller passed bad psz2");
  1139. RIPMSG(nChar>=0, "StrCmpNW: caller passed bad nChar");
  1140. return _StrCmpLocaleW(NORM_IGNORECASE | NORM_STOP_ON_NULL, psz1, nChar, psz2, nChar);
  1141. }
  1142. /*
  1143. * StrRStrI - Search for last occurrence of a substring
  1144. *
  1145. * Assumes lpSource points to the null terminated source string
  1146. * lpLast points to where to search from in the source string
  1147. * lpLast is not included in the search
  1148. * lpSrch points to string to search for
  1149. * returns last occurrence of string if successful; NULL otherwise
  1150. */
  1151. LPSTR StrRStrIA(LPCSTR lpSource, LPCSTR lpLast, LPCSTR lpSrch)
  1152. {
  1153. LPCSTR lpFound = NULL;
  1154. RIPMSG(lpSource && IS_VALID_STRING_PTRA(lpSource, -1), "StrRStrIA: Caller passed bad lpSource");
  1155. RIPMSG(!lpLast || (IS_VALID_STRING_PTRA(lpLast, -1) && lpLast>=lpSource && lpLast<=lpSource+lstrlenA(lpSource)), "StrRStrIA: Caller passed bad lpLast");
  1156. RIPMSG(lpSrch && IS_VALID_STRING_PTRA(lpSrch, -1) && *lpSrch, "StrRStrIA: Caller passed bad lpSrch");
  1157. if (!lpLast)
  1158. lpLast = lpSource + lstrlenA(lpSource);
  1159. if (lpSource && lpSrch && *lpSrch)
  1160. {
  1161. WORD wMatch;
  1162. UINT uLen;
  1163. LPCSTR lpStart;
  1164. wMatch = READNATIVEWORD(lpSrch);
  1165. wMatch = (UINT)(IsDBCSLeadByte(LOBYTE(wMatch)) ? wMatch : LOBYTE(wMatch));
  1166. uLen = lstrlenA(lpSrch);
  1167. lpStart = lpSource;
  1168. while (*lpStart && (lpStart < lpLast))
  1169. {
  1170. if (!ChrCmpIA(READNATIVEWORD(lpStart), wMatch))
  1171. {
  1172. if (StrCmpNIA(lpStart, lpSrch, uLen) == 0)
  1173. lpFound = lpStart;
  1174. }
  1175. lpStart = AnsiNext(lpStart);
  1176. }
  1177. }
  1178. return((LPSTR)lpFound);
  1179. }
  1180. LPWSTR StrRStrIW(LPCWSTR lpSource, LPCWSTR lpLast, LPCWSTR lpSrch)
  1181. {
  1182. LPCWSTR lpFound = NULL;
  1183. RIPMSG(lpSource && IS_VALID_STRING_PTRW(lpSource, -1), "StrRStrIW: Caller passed bad lpSource");
  1184. RIPMSG(!lpLast || (IS_VALID_STRING_PTRW(lpLast, -1) && lpLast>=lpSource && lpLast<=lpSource+lstrlenW(lpSource)), "StrRStrIW: Caller passed bad lpLast");
  1185. RIPMSG(lpSrch && IS_VALID_STRING_PTRW(lpSrch, -1) && *lpSrch, "StrRStrIW: Caller passed bad lpSrch");
  1186. if (!lpLast)
  1187. lpLast = lpSource + lstrlenW(lpSource);
  1188. if (lpSource && lpSrch && *lpSrch)
  1189. {
  1190. WCHAR wMatch;
  1191. UINT uLen;
  1192. LPCWSTR lpStart;
  1193. wMatch = *lpSrch;
  1194. uLen = lstrlenW(lpSrch);
  1195. lpStart = lpSource;
  1196. while (*lpStart && (lpStart < lpLast))
  1197. {
  1198. if (!ChrCmpIW(*lpStart, wMatch))
  1199. {
  1200. if (StrCmpNIW(lpStart, lpSrch, uLen) == 0)
  1201. lpFound = lpStart;
  1202. }
  1203. lpStart++;
  1204. }
  1205. }
  1206. return((LPWSTR)lpFound);
  1207. }
  1208. /*
  1209. * StrStr - Search for first occurrence of a substring
  1210. *
  1211. * Assumes lpSource points to source string
  1212. * lpSrch points to string to search for
  1213. * returns first occurrence of string if successful; NULL otherwise
  1214. */
  1215. LPSTR StrStrA(LPCSTR lpFirst, LPCSTR lpSrch)
  1216. {
  1217. RIPMSG(lpFirst && IS_VALID_STRING_PTRA(lpFirst, -1), "StrStrA: Caller passed bad lpFirst");
  1218. RIPMSG(lpSrch && IS_VALID_STRING_PTRA(lpSrch, -1), "StrStrA: Caller passed bad lpSrch");
  1219. if (lpFirst && lpSrch)
  1220. {
  1221. UINT uLen;
  1222. WORD wMatch;
  1223. CPINFO cpinfo;
  1224. BOOL fMBCS = GetCPInfo(CP_ACP, &cpinfo) && cpinfo.LeadByte[0];
  1225. uLen = (UINT)lstrlenA(lpSrch);
  1226. wMatch = READNATIVEWORD(lpSrch);
  1227. for ( ; (lpFirst=_StrChrA(lpFirst, wMatch, fMBCS))!=0 && _StrCmpNA(lpFirst, lpSrch, uLen, fMBCS);
  1228. lpFirst=AnsiNext(lpFirst))
  1229. continue; /* continue until we hit the end of the string or get a match */
  1230. return((LPSTR)lpFirst);
  1231. }
  1232. return(NULL);
  1233. }
  1234. LPWSTR StrStrW(LPCWSTR lpFirst, LPCWSTR lpSrch)
  1235. {
  1236. RIPMSG(lpFirst && IS_VALID_STRING_PTRW(lpFirst, -1), "StrStrW: Caller passed bad lpFirst");
  1237. RIPMSG(lpSrch && IS_VALID_STRING_PTRW(lpSrch, -1), "StrStrW: Caller passed bad lpSrch");
  1238. if (lpFirst && lpSrch)
  1239. {
  1240. UINT uLen;
  1241. WCHAR wMatch;
  1242. uLen = (UINT)lstrlenW(lpSrch);
  1243. wMatch = *lpSrch;
  1244. for ( ; (lpFirst=StrChrW(lpFirst, wMatch))!=0 && StrCmpNW(lpFirst, lpSrch, uLen);
  1245. lpFirst++)
  1246. continue; /* continue until we hit the end of the string or get a match */
  1247. return (LPWSTR)lpFirst;
  1248. }
  1249. return NULL;
  1250. }
  1251. /*
  1252. * StrStrN - Search for first occurrence of a substring
  1253. *
  1254. * Assumes lpSource points to source string
  1255. * lpSrch points to string to search for
  1256. * returns first occurrence of string if successful; NULL otherwise
  1257. */
  1258. LPWSTR StrStrNW(LPCWSTR lpFirst, LPCWSTR lpSrch, UINT cchMax)
  1259. {
  1260. RIPMSG(lpFirst && IS_VALID_STRING_PTRW(lpFirst, cchMax), "StrStrW: Caller passed bad lpFirst");
  1261. RIPMSG(lpSrch && IS_VALID_STRING_PTRW(lpSrch, cchMax), "StrStrW: Caller passed bad lpSrch");
  1262. if (lpFirst && lpSrch)
  1263. {
  1264. UINT uLen;
  1265. WCHAR wMatch;
  1266. LPCWSTR lpSentinel = lpFirst+cchMax;
  1267. uLen = (UINT)lstrlenW(lpSrch);
  1268. wMatch = *lpSrch;
  1269. // the first two conditions in this loop signify failure when they eval to false,
  1270. // while the third condition signifies success. We need to special case the second
  1271. // condition at the end of the function because it doesn't automatically cause the
  1272. // right value to be returned
  1273. while((lpFirst=StrChrNW(lpFirst, wMatch, cchMax))!=0 && cchMax>=uLen &&StrCmpNW(lpFirst, lpSrch, uLen))
  1274. {
  1275. lpFirst++;
  1276. cchMax=(UINT)(lpSentinel-lpFirst);
  1277. }/* continue until we hit the end of the string or get a match */
  1278. if(cchMax<uLen)
  1279. return NULL;// we ran out of space
  1280. return (LPWSTR)lpFirst;
  1281. }
  1282. return NULL;
  1283. }
  1284. /*
  1285. * StrStrI - Search for first occurrence of a substring, case insensitive
  1286. *
  1287. * Assumes lpFirst points to source string
  1288. * lpSrch points to string to search for
  1289. * returns first occurrence of string if successful; NULL otherwise
  1290. */
  1291. LPSTR StrStrIA(LPCSTR lpFirst, LPCSTR lpSrch)
  1292. {
  1293. RIPMSG(lpFirst && IS_VALID_STRING_PTRA(lpFirst, -1), "StrStrIA: Caller passed bad lpFirst");
  1294. RIPMSG(lpSrch && IS_VALID_STRING_PTRA(lpSrch, -1), "StrStrIA: Caller passed bad lpSrch");
  1295. if (lpFirst && lpSrch)
  1296. {
  1297. UINT uLen = (UINT)lstrlenA(lpSrch);
  1298. WORD wMatch = READNATIVEWORD(lpSrch);
  1299. for ( ; (lpFirst = StrChrIA(lpFirst, wMatch)) != 0 && StrCmpNIA(lpFirst, lpSrch, uLen);
  1300. lpFirst=AnsiNext(lpFirst))
  1301. continue; /* continue until we hit the end of the string or get a match */
  1302. return (LPSTR)lpFirst;
  1303. }
  1304. return NULL;
  1305. }
  1306. LPWSTR StrStrIW(LPCWSTR lpFirst, LPCWSTR lpSrch)
  1307. {
  1308. RIPMSG(lpFirst && IS_VALID_STRING_PTRW(lpFirst, -1), "StrStrIW: Caller passed bad lpFirst");
  1309. RIPMSG(lpSrch && IS_VALID_STRING_PTRW(lpSrch, -1), "StrStrIW: Caller passed bad lpSrch");
  1310. if (lpFirst && lpSrch)
  1311. {
  1312. UINT uLen = (UINT)lstrlenW(lpSrch);
  1313. WCHAR wMatch = *lpSrch;
  1314. for ( ; (lpFirst = StrChrIW(lpFirst, wMatch)) != 0 && StrCmpNIW(lpFirst, lpSrch, uLen);
  1315. lpFirst++)
  1316. continue; /* continue until we hit the end of the string or get a match */
  1317. return (LPWSTR)lpFirst;
  1318. }
  1319. return NULL;
  1320. }
  1321. /*
  1322. * StrStrNI - Search for first occurrence of a substring, case insensitive, counted
  1323. *
  1324. * Assumes lpFirst points to source string
  1325. * lpSrch points to string to search for
  1326. * returns first occurrence of string if successful; NULL otherwise
  1327. */
  1328. LPWSTR StrStrNIW(LPCWSTR lpFirst, LPCWSTR lpSrch, UINT cchMax)
  1329. {
  1330. RIPMSG(lpFirst && IS_VALID_STRING_PTRW(lpFirst, cchMax), "StrStrNIW: Caller passed bad lpFirst");
  1331. RIPMSG(lpSrch && IS_VALID_STRING_PTRW(lpSrch, cchMax), "StrStrNIW: Caller passed bad lpSrch");
  1332. if (lpFirst && lpSrch)
  1333. {
  1334. UINT uLen = (UINT)lstrlenW(lpSrch);
  1335. WCHAR wMatch = *lpSrch;
  1336. LPCWSTR lpSentinel = lpFirst+cchMax;
  1337. // the first two conditions in this loop signify failure when they eval to false,
  1338. // while the third condition signifies success. We need to special case the second
  1339. // condition at the end of the function because it doesn't automatically cause the
  1340. // right value to be returned
  1341. while((lpFirst = StrChrNIW(lpFirst, wMatch, cchMax)) != 0 && cchMax >= uLen && StrCmpNIW(lpFirst, lpSrch, uLen))
  1342. {
  1343. lpFirst++;
  1344. cchMax = (UINT)(lpSentinel - lpFirst);
  1345. }/* continue until we hit the end of the string or get a match */
  1346. if(cchMax<uLen)
  1347. return NULL;// we ran out of space
  1348. return (LPWSTR)lpFirst;
  1349. }
  1350. return NULL;
  1351. }
  1352. LPSTR StrDupA(LPCSTR psz)
  1353. {
  1354. RIPMSG(psz && IS_VALID_STRING_PTRA(psz, -1), "StrDupA: Caller passed invalid psz");
  1355. if (psz)
  1356. {
  1357. LPSTR pszRet = (LPSTR)LocalAlloc(LPTR, (lstrlenA(psz) + 1) * sizeof(*pszRet));
  1358. if (pszRet)
  1359. {
  1360. lstrcpyA(pszRet, psz);
  1361. }
  1362. return pszRet;
  1363. }
  1364. return NULL;
  1365. }
  1366. LPWSTR StrDupW(LPCWSTR psz)
  1367. {
  1368. RIPMSG(psz && IS_VALID_STRING_PTRW(psz, -1), "StrDupW: Caller passed invalid psz");
  1369. if (psz)
  1370. {
  1371. LPWSTR pszRet = (LPWSTR)LocalAlloc(LPTR, (lstrlenW(psz) + 1) * sizeof(*pszRet));
  1372. if (pszRet)
  1373. {
  1374. StrCpyW(pszRet, psz);
  1375. }
  1376. return pszRet;
  1377. }
  1378. return NULL;
  1379. }
  1380. void _StrOut(LPSTR* ppszBuf, HMODULE hmod, UINT idRes, DWORD* pdwTimeS, int* pdigits, UINT iDiv)
  1381. {
  1382. if (*pdigits)
  1383. {
  1384. DWORD dwCur = *pdwTimeS/iDiv;
  1385. if (dwCur || iDiv==1)
  1386. {
  1387. DWORD dwBase;
  1388. CHAR szBuf[64], szTemplate[64];
  1389. LPSTR pszBuf = szBuf;
  1390. *pdwTimeS -= dwCur*iDiv;
  1391. for (dwBase=1; dwCur/(dwBase*10); dwBase*=10);
  1392. DebugMsg(DM_INTERVAL, TEXT("dwCur, dwBase, *pdwTimeS = %d, %d, %d"), dwCur, dwBase, *pdwTimeS);
  1393. //
  1394. // LATER: We could use atoi if we mathematically trancate
  1395. // the numbers based on digits.
  1396. //
  1397. for (;dwBase; dwBase/=10, pszBuf++)
  1398. {
  1399. if (*pdigits)
  1400. {
  1401. DWORD i = dwCur/dwBase;
  1402. dwCur -= i*dwBase;
  1403. *pszBuf = '0'+(unsigned short)i;
  1404. (*pdigits)--;
  1405. }
  1406. else
  1407. {
  1408. *pszBuf = '0';
  1409. }
  1410. }
  1411. *pszBuf = '\0';
  1412. MLLoadStringA(idRes, szTemplate, ARRAYSIZE(szTemplate));
  1413. wsprintfA(*ppszBuf, szTemplate, szBuf);
  1414. (*ppszBuf) += lstrlenA(*ppszBuf);
  1415. }
  1416. }
  1417. }
  1418. void _StrOutW(LPWSTR* ppwszBuf, HMODULE hmod, UINT idRes, DWORD* pdwTimeS, int* pdigits, UINT iDiv)
  1419. {
  1420. if (*pdigits)
  1421. {
  1422. DWORD dwCur = *pdwTimeS/iDiv;
  1423. if (dwCur || iDiv==1)
  1424. {
  1425. DWORD dwBase;
  1426. WCHAR wszBuf[64], wszTemplate[64];
  1427. LPWSTR pwszBuf = wszBuf;
  1428. *pdwTimeS -= dwCur*iDiv;
  1429. for (dwBase=1; dwCur/(dwBase*10); dwBase*=10);
  1430. DebugMsg(DM_INTERVAL, TEXT("dwCur, dwBase, *pdwTimeS = %d, %d, %d"), dwCur, dwBase, *pdwTimeS);
  1431. //
  1432. // LATER: We could use atoi if we mathematically trancate
  1433. // the numbers based on digits.
  1434. //
  1435. for (;dwBase; dwBase/=10, pwszBuf++)
  1436. {
  1437. if (*pdigits)
  1438. {
  1439. DWORD i = dwCur/dwBase;
  1440. dwCur -= i*dwBase;
  1441. *pwszBuf = L'0'+(unsigned short)i;
  1442. (*pdigits)--;
  1443. }
  1444. else
  1445. {
  1446. *pwszBuf = L'0';
  1447. }
  1448. }
  1449. *pwszBuf = L'\0';
  1450. MLLoadStringW(idRes, wszTemplate, ARRAYSIZE(wszTemplate));
  1451. // NOTE: 256 comes from the work buffer in StrFromTimeIntervalA/W
  1452. wnsprintfW(*ppwszBuf, 256, wszTemplate, wszBuf);
  1453. (*ppwszBuf) += lstrlenW(*ppwszBuf);
  1454. }
  1455. }
  1456. }
  1457. BOOL _StrFromTimeInterval(LPSTR szBuf, DWORD dwTimeMS, int digits)
  1458. {
  1459. DWORD dwTimeS = (dwTimeMS+500)/1000;
  1460. LPSTR pszBuf = szBuf;
  1461. DebugMsg(DM_INTERVAL, TEXT("dwTimeS = %d"), dwTimeS);
  1462. szBuf = '\0';
  1463. _StrOut(&pszBuf, g_hinst, IDS_HOUR, &dwTimeS, &digits, 3600);
  1464. _StrOut(&pszBuf, g_hinst, IDS_MIN, &dwTimeS, &digits, 60);
  1465. _StrOut(&pszBuf, g_hinst, IDS_SEC, &dwTimeS, &digits, 1);
  1466. return TRUE;
  1467. }
  1468. BOOL _StrFromTimeIntervalW(LPWSTR wszBuf, DWORD dwTimeMS, int digits)
  1469. {
  1470. DWORD dwTimeS = (dwTimeMS+500)/1000;
  1471. LPWSTR pwszBuf = wszBuf;
  1472. DebugMsg(DM_INTERVAL, TEXT("dwTimeS = %d"), dwTimeS);
  1473. wszBuf = '\0';
  1474. _StrOutW(&pwszBuf, g_hinst, IDS_HOUR, &dwTimeS, &digits, 3600);
  1475. _StrOutW(&pwszBuf, g_hinst, IDS_MIN, &dwTimeS, &digits, 60);
  1476. _StrOutW(&pwszBuf, g_hinst, IDS_SEC, &dwTimeS, &digits, 1);
  1477. return TRUE;
  1478. }
  1479. //
  1480. // This API converts a given time-interval (in msec) into a human readable
  1481. // string.
  1482. //
  1483. // Parameters:
  1484. // pszOut -- Specifies the string buffer. NULL is valid to query size.
  1485. // cchMax -- Specifies the size of buffer in char/WCHAR
  1486. // dwTimeMS -- Specifies the time interval in msec
  1487. // digits -- Specifies the minimum number of digits to be displayed
  1488. //
  1489. // Returns:
  1490. // Number of characters in the buffer (not including the terminator).
  1491. //
  1492. // Exmaples:
  1493. // dwTimeMS digits output
  1494. // 34000 3 34 sec
  1495. // 34000 2 34 sec
  1496. // 34000 1 30 sec
  1497. // 74000 3 1 min 14 sec
  1498. // 74000 2 1 min 10 sec
  1499. // 74000 1 1 min
  1500. //
  1501. int StrFromTimeIntervalA(LPSTR pszOut, UINT cchMax, DWORD dwTimeMS, int digits)
  1502. {
  1503. CHAR szBuf[256];
  1504. int cchRet = 0;
  1505. RIPMSG(!pszOut || IS_VALID_WRITE_BUFFER(pszOut, char, cchMax), "StrFromTimeIntervalA: Caller passed invalid pszOut");
  1506. DEBUGWhackPathBufferA(pszOut, cchMax);
  1507. if (_StrFromTimeInterval(szBuf, dwTimeMS, digits))
  1508. {
  1509. if (pszOut)
  1510. {
  1511. lstrcpynA(pszOut, szBuf, cchMax);
  1512. cchRet = lstrlenA(pszOut);
  1513. }
  1514. else
  1515. {
  1516. cchRet = lstrlenA(szBuf);
  1517. }
  1518. }
  1519. return cchRet;
  1520. }
  1521. int StrFromTimeIntervalW(LPWSTR pwszOut, UINT cchMax, DWORD dwTimeMS, int digits)
  1522. {
  1523. WCHAR wszBuf[256];
  1524. int cchRet = 0;
  1525. RIPMSG(!pwszOut || IS_VALID_WRITE_BUFFER(pwszOut, WCHAR, cchMax), "StrFromTimeIntervalW: Caller passed invalid pszOut");
  1526. DEBUGWhackPathBufferW(pwszOut, cchMax);
  1527. if (_StrFromTimeIntervalW(wszBuf, dwTimeMS, digits))
  1528. {
  1529. if (pwszOut)
  1530. {
  1531. lstrcpynW(pwszOut, wszBuf, cchMax);
  1532. cchRet = lstrlenW(pwszOut);
  1533. }
  1534. else
  1535. {
  1536. cchRet = lstrlenW(wszBuf);
  1537. }
  1538. }
  1539. return cchRet;
  1540. }
  1541. /*
  1542. * IntlStrEq
  1543. *
  1544. * returns TRUE if strings are equal, FALSE if not
  1545. */
  1546. BOOL StrIsIntlEqualA(BOOL fCaseSens, LPCSTR lpString1, LPCSTR lpString2, int nChar)
  1547. {
  1548. DWORD dwFlags = fCaseSens ? LOCALE_USE_CP_ACP : (NORM_IGNORECASE | LOCALE_USE_CP_ACP);
  1549. RIPMSG(lpString1 && IS_VALID_STRING_PTRA(lpString1, nChar), "StrIsIntlEqualA: Caller passed invalid lpString1");
  1550. RIPMSG(lpString2 && IS_VALID_STRING_PTRA(lpString2, nChar), "StrIsIntlEqualA: Caller passed invalid lpString2");
  1551. RIPMSG(nChar >= -1, "StrIsIntlEqualA: Caller passed invalid nChar");
  1552. if (g_bRunningOnNT)
  1553. {
  1554. dwFlags |= NORM_STOP_ON_NULL; // only supported on NT
  1555. }
  1556. else if (nChar != -1 && lpString1 && lpString2)
  1557. {
  1558. // On Win9x we have to do the check manually
  1559. //
  1560. int cch = 0;
  1561. LPCSTR psz1 = lpString1;
  1562. LPCSTR psz2 = lpString2;
  1563. while(*psz1 != 0 && *psz2 != 0 && cch < nChar)
  1564. {
  1565. psz1 = CharNextA(psz1);
  1566. psz2 = CharNextA(psz2);
  1567. cch = (int) min(psz1 - lpString1, psz2 - lpString2);
  1568. }
  1569. // add one in for terminating '\0'
  1570. cch++;
  1571. if (cch < nChar)
  1572. nChar = cch;
  1573. }
  1574. return 0 == _StrCmpLocaleA(dwFlags, lpString1, nChar, lpString2, nChar);
  1575. }
  1576. BOOL StrIsIntlEqualW(BOOL fCaseSens, LPCWSTR psz1, LPCWSTR psz2, int nChar)
  1577. {
  1578. RIPMSG(psz1 && IS_VALID_STRING_PTRW(psz1, nChar), "StrIsIntlEqualW: Caller passed invalid psz1");
  1579. RIPMSG(psz2 && IS_VALID_STRING_PTRW(psz2, nChar), "StrIsIntlEqualW: Caller passed invalid psz2");
  1580. RIPMSG(nChar >= -1, "StrIsIntlEqualW: Caller passed invalid nChar");
  1581. return 0 == _StrCmpLocaleW(fCaseSens ? NORM_STOP_ON_NULL : NORM_IGNORECASE | NORM_STOP_ON_NULL,
  1582. psz1, nChar, psz2, nChar);
  1583. }
  1584. // This is stolen from shell32 - util.c
  1585. #define LODWORD(_qw) (DWORD)(_qw)
  1586. const short c_aOrders[] = {IDS_BYTES, IDS_ORDERKB, IDS_ORDERMB,
  1587. IDS_ORDERGB, IDS_ORDERTB, IDS_ORDERPB, IDS_ORDEREB};
  1588. void Int64ToStr(LONGLONG n, LPWSTR lpBuffer)
  1589. {
  1590. WCHAR szTemp[40];
  1591. LONGLONG iChr;
  1592. iChr = 0;
  1593. do {
  1594. szTemp[iChr++] = L'0' + (WCHAR)(n % 10);
  1595. n = n / 10;
  1596. } while (n != 0);
  1597. do {
  1598. iChr--;
  1599. *lpBuffer++ = szTemp[iChr];
  1600. } while (iChr != 0);
  1601. *lpBuffer++ = L'\0';
  1602. }
  1603. //
  1604. // Obtain NLS info about how numbers should be grouped.
  1605. //
  1606. // The annoying thing is that LOCALE_SGROUPING and NUMBERFORMAT
  1607. // have different ways of specifying number grouping.
  1608. //
  1609. // LOCALE NUMBERFMT Sample Country
  1610. //
  1611. // 3;0 3 1,234,567 United States
  1612. // 3;2;0 32 12,34,567 India
  1613. // 3 30 1234,567 ??
  1614. //
  1615. // Not my idea. That's the way it works.
  1616. //
  1617. // Bonus treat - Win9x doesn't support complex number formats,
  1618. // so we return only the first number.
  1619. //
  1620. UINT GetNLSGrouping(void)
  1621. {
  1622. UINT grouping;
  1623. LPWSTR psz;
  1624. WCHAR szGrouping[32];
  1625. // If no locale info, then assume Western style thousands
  1626. if (!GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_SGROUPING, szGrouping, ARRAYSIZE(szGrouping)))
  1627. return 3;
  1628. grouping = 0;
  1629. psz = szGrouping;
  1630. if (g_bRunningOnNT)
  1631. {
  1632. for (;;)
  1633. {
  1634. if (*psz == L'0') break; // zero - stop
  1635. else if ((UINT)(*psz - L'0') < 10) // digit - accumulate it
  1636. grouping = grouping * 10 + (UINT)(*psz - L'0');
  1637. else if (*psz) // punctuation - ignore it
  1638. { }
  1639. else // end of string, no "0" found
  1640. {
  1641. grouping = grouping * 10; // put zero on end (see examples)
  1642. break; // and finished
  1643. }
  1644. psz++;
  1645. }
  1646. }
  1647. else
  1648. {
  1649. // Win9x - take only the first grouping
  1650. grouping = StrToIntW(szGrouping);
  1651. }
  1652. return grouping;
  1653. }
  1654. // Sizes of various stringized numbers
  1655. #define MAX_INT64_SIZE 30 // 2^64 is less than 30 chars long
  1656. #define MAX_COMMA_NUMBER_SIZE (MAX_INT64_SIZE + 10)
  1657. // takes a DWORD add commas etc to it and puts the result in the buffer
  1658. LPWSTR CommifyString(LONGLONG n, LPWSTR pszBuf, UINT cchBuf)
  1659. {
  1660. WCHAR szNum[MAX_COMMA_NUMBER_SIZE], szSep[5];
  1661. NUMBERFMTW nfmt;
  1662. nfmt.NumDigits = 0;
  1663. nfmt.LeadingZero = 0;
  1664. nfmt.Grouping = GetNLSGrouping();
  1665. GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_STHOUSAND, szSep, ARRAYSIZE(szSep));
  1666. nfmt.lpDecimalSep = nfmt.lpThousandSep = szSep;
  1667. nfmt.NegativeOrder = 0;
  1668. Int64ToStr(n, szNum);
  1669. if (GetNumberFormatW(LOCALE_USER_DEFAULT, 0, szNum, &nfmt, pszBuf, cchBuf) == 0)
  1670. StrCpyNW(pszBuf, szNum, cchBuf);
  1671. return pszBuf;
  1672. }
  1673. /* converts numbers into sort formats
  1674. * 532 -> 523 bytes
  1675. * 1340 -> 1.3KB
  1676. * 23506 -> 23.5KB
  1677. * -> 2.4MB
  1678. * -> 5.2GB
  1679. */
  1680. LPWSTR StrFormatByteSizeW(LONGLONG n, LPWSTR pszBuf, UINT cchBuf)
  1681. {
  1682. RIPMSG(pszBuf && IS_VALID_WRITE_BUFFER(pszBuf, WCHAR, cchBuf), "StrFormatByteSizeW: Caller passed invalid pszBuf");
  1683. DEBUGWhackPathBufferW(pszBuf, cchBuf);
  1684. if (pszBuf)
  1685. {
  1686. WCHAR szWholeNum[32], szOrder[32];
  1687. int iOrder;
  1688. // If the size is less than 1024, then the order should be bytes we have nothing
  1689. // more to figure out
  1690. if (n < 1024)
  1691. {
  1692. wnsprintfW(szWholeNum, ARRAYSIZE(szWholeNum), L"%d", LODWORD(n));
  1693. iOrder = 0;
  1694. }
  1695. else
  1696. {
  1697. UINT uInt, uLen, uDec;
  1698. WCHAR szFormat[8];
  1699. // Find the right order
  1700. for (iOrder = 1; iOrder < ARRAYSIZE(c_aOrders) -1 && n >= 1000L * 1024L; n >>= 10, iOrder++);
  1701. /* do nothing */
  1702. uInt = LODWORD(n >> 10);
  1703. CommifyString(uInt, szWholeNum, ARRAYSIZE(szWholeNum));
  1704. uLen = lstrlenW(szWholeNum);
  1705. if (uLen < 3)
  1706. {
  1707. uDec = LODWORD(n - (LONGLONG)uInt * 1024L) * 1000 / 1024;
  1708. // At this point, uDec should be between 0 and 1000
  1709. // we want get the top one (or two) digits.
  1710. uDec /= 10;
  1711. if (uLen == 2)
  1712. uDec /= 10;
  1713. // Note that we need to set the format before getting the
  1714. // intl char.
  1715. StrCpyW(szFormat, L"%02d");
  1716. szFormat[2] = TEXT('0') + 3 - uLen;
  1717. GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_SDECIMAL,
  1718. szWholeNum + uLen, ARRAYSIZE(szWholeNum) - uLen);
  1719. uLen = lstrlenW(szWholeNum);
  1720. wnsprintfW(szWholeNum + uLen, ARRAYSIZE(szWholeNum) - uLen, szFormat, uDec);
  1721. }
  1722. }
  1723. MLLoadStringW(c_aOrders[iOrder], szOrder, ARRAYSIZE(szOrder));
  1724. wnsprintfW(pszBuf, cchBuf, szOrder, szWholeNum);
  1725. }
  1726. return pszBuf;
  1727. }
  1728. // dw - the nubmer to be converted
  1729. // pszBuf - buffer for the resulting string
  1730. // cchBuf - Max characters in Buffer
  1731. LPSTR StrFormatByteSize64A(LONGLONG dw, LPSTR pszBuf, UINT cchBuf)
  1732. {
  1733. WCHAR szT[32];
  1734. DEBUGWhackPathBuffer(pszBuf, cchBuf);
  1735. StrFormatByteSizeW(dw, szT, SIZECHARS(szT));
  1736. SHUnicodeToAnsi(szT, pszBuf, cchBuf);
  1737. return pszBuf;
  1738. }
  1739. LPSTR StrFormatByteSizeA(DWORD dw, LPSTR pszBuf, UINT cchBuf)
  1740. {
  1741. return StrFormatByteSize64A((LONGLONG)dw, pszBuf, cchBuf);
  1742. }
  1743. LPWSTR StrFormatKBSizeW(LONGLONG n, LPWSTR pszBuf, UINT cchBuf)
  1744. {
  1745. RIPMSG(pszBuf && IS_VALID_WRITE_BUFFER(pszBuf, WCHAR, cchBuf), "StrFormatKBSizeW: Caller passed invalid pszBuf");
  1746. DEBUGWhackPathBufferW(pszBuf, cchBuf);
  1747. if (pszBuf)
  1748. {
  1749. static WCHAR s_szOrder[16] = {0};
  1750. WCHAR szNum[64];
  1751. if (s_szOrder[0] == TEXT('\0'))
  1752. LoadStringW(HINST_THISDLL, IDS_ORDERKB, s_szOrder, ARRAYSIZE(s_szOrder));
  1753. CommifyString((n + 1023) / 1024, szNum, ARRAYSIZE(szNum));
  1754. wnsprintfW(pszBuf, cchBuf, s_szOrder, szNum);
  1755. }
  1756. return pszBuf;
  1757. }
  1758. LPSTR StrFormatKBSizeA(LONGLONG n, LPSTR pszBuf, UINT cchBuf)
  1759. {
  1760. WCHAR szNum[64];
  1761. DEBUGWhackPathBufferA(pszBuf, cchBuf);
  1762. StrFormatKBSizeW(n, szNum, ARRAYSIZE(szNum));
  1763. SHUnicodeToAnsi(szNum, pszBuf, cchBuf);
  1764. return pszBuf;
  1765. }
  1766. // Win95 does not support the wide-char version of lstrcmp, lstrcmpi
  1767. // Wrapper for lstrcmpW so it works on Win95
  1768. int StrCmpW(LPCWSTR pwsz1, LPCWSTR pwsz2)
  1769. {
  1770. RIPMSG(pwsz1 && IS_VALID_STRING_PTRW(pwsz1, -1), "StrCmpW: Caller passed invalid pwsz1");
  1771. RIPMSG(pwsz2 && IS_VALID_STRING_PTRW(pwsz2, -1), "StrCmpW: Caller passed invalid pwsz2");
  1772. return _StrCmpLocaleW(0, pwsz1, -1, pwsz2, -1);
  1773. }
  1774. // Wrapper for lstrcmpiW so it works on Win95
  1775. int StrCmpIW(LPCWSTR pwsz1, LPCWSTR pwsz2)
  1776. {
  1777. RIPMSG(pwsz1 && IS_VALID_STRING_PTRW(pwsz1, -1), "StrCmpIW: Caller passed invalid pwsz1");
  1778. RIPMSG(pwsz2 && IS_VALID_STRING_PTRW(pwsz2, -1), "StrCmpIW: Caller passed invalid pwsz2");
  1779. return _StrCmpLocaleW(NORM_IGNORECASE, pwsz1, -1, pwsz2, -1);
  1780. }
  1781. /*----------------------------------------------------------
  1782. Purpose: Trim the string pszTrimMe of any leading or trailing
  1783. characters that are in pszTrimChars.
  1784. Returns: TRUE if anything was stripped
  1785. */
  1786. STDAPI_(BOOL) StrTrimA(IN OUT LPSTR pszTrimMe, LPCSTR pszTrimChars)
  1787. {
  1788. BOOL bRet = FALSE;
  1789. RIPMSG(pszTrimMe && IS_VALID_STRING_PTRA(pszTrimMe, -1), "StrTrimA: Caller passed invalid pszTrimMe");
  1790. RIPMSG(pszTrimChars && IS_VALID_STRING_PTRA(pszTrimChars, -1), "StrTrimA: Caller passed invalid pszTrimChars");
  1791. if (pszTrimMe && pszTrimChars)
  1792. {
  1793. LPSTR psz;
  1794. LPSTR pszStartMeat;
  1795. LPSTR pszMark = NULL;
  1796. /* Trim leading characters. */
  1797. psz = pszTrimMe;
  1798. while (*psz && StrChrA(pszTrimChars, READNATIVEWORD(psz)))
  1799. psz = CharNextA(psz);
  1800. pszStartMeat = psz;
  1801. /* Trim trailing characters. */
  1802. // (The old algorithm used to start from the end and go
  1803. // backwards, but that is piggy because DBCS version of
  1804. // CharPrev iterates from the beginning of the string
  1805. // on every call.)
  1806. while (*psz)
  1807. {
  1808. if (StrChrA(pszTrimChars, READNATIVEWORD(psz)))
  1809. {
  1810. if (!pszMark)
  1811. {
  1812. pszMark = psz;
  1813. }
  1814. }
  1815. else
  1816. {
  1817. pszMark = NULL;
  1818. }
  1819. psz = CharNextA(psz);
  1820. }
  1821. // Any trailing characters to clip?
  1822. if (pszMark)
  1823. {
  1824. // Yes
  1825. *pszMark = '\0';
  1826. bRet = TRUE;
  1827. }
  1828. /* Relocate stripped string. */
  1829. if (pszStartMeat > pszTrimMe)
  1830. {
  1831. /* (+ 1) for null terminator. */
  1832. MoveMemory(pszTrimMe, pszStartMeat, CbFromCchA(lstrlenA(pszStartMeat) + 1));
  1833. bRet = TRUE;
  1834. }
  1835. else
  1836. ASSERT(pszStartMeat == pszTrimMe);
  1837. ASSERT(IS_VALID_STRING_PTRA(pszTrimMe, -1));
  1838. }
  1839. return bRet;
  1840. }
  1841. /*----------------------------------------------------------
  1842. Purpose: Trim the string pszTrimMe of any leading or trailing
  1843. characters that are in pszTrimChars.
  1844. Returns: TRUE if anything was stripped
  1845. */
  1846. STDAPI_(BOOL) StrTrimW(IN OUT LPWSTR pszTrimMe, LPCWSTR pszTrimChars)
  1847. {
  1848. BOOL bRet = FALSE;
  1849. RIPMSG(pszTrimMe && IS_VALID_STRING_PTRW(pszTrimMe, -1), "StrTrimW: Caller passed invalid pszTrimMe");
  1850. RIPMSG(pszTrimChars && IS_VALID_STRING_PTRW(pszTrimChars, -1), "StrTrimW: Caller passed invalid pszTrimChars");
  1851. if (pszTrimMe && pszTrimChars)
  1852. {
  1853. LPWSTR psz;
  1854. LPWSTR pszStartMeat;
  1855. LPWSTR pszMark = NULL;
  1856. /* Trim leading characters. */
  1857. psz = pszTrimMe;
  1858. while (*psz && StrChrW(pszTrimChars, *psz))
  1859. psz++;
  1860. pszStartMeat = psz;
  1861. /* Trim trailing characters. */
  1862. // (The old algorithm used to start from the end and go
  1863. // backwards, but that is piggy because DBCS version of
  1864. // CharPrev iterates from the beginning of the string
  1865. // on every call.)
  1866. while (*psz)
  1867. {
  1868. if (StrChrW(pszTrimChars, *psz))
  1869. {
  1870. if (!pszMark)
  1871. {
  1872. pszMark = psz;
  1873. }
  1874. }
  1875. else
  1876. {
  1877. pszMark = NULL;
  1878. }
  1879. psz++;
  1880. }
  1881. // Any trailing characters to clip?
  1882. if (pszMark)
  1883. {
  1884. // Yes
  1885. *pszMark = '\0';
  1886. bRet = TRUE;
  1887. }
  1888. /* Relocate stripped string. */
  1889. if (pszStartMeat > pszTrimMe)
  1890. {
  1891. /* (+ 1) for null terminator. */
  1892. MoveMemory(pszTrimMe, pszStartMeat, CbFromCchW(lstrlenW(pszStartMeat) + 1));
  1893. bRet = TRUE;
  1894. }
  1895. else
  1896. ASSERT(pszStartMeat == pszTrimMe);
  1897. ASSERT(IS_VALID_STRING_PTRW(pszTrimMe, -1));
  1898. }
  1899. return bRet;
  1900. }
  1901. /*----------------------------------------------------------
  1902. Purpose: Compare strings using C runtime (ASCII) collation rules.
  1903. Returns: < 0 if pch1 < pch2
  1904. = 0 if pch1 == pch2
  1905. > 0 if pch1 > pch2
  1906. */
  1907. LWSTDAPI_(int) StrCmpNCA(LPCSTR pch1, LPCSTR pch2, int n)
  1908. {
  1909. if (n == 0)
  1910. return 0;
  1911. while (--n && *pch1 && *pch1 == *pch2)
  1912. {
  1913. pch1++;
  1914. pch2++;
  1915. }
  1916. return *(unsigned char *)pch1 - *(unsigned char *)pch2;
  1917. }
  1918. /*----------------------------------------------------------
  1919. Purpose: Compare strings using C runtime (ASCII) collation rules.
  1920. Returns: < 0 if pch1 < pch2
  1921. = 0 if pch1 == pch2
  1922. > 0 if pch1 > pch2
  1923. */
  1924. LWSTDAPI_(int) StrCmpNCW(LPCWSTR pch1, LPCWSTR pch2, int n)
  1925. {
  1926. if (n == 0)
  1927. return 0;
  1928. while (--n && *pch1 && *pch1 == *pch2)
  1929. {
  1930. pch1++;
  1931. pch2++;
  1932. }
  1933. return *pch1 - *pch2;
  1934. }
  1935. /*----------------------------------------------------------
  1936. Purpose: Compare strings using C runtime (ASCII) collation rules.
  1937. Returns: < 0 if pch1 < pch2
  1938. = 0 if pch1 == pch2
  1939. > 0 if pch1 > pch2
  1940. */
  1941. LWSTDAPI_(int) StrCmpNICA(LPCSTR pch1, LPCSTR pch2, int n)
  1942. {
  1943. int ch1, ch2;
  1944. if (n != 0)
  1945. {
  1946. do {
  1947. ch1 = *pch1++;
  1948. if (ch1 >= 'A' && ch1 <= 'Z')
  1949. ch1 += 'a' - 'A';
  1950. ch2 = *pch2++;
  1951. if (ch2 >= 'A' && ch2 <= 'Z')
  1952. ch2 += 'a' - 'A';
  1953. } while ( --n && ch1 && (ch1 == ch2) );
  1954. return ch1 - ch2;
  1955. }
  1956. else
  1957. {
  1958. return 0;
  1959. }
  1960. }
  1961. /*----------------------------------------------------------
  1962. Purpose: Compare strings using C runtime (ASCII) collation rules.
  1963. Returns: < 0 if pch1 < pch2
  1964. = 0 if pch1 == pch2
  1965. > 0 if pch1 > pch2
  1966. */
  1967. LWSTDAPI_(int) StrCmpNICW(LPCWSTR pch1, LPCWSTR pch2, int n)
  1968. {
  1969. int ch1, ch2;
  1970. if (n != 0)
  1971. {
  1972. do {
  1973. ch1 = *pch1++;
  1974. if (ch1 >= L'A' && ch1 <= L'Z')
  1975. ch1 += L'a' - L'A';
  1976. ch2 = *pch2++;
  1977. if (ch2 >= L'A' && ch2 <= L'Z')
  1978. ch2 += L'a' - L'A';
  1979. } while ( --n && ch1 && (ch1 == ch2) );
  1980. return ch1 - ch2;
  1981. }
  1982. else
  1983. {
  1984. return 0;
  1985. }
  1986. }
  1987. /*----------------------------------------------------------
  1988. Purpose: Compare strings using C runtime (ASCII) collation rules.
  1989. Returns: < 0 if pch1 < pch2
  1990. = 0 if pch1 == pch2
  1991. > 0 if pch1 > pch2
  1992. */
  1993. LWSTDAPI_(int) StrCmpCA(LPCSTR pch1, LPCSTR pch2)
  1994. {
  1995. while (*pch1 && (*pch1 == *pch2))
  1996. {
  1997. ++pch1;
  1998. ++pch2;
  1999. }
  2000. return *(unsigned char *)pch1 - *(unsigned char *)pch2;
  2001. }
  2002. /*----------------------------------------------------------
  2003. Purpose: Compare strings using C runtime (ASCII) collation rules.
  2004. Returns: < 0 if pch1 < pch2
  2005. = 0 if pch1 == pch2
  2006. > 0 if pch1 > pch2
  2007. */
  2008. LWSTDAPI_(int) StrCmpCW(LPCWSTR pch1, LPCWSTR pch2)
  2009. {
  2010. while (*pch1 && (*pch1 == *pch2))
  2011. {
  2012. ++pch1;
  2013. ++pch2;
  2014. }
  2015. return *pch1 - *pch2;
  2016. }
  2017. /*----------------------------------------------------------
  2018. Purpose: Compare strings using C runtime (ASCII) collation rules.
  2019. Returns: < 0 if pch1 < pch2
  2020. = 0 if pch1 == pch2
  2021. > 0 if pch1 > pch2
  2022. */
  2023. LWSTDAPI_(int) StrCmpICA(LPCSTR pch1, LPCSTR pch2)
  2024. {
  2025. int ch1, ch2;
  2026. do {
  2027. ch1 = *pch1++;
  2028. if (ch1 >= 'A' && ch1 <= 'Z')
  2029. ch1 += 'a' - 'A';
  2030. ch2 = *pch2++;
  2031. if (ch2 >= 'A' && ch2 <= 'Z')
  2032. ch2 += 'a' - 'A';
  2033. } while (ch1 && (ch1 == ch2));
  2034. return ch1 - ch2;
  2035. }
  2036. /*----------------------------------------------------------
  2037. Purpose: Compare strings using C runtime (ASCII) collation rules.
  2038. Returns: < 0 if pch1 < pch2
  2039. = 0 if pch1 == pch2
  2040. > 0 if pch1 > pch2
  2041. */
  2042. LWSTDAPI_(int) StrCmpICW(LPCWSTR pch1, LPCWSTR pch2)
  2043. {
  2044. int ch1, ch2;
  2045. do {
  2046. ch1 = *pch1++;
  2047. if (ch1 >= L'A' && ch1 <= L'Z')
  2048. ch1 += L'a' - L'A';
  2049. ch2 = *pch2++;
  2050. if (ch2 >= L'A' && ch2 <= L'Z')
  2051. ch2 += L'a' - L'A';
  2052. } while (ch1 && (ch1 == ch2));
  2053. return ch1 - ch2;
  2054. }
  2055. LWSTDAPI StrRetToStrW(STRRET *psr, LPCITEMIDLIST pidl, WCHAR **ppsz)
  2056. {
  2057. HRESULT hres = S_OK;
  2058. switch (psr->uType)
  2059. {
  2060. case STRRET_WSTR:
  2061. *ppsz = psr->pOleStr;
  2062. psr->pOleStr = NULL; // avoid alias
  2063. hres = *ppsz ? S_OK : E_FAIL;
  2064. break;
  2065. case STRRET_OFFSET:
  2066. hres = SHStrDupA(STRRET_OFFPTR(pidl, psr), ppsz);
  2067. break;
  2068. case STRRET_CSTR:
  2069. hres = SHStrDupA(psr->cStr, ppsz);
  2070. break;
  2071. default:
  2072. *ppsz = NULL;
  2073. hres = E_FAIL;
  2074. }
  2075. return hres;
  2076. }
  2077. LWSTDAPI StrRetToBSTR(STRRET *psr, LPCITEMIDLIST pidl, BSTR *pbstr)
  2078. {
  2079. switch (psr->uType)
  2080. {
  2081. case STRRET_WSTR:
  2082. {
  2083. LPWSTR psz = psr->pOleStr;
  2084. psr->pOleStr = NULL; // avoid alias
  2085. *pbstr = SysAllocString(psz);
  2086. CoTaskMemFree(psz);
  2087. break;
  2088. }
  2089. case STRRET_OFFSET:
  2090. *pbstr = SysAllocStringA(STRRET_OFFPTR(pidl, psr));
  2091. break;
  2092. case STRRET_CSTR:
  2093. *pbstr = SysAllocStringA(psr->cStr);
  2094. break;
  2095. default:
  2096. *pbstr = NULL;
  2097. return E_FAIL;
  2098. }
  2099. return (*pbstr) ? S_OK : E_OUTOFMEMORY;
  2100. }
  2101. HRESULT DupWideToAnsi(LPCWSTR pwsz, LPSTR *ppsz)
  2102. {
  2103. UINT cch = WideCharToMultiByte(CP_ACP, 0, pwsz, -1, NULL, 0, NULL, NULL) + 1;
  2104. *ppsz = CoTaskMemAlloc(cch * sizeof(**ppsz));
  2105. if (*ppsz)
  2106. {
  2107. SHUnicodeToAnsi(pwsz, *ppsz, cch);
  2108. return S_OK;
  2109. }
  2110. return E_OUTOFMEMORY;
  2111. }
  2112. HRESULT DupAnsiToAnsi(LPCSTR psz, LPSTR *ppsz)
  2113. {
  2114. *ppsz = (LPSTR)CoTaskMemAlloc((lstrlenA(psz) + 1) * sizeof(**ppsz));
  2115. if (*ppsz)
  2116. {
  2117. lstrcpyA(*ppsz, psz);
  2118. return S_OK;
  2119. }
  2120. return E_OUTOFMEMORY;
  2121. }
  2122. LWSTDAPI StrRetToStrA(STRRET *psr, LPCITEMIDLIST pidl, CHAR **ppsz)
  2123. {
  2124. HRESULT hres;
  2125. LPWSTR pwsz;
  2126. switch (psr->uType)
  2127. {
  2128. case STRRET_WSTR:
  2129. hres = DupWideToAnsi(psr->pOleStr, ppsz);
  2130. pwsz = psr->pOleStr;
  2131. psr->pOleStr = NULL; // avoid alias
  2132. CoTaskMemFree(pwsz);
  2133. break;
  2134. case STRRET_OFFSET:
  2135. hres = DupAnsiToAnsi(STRRET_OFFPTR(pidl, psr), ppsz);
  2136. break;
  2137. case STRRET_CSTR:
  2138. hres = DupAnsiToAnsi(psr->cStr, ppsz);
  2139. break;
  2140. default:
  2141. *ppsz = NULL;
  2142. hres = E_FAIL;
  2143. }
  2144. return hres;
  2145. }
  2146. STDAPI StrRetToBufA(STRRET *psr, LPCITEMIDLIST pidl, LPSTR pszBuf, UINT cchBuf)
  2147. {
  2148. HRESULT hres = E_FAIL;
  2149. switch (psr->uType)
  2150. {
  2151. case STRRET_WSTR:
  2152. {
  2153. LPWSTR pszStr = psr->pOleStr; // temp copy because SHUnicodeToAnsi may overwrite buffer
  2154. if (pszStr)
  2155. {
  2156. SHUnicodeToAnsi(pszStr, pszBuf, cchBuf);
  2157. CoTaskMemFree(pszStr);
  2158. // Make sure no one thinks things are allocated still
  2159. psr->uType = STRRET_CSTR;
  2160. psr->cStr[0] = 0;
  2161. hres = S_OK;
  2162. }
  2163. }
  2164. break;
  2165. case STRRET_CSTR:
  2166. SHAnsiToAnsi(psr->cStr, pszBuf, cchBuf);
  2167. hres = S_OK;
  2168. break;
  2169. case STRRET_OFFSET:
  2170. if (pidl)
  2171. {
  2172. SHAnsiToAnsi(STRRET_OFFPTR(pidl, psr), pszBuf, cchBuf);
  2173. hres = S_OK;
  2174. }
  2175. break;
  2176. }
  2177. if (FAILED(hres) && cchBuf)
  2178. *pszBuf = 0;
  2179. return hres;
  2180. }
  2181. STDAPI StrRetToBufW(STRRET *psr, LPCITEMIDLIST pidl, LPWSTR pszBuf, UINT cchBuf)
  2182. {
  2183. HRESULT hres = E_FAIL;
  2184. switch (psr->uType)
  2185. {
  2186. case STRRET_WSTR:
  2187. {
  2188. LPWSTR pwszTmp = psr->pOleStr;
  2189. if (pwszTmp)
  2190. {
  2191. StrCpyNW(pszBuf, pwszTmp, cchBuf);
  2192. CoTaskMemFree(pwszTmp);
  2193. // Make sure no one thinks things are allocated still
  2194. psr->uType = STRRET_CSTR;
  2195. psr->cStr[0] = 0;
  2196. hres = S_OK;
  2197. }
  2198. }
  2199. break;
  2200. case STRRET_CSTR:
  2201. SHAnsiToUnicode(psr->cStr, pszBuf, cchBuf);
  2202. hres = S_OK;
  2203. break;
  2204. case STRRET_OFFSET:
  2205. if (pidl)
  2206. {
  2207. SHAnsiToUnicode(STRRET_OFFPTR(pidl, psr), pszBuf, cchBuf);
  2208. hres = S_OK;
  2209. }
  2210. break;
  2211. }
  2212. if (FAILED(hres) && cchBuf)
  2213. *pszBuf = 0;
  2214. return hres;
  2215. }
  2216. // dupe a string using the task allocator for returing from a COM interface
  2217. //
  2218. STDAPI SHStrDupA(LPCSTR psz, WCHAR **ppwsz)
  2219. {
  2220. WCHAR *pwsz;
  2221. DWORD cch;
  2222. RIPMSG(psz && IS_VALID_STRING_PTRA(psz, -1), "SHStrDupA: Caller passed invalid psz");
  2223. if (psz)
  2224. {
  2225. cch = MultiByteToWideChar(CP_ACP, 0, psz, -1, NULL, 0);
  2226. pwsz = (WCHAR *)CoTaskMemAlloc((cch + 1) * SIZEOF(WCHAR));
  2227. }
  2228. else
  2229. pwsz = NULL;
  2230. *((PVOID UNALIGNED64 *) ppwsz) = pwsz;
  2231. if (pwsz)
  2232. {
  2233. MultiByteToWideChar(CP_ACP, 0, psz, -1, *ppwsz, cch);
  2234. return S_OK;
  2235. }
  2236. return E_OUTOFMEMORY;
  2237. }
  2238. // dupe a string using the task allocator for returing from a COM interface
  2239. // Sometimes, due to structure packing, the pointer we get is not properly
  2240. // aligned for Win64, so we have to do UNALIGNED64.
  2241. //
  2242. STDAPI SHStrDupW(LPCWSTR psz, WCHAR **ppwsz)
  2243. {
  2244. WCHAR *pwsz;
  2245. int cb;
  2246. RIPMSG(psz && IS_VALID_STRING_PTRW(psz, -1), "SHStrDupW: Caller passed invalid psz");
  2247. if (psz)
  2248. {
  2249. cb = (lstrlenW(psz) + 1) * SIZEOF(WCHAR);
  2250. pwsz = (WCHAR *)CoTaskMemAlloc(cb);
  2251. }
  2252. else
  2253. pwsz = NULL;
  2254. *((PVOID UNALIGNED64 *) ppwsz) = pwsz;
  2255. if (pwsz)
  2256. {
  2257. CopyMemory(pwsz, psz, cb);
  2258. return S_OK;
  2259. }
  2260. return E_OUTOFMEMORY;
  2261. }
  2262. STDAPI_(int) StrCmpLogicalW(PCWSTR psz1, PCWSTR psz2)
  2263. {
  2264. int iRet = 0;
  2265. int iCmpNum = 0;
  2266. while (iRet == 0 && (*psz1 || *psz2))
  2267. {
  2268. int cch1 = 0;
  2269. int cch2 = 0;
  2270. BOOL fIsDigit1 = IS_DIGITW(*psz1);
  2271. BOOL fIsDigit2 = IS_DIGITW(*psz2);
  2272. ASSERT(fIsDigit1 == TRUE || fIsDigit1 == FALSE);
  2273. ASSERT(fIsDigit2 == TRUE || fIsDigit2 == FALSE);
  2274. // using bit wise XOR as logical XOR
  2275. // if the numbers are mismatched then n
  2276. if (fIsDigit1 ^ fIsDigit2)
  2277. {
  2278. iRet = _StrCmpLocaleW(NORM_IGNORECASE, psz1, -1, psz2, -1);
  2279. }
  2280. else if (fIsDigit1 && fIsDigit2)
  2281. {
  2282. int cchZero1 = 0;
  2283. int cchZero2 = 0;
  2284. // eat leading zeros
  2285. while (*psz1 == TEXT('0'))
  2286. {
  2287. psz1++;
  2288. cchZero1++;
  2289. }
  2290. while (*psz2 == TEXT('0'))
  2291. {
  2292. psz2++;
  2293. cchZero2++;
  2294. }
  2295. while (IS_DIGITW(psz1[cch1]))
  2296. cch1++;
  2297. while (IS_DIGITW(psz2[cch2]))
  2298. cch2++;
  2299. if (cch1 != cch2)
  2300. {
  2301. iRet = cch1 > cch2 ? 1 : -1;
  2302. }
  2303. else
  2304. {
  2305. // remember the first numerical difference
  2306. iRet = _StrCmpLocaleW(NORM_IGNORECASE, psz1, cch1, psz2, cch2);
  2307. if (iRet == 0 && iCmpNum == 0 && cchZero1 != cchZero2)
  2308. {
  2309. iCmpNum = cchZero2 > cchZero1 ? 1 : -1;
  2310. }
  2311. }
  2312. }
  2313. else
  2314. {
  2315. while (psz1[cch1] && !IS_DIGITW(psz1[cch1]))
  2316. cch1++;
  2317. while (psz2[cch2] && !IS_DIGITW(psz2[cch2]))
  2318. cch2++;
  2319. iRet = _StrCmpLocaleW(NORM_IGNORECASE, psz1, cch1, psz2, cch2);
  2320. }
  2321. // at this point they should be numbers or terminators or different
  2322. psz1 = &psz1[cch1];
  2323. psz2 = &psz2[cch2];
  2324. }
  2325. if (iRet == 0 && iCmpNum)
  2326. iRet = iCmpNum;
  2327. return iRet;
  2328. }
  2329. STDAPI_(DWORD) StrCatChainW(LPWSTR pszDst, DWORD cchDst, DWORD ichAt, LPCWSTR pszSrc)
  2330. {
  2331. RIPMSG(pszDst && IS_VALID_STRING_PTRW(pszDst, -1) && (DWORD)lstrlenW(pszDst)<cchDst && IS_VALID_WRITE_BUFFER(pszDst, WCHAR, cchDst), "StrCatChainW: Caller passed invalid pszDst");
  2332. RIPMSG(pszSrc && IS_VALID_STRING_PTRW(pszSrc, -1), "StrCatChainW: Caller passed invalid pszSrc");
  2333. if (ichAt == -1)
  2334. ichAt = lstrlenW(pszDst);
  2335. if (cchDst > 0)
  2336. {
  2337. #ifdef DEBUG
  2338. if (ichAt < cchDst)
  2339. DEBUGWhackPathBufferW(pszDst+ichAt, cchDst-ichAt);
  2340. #endif
  2341. while (ichAt < cchDst)
  2342. {
  2343. if (!(pszDst[ichAt] = *pszSrc++))
  2344. break;
  2345. ichAt++;
  2346. }
  2347. // check to make sure we copied a NULL
  2348. if (ichAt == cchDst)
  2349. pszDst[ichAt-1] = 0;
  2350. }
  2351. return ichAt;
  2352. }