Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1574 lines
37 KiB

  1. /*++
  2. Copyright (c) 1998 Microsoft Corporation
  3. Module Name:
  4. bsstring.cpp
  5. Abstract:
  6. This module implements the CBsString class. This class manages character
  7. arrays in a similar manner as the CString class in VC++. In fact, this
  8. class is a copy of the CString class with the MFC specific stuff ripped
  9. out since LTS doesn't use MTF.
  10. Author:
  11. Stefan R. Steiner [SSteiner] 1-Mar-1998
  12. Revision History:
  13. Stefan R. Steiner [SSteiner] 10-Apr-2000
  14. Added fixed allocator code and resynced with MFC 6 SR-1 code
  15. --*/
  16. //#include "stdafx.h"
  17. #include <windows.h>
  18. #include "bsstring.hxx"
  19. #include <malloc.h>
  20. #ifdef _DEBUG
  21. #undef THIS_FILE
  22. static char THIS_FILE[] = __FILE__;
  23. #endif
  24. struct _BSAFX_DOUBLE { BYTE doubleBits[sizeof(double)]; };
  25. struct _BSAFX_FLOAT { BYTE floatBits[sizeof(float)]; };
  26. // #define new DEBUG_NEW
  27. /////////////////////////////////////////////////////////////////////////////
  28. // static class data, special inlines
  29. TCHAR bsafxChNil = '\0';
  30. // For an empty string, m_pchData will point here
  31. // (note: avoids special case of checking for NULL m_pchData)
  32. // empty string data (and locked)
  33. static int _bsafxInitData[] = { -1, 0, 0, 0 };
  34. static CBsStringData* _bsafxDataNil = (CBsStringData*)&_bsafxInitData;
  35. static LPCTSTR _bsafxPchNil = (LPCTSTR)(((BYTE*)&_bsafxInitData)+sizeof(CBsStringData));
  36. // special function to make bsafxEmptyString work even during initialization
  37. const CBsString& BSAFXAPI BsAfxGetEmptyString()
  38. { return *(CBsString*)&_bsafxPchNil; }
  39. //////////////////////////////////////////////////////////////////////////////
  40. // Construction/Destruction
  41. CBsString::CBsString()
  42. {
  43. Init();
  44. }
  45. CBsString::CBsString(const CBsString& stringSrc)
  46. {
  47. ASSERT(stringSrc.GetData()->nRefs != 0);
  48. if (stringSrc.GetData()->nRefs >= 0)
  49. {
  50. ASSERT(stringSrc.GetData() != _bsafxDataNil);
  51. m_pchData = stringSrc.m_pchData;
  52. InterlockedIncrement(&GetData()->nRefs);
  53. }
  54. else
  55. {
  56. Init();
  57. *this = stringSrc.m_pchData;
  58. }
  59. }
  60. CBsString::CBsString(GUID guid)
  61. {
  62. Init();
  63. AllocBuffer(38);
  64. _stprintf( m_pchData, _T("{%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X}"),
  65. guid.Data1, guid.Data2, guid.Data3, guid.Data4[0], guid.Data4[1],
  66. guid.Data4[2], guid.Data4[3], guid.Data4[4], guid.Data4[5],
  67. guid.Data4[6], guid.Data4[7] );
  68. }
  69. #ifndef _DEBUG
  70. #pragma warning(disable: 4074)
  71. #pragma init_seg(compiler)
  72. #define ROUND(x,y) (((x)+(y-1))&~(y-1))
  73. #define ROUND4(x) ROUND(x, 4)
  74. static CBsFixedAlloc _bsafxAlloc8(ROUND4(9*sizeof(TCHAR)+sizeof(CBsStringData)));
  75. static CBsFixedAlloc _bsafxAlloc16(ROUND4(17*sizeof(TCHAR)+sizeof(CBsStringData)));
  76. static CBsFixedAlloc _bsafxAlloc32(ROUND4(33*sizeof(TCHAR)+sizeof(CBsStringData)));
  77. static CBsFixedAlloc _bsafxAlloc64(ROUND4(65*sizeof(TCHAR)+sizeof(CBsStringData)));
  78. static CBsFixedAlloc _bsafxAlloc128(ROUND4(129*sizeof(TCHAR)+sizeof(CBsStringData)));
  79. static CBsFixedAlloc _bsafxAlloc256(ROUND4(257*sizeof(TCHAR)+sizeof(CBsStringData)));
  80. static CBsFixedAlloc _bsafxAlloc512(ROUND4(513*sizeof(TCHAR)+sizeof(CBsStringData)));
  81. #endif //!_DEBUG
  82. void CBsString::AllocBuffer(int nLen)
  83. // always allocate one extra character for '\0' termination
  84. // assumes [optimistically] that data length will equal allocation length
  85. {
  86. ASSERT(nLen >= 0);
  87. ASSERT(nLen <= INT_MAX-1); // max size (enough room for 1 extra)
  88. if (nLen == 0)
  89. Init();
  90. else
  91. {
  92. CBsStringData* pData;
  93. #ifndef _DEBUG
  94. if (nLen <= 8)
  95. {
  96. pData = (CBsStringData*)_bsafxAlloc8.Alloc();
  97. pData->nAllocLength = 8;
  98. }
  99. else if (nLen <= 16)
  100. {
  101. pData = (CBsStringData*)_bsafxAlloc16.Alloc();
  102. pData->nAllocLength = 16;
  103. }
  104. else if (nLen <= 32)
  105. {
  106. pData = (CBsStringData*)_bsafxAlloc32.Alloc();
  107. pData->nAllocLength = 32;
  108. }
  109. else if (nLen <= 64)
  110. {
  111. pData = (CBsStringData*)_bsafxAlloc64.Alloc();
  112. pData->nAllocLength = 64;
  113. }
  114. else if (nLen <= 128)
  115. {
  116. pData = (CBsStringData*)_bsafxAlloc128.Alloc();
  117. pData->nAllocLength = 128;
  118. }
  119. else if (nLen <= 256)
  120. {
  121. pData = (CBsStringData*)_bsafxAlloc256.Alloc();
  122. pData->nAllocLength = 256;
  123. }
  124. else if (nLen <= 512)
  125. {
  126. pData = (CBsStringData*)_bsafxAlloc512.Alloc();
  127. pData->nAllocLength = 512;
  128. }
  129. else
  130. #endif
  131. {
  132. pData = (CBsStringData*)
  133. new BYTE[sizeof(CBsStringData) + (nLen+1)*sizeof(TCHAR)];
  134. if ( pData == NULL )
  135. throw E_OUTOFMEMORY;
  136. pData->nAllocLength = nLen;
  137. }
  138. pData->nRefs = 1;
  139. pData->data()[nLen] = '\0';
  140. pData->nDataLength = nLen;
  141. m_pchData = pData->data();
  142. }
  143. }
  144. void FASTCALL CBsString::FreeData(CBsStringData* pData)
  145. {
  146. #ifndef _DEBUG
  147. int nLen = pData->nAllocLength;
  148. if (nLen == 8)
  149. _bsafxAlloc8.Free(pData);
  150. else if (nLen == 16)
  151. _bsafxAlloc16.Free(pData);
  152. else if (nLen == 32)
  153. _bsafxAlloc32.Free(pData);
  154. else if (nLen == 64)
  155. _bsafxAlloc64.Free(pData);
  156. else if (nLen == 128)
  157. _bsafxAlloc128.Free(pData);
  158. else if (nLen == 256)
  159. _bsafxAlloc256.Free(pData);
  160. else if (nLen == 512)
  161. _bsafxAlloc512.Free(pData);
  162. else
  163. {
  164. ASSERT(nLen > 512);
  165. delete[] (BYTE*)pData;
  166. }
  167. #else
  168. delete[] (BYTE*)pData;
  169. #endif
  170. }
  171. void CBsString::Release()
  172. {
  173. if (GetData() != _bsafxDataNil)
  174. {
  175. ASSERT(GetData()->nRefs != 0);
  176. if (InterlockedDecrement(&GetData()->nRefs) <= 0)
  177. FreeData(GetData());
  178. Init();
  179. }
  180. }
  181. void PASCAL CBsString::Release(CBsStringData* pData)
  182. {
  183. if (pData != _bsafxDataNil)
  184. {
  185. ASSERT(pData->nRefs != 0);
  186. if (InterlockedDecrement(&pData->nRefs) <= 0)
  187. FreeData(pData);
  188. }
  189. }
  190. void CBsString::Empty()
  191. {
  192. if (GetData()->nDataLength == 0)
  193. return;
  194. if (GetData()->nRefs >= 0)
  195. Release();
  196. else
  197. *this = &bsafxChNil;
  198. ASSERT(GetData()->nDataLength == 0);
  199. ASSERT(GetData()->nRefs < 0 || GetData()->nAllocLength == 0);
  200. }
  201. void CBsString::CopyBeforeWrite()
  202. {
  203. if (GetData()->nRefs > 1)
  204. {
  205. CBsStringData* pData = GetData();
  206. Release();
  207. AllocBuffer(pData->nDataLength);
  208. memcpy(m_pchData, pData->data(), (pData->nDataLength+1)*sizeof(TCHAR));
  209. }
  210. ASSERT(GetData()->nRefs <= 1);
  211. }
  212. void CBsString::AllocBeforeWrite(int nLen)
  213. {
  214. if (GetData()->nRefs > 1 || nLen > GetData()->nAllocLength)
  215. {
  216. Release();
  217. AllocBuffer(nLen);
  218. }
  219. ASSERT(GetData()->nRefs <= 1);
  220. }
  221. CBsString::~CBsString()
  222. // free any attached data
  223. {
  224. if (GetData() != _bsafxDataNil)
  225. {
  226. if (InterlockedDecrement(&GetData()->nRefs) <= 0)
  227. FreeData(GetData());
  228. }
  229. }
  230. //////////////////////////////////////////////////////////////////////////////
  231. // Helpers for the rest of the implementation
  232. void CBsString::AllocCopy(CBsString& dest, int nCopyLen, int nCopyIndex,
  233. int nExtraLen) const
  234. {
  235. // will clone the data attached to this string
  236. // allocating 'nExtraLen' characters
  237. // Places results in uninitialized string 'dest'
  238. // Will copy the part or all of original data to start of new string
  239. int nNewLen = nCopyLen + nExtraLen;
  240. if (nNewLen == 0)
  241. {
  242. dest.Init();
  243. }
  244. else
  245. {
  246. dest.AllocBuffer(nNewLen);
  247. memcpy(dest.m_pchData, m_pchData+nCopyIndex, nCopyLen*sizeof(TCHAR));
  248. }
  249. }
  250. //////////////////////////////////////////////////////////////////////////////
  251. // More sophisticated construction
  252. CBsString::CBsString(LPCTSTR lpsz)
  253. {
  254. Init();
  255. int nLen = SafeStrlen(lpsz);
  256. if (nLen != 0)
  257. {
  258. AllocBuffer(nLen);
  259. memcpy(m_pchData, lpsz, nLen*sizeof(TCHAR));
  260. }
  261. }
  262. /////////////////////////////////////////////////////////////////////////////
  263. // Special conversion constructors
  264. #ifdef _UNICODE
  265. CBsString::CBsString(LPCSTR lpsz)
  266. {
  267. Init();
  268. int nSrcLen = lpsz != NULL ? lstrlenA(lpsz) : 0;
  269. if (nSrcLen != 0)
  270. {
  271. AllocBuffer(nSrcLen);
  272. _mbstowcsz(m_pchData, lpsz, nSrcLen+1);
  273. ReleaseBuffer();
  274. }
  275. }
  276. #else //_UNICODE
  277. CBsString::CBsString(LPCWSTR lpsz)
  278. {
  279. Init();
  280. int nSrcLen = lpsz != NULL ? wcslen(lpsz) : 0;
  281. if (nSrcLen != 0)
  282. {
  283. AllocBuffer(nSrcLen*2);
  284. _wcstombsz(m_pchData, lpsz, (nSrcLen*2)+1);
  285. ReleaseBuffer();
  286. }
  287. }
  288. #endif //!_UNICODE
  289. //////////////////////////////////////////////////////////////////////////////
  290. // Assignment operators
  291. // All assign a new value to the string
  292. // (a) first see if the buffer is big enough
  293. // (b) if enough room, copy on top of old buffer, set size and type
  294. // (c) otherwise free old string data, and create a new one
  295. //
  296. // All routines return the new string (but as a 'const CBsString&' so that
  297. // assigning it again will cause a copy, eg: s1 = s2 = "hi there".
  298. //
  299. void CBsString::AssignCopy(int nSrcLen, LPCTSTR lpszSrcData)
  300. {
  301. AllocBeforeWrite(nSrcLen);
  302. memcpy(m_pchData, lpszSrcData, nSrcLen*sizeof(TCHAR));
  303. GetData()->nDataLength = nSrcLen;
  304. m_pchData[nSrcLen] = '\0';
  305. }
  306. const CBsString& CBsString::operator=(const CBsString& stringSrc)
  307. {
  308. if (m_pchData != stringSrc.m_pchData)
  309. {
  310. if ((GetData()->nRefs < 0 && GetData() != _bsafxDataNil) ||
  311. stringSrc.GetData()->nRefs < 0)
  312. {
  313. // actual copy necessary since one of the strings is locked
  314. AssignCopy(stringSrc.GetData()->nDataLength, stringSrc.m_pchData);
  315. }
  316. else
  317. {
  318. // can just copy references around
  319. Release();
  320. ASSERT(stringSrc.GetData() != _bsafxDataNil);
  321. m_pchData = stringSrc.m_pchData;
  322. InterlockedIncrement(&GetData()->nRefs);
  323. }
  324. }
  325. return *this;
  326. }
  327. const CBsString& CBsString::operator=(LPCTSTR lpsz)
  328. {
  329. ASSERT(lpsz == NULL || BsAfxIsValidString(lpsz));
  330. AssignCopy(SafeStrlen(lpsz), lpsz);
  331. return *this;
  332. }
  333. /////////////////////////////////////////////////////////////////////////////
  334. // Special conversion assignment
  335. #ifdef _UNICODE
  336. const CBsString& CBsString::operator=(LPCSTR lpsz)
  337. {
  338. int nSrcLen = lpsz != NULL ? lstrlenA(lpsz) : 0;
  339. AllocBeforeWrite(nSrcLen);
  340. _mbstowcsz(m_pchData, lpsz, nSrcLen+1);
  341. ReleaseBuffer();
  342. return *this;
  343. }
  344. #else //!_UNICODE
  345. const CBsString& CBsString::operator=(LPCWSTR lpsz)
  346. {
  347. int nSrcLen = lpsz != NULL ? wcslen(lpsz) : 0;
  348. AllocBeforeWrite(nSrcLen*2);
  349. _wcstombsz(m_pchData, lpsz, (nSrcLen*2)+1);
  350. ReleaseBuffer();
  351. return *this;
  352. }
  353. #endif //!_UNICODE
  354. //////////////////////////////////////////////////////////////////////////////
  355. // concatenation
  356. // NOTE: "operator+" is done as friend functions for simplicity
  357. // There are three variants:
  358. // CBsString + CBsString
  359. // and for ? = TCHAR, LPCTSTR
  360. // CBsString + ?
  361. // ? + CBsString
  362. void CBsString::ConcatCopy(int nSrc1Len, LPCTSTR lpszSrc1Data,
  363. int nSrc2Len, LPCTSTR lpszSrc2Data)
  364. {
  365. // -- master concatenation routine
  366. // Concatenate two sources
  367. // -- assume that 'this' is a new CBsString object
  368. int nNewLen = nSrc1Len + nSrc2Len;
  369. if (nNewLen != 0)
  370. {
  371. AllocBuffer(nNewLen);
  372. memcpy(m_pchData, lpszSrc1Data, nSrc1Len*sizeof(TCHAR));
  373. memcpy(m_pchData+nSrc1Len, lpszSrc2Data, nSrc2Len*sizeof(TCHAR));
  374. }
  375. }
  376. CBsString BSAFXAPI operator+(const CBsString& string1, const CBsString& string2)
  377. {
  378. CBsString s;
  379. s.ConcatCopy(string1.GetData()->nDataLength, string1.m_pchData,
  380. string2.GetData()->nDataLength, string2.m_pchData);
  381. return s;
  382. }
  383. CBsString BSAFXAPI operator+(const CBsString& string, LPCTSTR lpsz)
  384. {
  385. ASSERT(lpsz == NULL || BsAfxIsValidString(lpsz));
  386. CBsString s;
  387. s.ConcatCopy(string.GetData()->nDataLength, string.m_pchData,
  388. CBsString::SafeStrlen(lpsz), lpsz);
  389. return s;
  390. }
  391. CBsString BSAFXAPI operator+(LPCTSTR lpsz, const CBsString& string)
  392. {
  393. ASSERT(lpsz == NULL || BsAfxIsValidString(lpsz));
  394. CBsString s;
  395. s.ConcatCopy(CBsString::SafeStrlen(lpsz), lpsz, string.GetData()->nDataLength,
  396. string.m_pchData);
  397. return s;
  398. }
  399. //////////////////////////////////////////////////////////////////////////////
  400. // concatenate in place
  401. void CBsString::ConcatInPlace(int nSrcLen, LPCTSTR lpszSrcData)
  402. {
  403. // -- the main routine for += operators
  404. // concatenating an empty string is a no-op!
  405. if (nSrcLen == 0)
  406. return;
  407. // if the buffer is too small, or we have a width mis-match, just
  408. // allocate a new buffer (slow but sure)
  409. if (GetData()->nRefs > 1 || GetData()->nDataLength + nSrcLen > GetData()->nAllocLength)
  410. {
  411. // we have to grow the buffer, use the ConcatCopy routine
  412. CBsStringData* pOldData = GetData();
  413. ConcatCopy(GetData()->nDataLength, m_pchData, nSrcLen, lpszSrcData);
  414. ASSERT(pOldData != NULL);
  415. CBsString::Release(pOldData);
  416. }
  417. else
  418. {
  419. // fast concatenation when buffer big enough
  420. memcpy(m_pchData+GetData()->nDataLength, lpszSrcData, nSrcLen*sizeof(TCHAR));
  421. GetData()->nDataLength += nSrcLen;
  422. ASSERT(GetData()->nDataLength <= GetData()->nAllocLength);
  423. m_pchData[GetData()->nDataLength] = '\0';
  424. }
  425. }
  426. const CBsString& CBsString::operator+=(LPCTSTR lpsz)
  427. {
  428. ASSERT(lpsz == NULL || BsAfxIsValidString(lpsz));
  429. ConcatInPlace(SafeStrlen(lpsz), lpsz);
  430. return *this;
  431. }
  432. const CBsString& CBsString::operator+=(TCHAR ch)
  433. {
  434. ConcatInPlace(1, &ch);
  435. return *this;
  436. }
  437. const CBsString& CBsString::operator+=(const CBsString& string)
  438. {
  439. ConcatInPlace(string.GetData()->nDataLength, string.m_pchData);
  440. return *this;
  441. }
  442. ///////////////////////////////////////////////////////////////////////////////
  443. // Advanced direct buffer access
  444. LPTSTR CBsString::GetBuffer(int nMinBufLength)
  445. {
  446. ASSERT(nMinBufLength >= 0);
  447. if (GetData()->nRefs > 1 || nMinBufLength > GetData()->nAllocLength)
  448. {
  449. // we have to grow the buffer
  450. CBsStringData* pOldData = GetData();
  451. int nOldLen = GetData()->nDataLength; // AllocBuffer will tromp it
  452. if (nMinBufLength < nOldLen)
  453. nMinBufLength = nOldLen;
  454. AllocBuffer(nMinBufLength);
  455. memcpy(m_pchData, pOldData->data(), (nOldLen+1)*sizeof(TCHAR));
  456. GetData()->nDataLength = nOldLen;
  457. CBsString::Release(pOldData);
  458. }
  459. ASSERT(GetData()->nRefs <= 1);
  460. // return a pointer to the character storage for this string
  461. ASSERT(m_pchData != NULL);
  462. return m_pchData;
  463. }
  464. void CBsString::ReleaseBuffer(int nNewLength)
  465. {
  466. CopyBeforeWrite(); // just in case GetBuffer was not called
  467. if (nNewLength == -1)
  468. nNewLength = lstrlen(m_pchData); // zero terminated
  469. ASSERT(nNewLength <= GetData()->nAllocLength);
  470. GetData()->nDataLength = nNewLength;
  471. m_pchData[nNewLength] = '\0';
  472. }
  473. LPTSTR CBsString::GetBufferSetLength(int nNewLength)
  474. {
  475. ASSERT(nNewLength >= 0);
  476. GetBuffer(nNewLength);
  477. GetData()->nDataLength = nNewLength;
  478. m_pchData[nNewLength] = '\0';
  479. return m_pchData;
  480. }
  481. void CBsString::FreeExtra()
  482. {
  483. ASSERT(GetData()->nDataLength <= GetData()->nAllocLength);
  484. if (GetData()->nDataLength != GetData()->nAllocLength)
  485. {
  486. CBsStringData* pOldData = GetData();
  487. AllocBuffer(GetData()->nDataLength);
  488. memcpy(m_pchData, pOldData->data(), pOldData->nDataLength*sizeof(TCHAR));
  489. ASSERT(m_pchData[GetData()->nDataLength] == '\0');
  490. CBsString::Release(pOldData);
  491. }
  492. ASSERT(GetData() != NULL);
  493. }
  494. LPTSTR CBsString::LockBuffer()
  495. {
  496. LPTSTR lpsz = GetBuffer(0);
  497. GetData()->nRefs = -1;
  498. return lpsz;
  499. }
  500. void CBsString::UnlockBuffer()
  501. {
  502. ASSERT(GetData()->nRefs == -1);
  503. if (GetData() != _bsafxDataNil)
  504. GetData()->nRefs = 1;
  505. }
  506. ///////////////////////////////////////////////////////////////////////////////
  507. // Commonly used routines (rarely used routines in STREX.CPP)
  508. int CBsString::Find(TCHAR ch) const
  509. {
  510. return Find(ch, 0);
  511. }
  512. int CBsString::Find(TCHAR ch, int nStart) const
  513. {
  514. int nLength = GetData()->nDataLength;
  515. if (nStart >= nLength)
  516. return -1;
  517. // find first single character
  518. LPTSTR lpsz = _tcschr(m_pchData + nStart, (_TUCHAR)ch);
  519. // return -1 if not found and index otherwise
  520. return (lpsz == NULL) ? -1 : (int)(lpsz - m_pchData);
  521. }
  522. int CBsString::FindOneOf(LPCTSTR lpszCharSet) const
  523. {
  524. ASSERT(BsAfxIsValidString(lpszCharSet));
  525. LPTSTR lpsz = _tcspbrk(m_pchData, lpszCharSet);
  526. return (lpsz == NULL) ? -1 : (int)(lpsz - m_pchData);
  527. }
  528. void CBsString::MakeUpper()
  529. {
  530. CopyBeforeWrite();
  531. _tcsupr(m_pchData);
  532. }
  533. void CBsString::MakeLower()
  534. {
  535. CopyBeforeWrite();
  536. _tcslwr(m_pchData);
  537. }
  538. void CBsString::MakeReverse()
  539. {
  540. CopyBeforeWrite();
  541. _tcsrev(m_pchData);
  542. }
  543. void CBsString::SetAt(int nIndex, TCHAR ch)
  544. {
  545. ASSERT(nIndex >= 0);
  546. ASSERT(nIndex < GetData()->nDataLength);
  547. CopyBeforeWrite();
  548. m_pchData[nIndex] = ch;
  549. }
  550. #ifndef _UNICODE
  551. void CBsString::AnsiToOem()
  552. {
  553. CopyBeforeWrite();
  554. ::AnsiToOem(m_pchData, m_pchData);
  555. }
  556. void CBsString::OemToAnsi()
  557. {
  558. CopyBeforeWrite();
  559. ::OemToAnsi(m_pchData, m_pchData);
  560. }
  561. #endif
  562. ///////////////////////////////////////////////////////////////////////////////
  563. // CBsString conversion helpers (these use the current system locale)
  564. int BSAFX_CDECL _wcstombsz(char* mbstr, const wchar_t* wcstr, size_t count)
  565. {
  566. if (count == 0 && mbstr != NULL)
  567. return 0;
  568. int result = ::WideCharToMultiByte(CP_ACP, 0, wcstr, -1,
  569. mbstr, (INT)count, NULL, NULL);
  570. ASSERT(mbstr == NULL || result <= (int)count);
  571. if (result > 0)
  572. mbstr[result-1] = 0;
  573. return result;
  574. }
  575. int BSAFX_CDECL _mbstowcsz(wchar_t* wcstr, const char* mbstr, size_t count)
  576. {
  577. if (count == 0 && wcstr != NULL)
  578. return 0;
  579. int result = ::MultiByteToWideChar(CP_ACP, 0, mbstr, -1,
  580. wcstr, (INT)count);
  581. ASSERT(wcstr == NULL || result <= (int)count);
  582. if (result > 0)
  583. wcstr[result-1] = 0;
  584. return result;
  585. }
  586. LPWSTR BSAFXAPI BsAfxA2WHelper(LPWSTR lpw, LPCSTR lpa, int nChars)
  587. {
  588. if (lpa == NULL)
  589. return NULL;
  590. ASSERT(lpw != NULL);
  591. // verify that no illegal character present
  592. // since lpw was allocated based on the size of lpa
  593. // don't worry about the number of chars
  594. lpw[0] = '\0';
  595. MultiByteToWideChar(CP_ACP, 0, lpa, -1, lpw, nChars);
  596. return lpw;
  597. UNREFERENCED_PARAMETER( nChars );
  598. }
  599. LPSTR BSAFXAPI BsAfxW2AHelper(LPSTR lpa, LPCWSTR lpw, int nChars)
  600. {
  601. if (lpw == NULL)
  602. return NULL;
  603. ASSERT(lpa != NULL);
  604. // verify that no illegal character present
  605. // since lpa was allocated based on the size of lpw
  606. // don't worry about the number of chars
  607. lpa[0] = '\0';
  608. WideCharToMultiByte(CP_ACP, 0, lpw, -1, lpa, nChars, NULL, NULL);
  609. return lpa;
  610. UNREFERENCED_PARAMETER( nChars );
  611. }
  612. //
  613. // the following is from strex.cpp
  614. //
  615. //////////////////////////////////////////////////////////////////////////////
  616. // More sophisticated construction
  617. CBsString::CBsString(TCHAR ch, int nLength)
  618. {
  619. Init();
  620. if (nLength >= 1)
  621. {
  622. AllocBuffer(nLength);
  623. #ifdef _UNICODE
  624. for (int i = 0; i < nLength; i++)
  625. m_pchData[i] = ch;
  626. #else
  627. memset(m_pchData, ch, nLength);
  628. #endif
  629. }
  630. }
  631. CBsString::CBsString(LPCTSTR lpch, int nLength)
  632. {
  633. Init();
  634. if (nLength != 0)
  635. {
  636. ASSERT(BsAfxIsValidAddress(lpch, nLength, FALSE));
  637. AllocBuffer(nLength);
  638. memcpy(m_pchData, lpch, nLength*sizeof(TCHAR));
  639. }
  640. }
  641. /////////////////////////////////////////////////////////////////////////////
  642. // Special conversion constructors
  643. #ifdef _UNICODE
  644. CBsString::CBsString(LPCSTR lpsz, int nLength)
  645. {
  646. Init();
  647. if (nLength != 0)
  648. {
  649. AllocBuffer(nLength);
  650. int n = ::MultiByteToWideChar(CP_ACP, 0, lpsz, nLength, m_pchData, nLength+1);
  651. ReleaseBuffer(n >= 0 ? n : -1);
  652. }
  653. }
  654. #else //_UNICODE
  655. CBsString::CBsString(LPCWSTR lpsz, int nLength)
  656. {
  657. Init();
  658. if (nLength != 0)
  659. {
  660. AllocBuffer(nLength*2);
  661. int n = ::WideCharToMultiByte(CP_ACP, 0, lpsz, nLength, m_pchData,
  662. (nLength*2)+1, NULL, NULL);
  663. ReleaseBuffer(n >= 0 ? n : -1);
  664. }
  665. }
  666. #endif //!_UNICODE
  667. //////////////////////////////////////////////////////////////////////////////
  668. // Assignment operators
  669. const CBsString& CBsString::operator=(TCHAR ch)
  670. {
  671. AssignCopy(1, &ch);
  672. return *this;
  673. }
  674. //////////////////////////////////////////////////////////////////////////////
  675. // less common string expressions
  676. CBsString BSAFXAPI operator+(const CBsString& string1, TCHAR ch)
  677. {
  678. CBsString s;
  679. s.ConcatCopy(string1.GetData()->nDataLength, string1.m_pchData, 1, &ch);
  680. return s;
  681. }
  682. CBsString BSAFXAPI operator+(TCHAR ch, const CBsString& string)
  683. {
  684. CBsString s;
  685. s.ConcatCopy(1, &ch, string.GetData()->nDataLength, string.m_pchData);
  686. return s;
  687. }
  688. //////////////////////////////////////////////////////////////////////////////
  689. // Advanced manipulation
  690. int CBsString::Delete(int nIndex, int nCount /* = 1 */)
  691. {
  692. if (nIndex < 0)
  693. nIndex = 0;
  694. int nNewLength = GetData()->nDataLength;
  695. if (nCount > 0 && nIndex < nNewLength)
  696. {
  697. CopyBeforeWrite();
  698. int nBytesToCopy = nNewLength - (nIndex + nCount) + 1;
  699. memcpy(m_pchData + nIndex,
  700. m_pchData + nIndex + nCount, nBytesToCopy * sizeof(TCHAR));
  701. GetData()->nDataLength = nNewLength - nCount;
  702. }
  703. return nNewLength;
  704. }
  705. int CBsString::Insert(int nIndex, TCHAR ch)
  706. {
  707. CopyBeforeWrite();
  708. if (nIndex < 0)
  709. nIndex = 0;
  710. int nNewLength = GetData()->nDataLength;
  711. if (nIndex > nNewLength)
  712. nIndex = nNewLength;
  713. nNewLength++;
  714. if (GetData()->nAllocLength < nNewLength)
  715. {
  716. CBsStringData* pOldData = GetData();
  717. LPTSTR pstr = m_pchData;
  718. AllocBuffer(nNewLength);
  719. memcpy(m_pchData, pstr, (pOldData->nDataLength+1)*sizeof(TCHAR));
  720. CBsString::Release(pOldData);
  721. }
  722. // move existing bytes down
  723. memcpy(m_pchData + nIndex + 1,
  724. m_pchData + nIndex, (nNewLength-nIndex)*sizeof(TCHAR));
  725. m_pchData[nIndex] = ch;
  726. GetData()->nDataLength = nNewLength;
  727. return nNewLength;
  728. }
  729. int CBsString::Insert(int nIndex, LPCTSTR pstr)
  730. {
  731. if (nIndex < 0)
  732. nIndex = 0;
  733. int nInsertLength = SafeStrlen(pstr);
  734. int nNewLength = GetData()->nDataLength;
  735. if (nInsertLength > 0)
  736. {
  737. CopyBeforeWrite();
  738. if (nIndex > nNewLength)
  739. nIndex = nNewLength;
  740. nNewLength += nInsertLength;
  741. if (GetData()->nAllocLength < nNewLength)
  742. {
  743. CBsStringData* pOldData = GetData();
  744. LPTSTR pstr = m_pchData;
  745. AllocBuffer(nNewLength);
  746. memcpy(m_pchData, pstr, (pOldData->nDataLength+1)*sizeof(TCHAR));
  747. CBsString::Release(pOldData);
  748. }
  749. // move existing bytes down
  750. memcpy(m_pchData + nIndex + nInsertLength,
  751. m_pchData + nIndex,
  752. (nNewLength-nIndex-nInsertLength+1)*sizeof(TCHAR));
  753. memcpy(m_pchData + nIndex,
  754. pstr, nInsertLength*sizeof(TCHAR));
  755. GetData()->nDataLength = nNewLength;
  756. }
  757. return nNewLength;
  758. }
  759. int CBsString::Replace(TCHAR chOld, TCHAR chNew)
  760. {
  761. int nCount = 0;
  762. // short-circuit the nop case
  763. if (chOld != chNew)
  764. {
  765. // otherwise modify each character that matches in the string
  766. CopyBeforeWrite();
  767. LPTSTR psz = m_pchData;
  768. LPTSTR pszEnd = psz + GetData()->nDataLength;
  769. while (psz < pszEnd)
  770. {
  771. // replace instances of the specified character only
  772. if (*psz == chOld)
  773. {
  774. *psz = chNew;
  775. nCount++;
  776. }
  777. psz = _tcsinc(psz);
  778. }
  779. }
  780. return nCount;
  781. }
  782. int CBsString::Replace(LPCTSTR lpszOld, LPCTSTR lpszNew)
  783. {
  784. // can't have empty or NULL lpszOld
  785. int nSourceLen = SafeStrlen(lpszOld);
  786. if (nSourceLen == 0)
  787. return 0;
  788. int nReplacementLen = SafeStrlen(lpszNew);
  789. // loop once to figure out the size of the result string
  790. int nCount = 0;
  791. LPTSTR lpszStart = m_pchData;
  792. LPTSTR lpszEnd = m_pchData + GetData()->nDataLength;
  793. LPTSTR lpszTarget;
  794. while (lpszStart < lpszEnd)
  795. {
  796. while ((lpszTarget = _tcsstr(lpszStart, lpszOld)) != NULL)
  797. {
  798. nCount++;
  799. lpszStart = lpszTarget + nSourceLen;
  800. }
  801. lpszStart += lstrlen(lpszStart) + 1;
  802. }
  803. // if any changes were made, make them
  804. if (nCount > 0)
  805. {
  806. CopyBeforeWrite();
  807. // if the buffer is too small, just
  808. // allocate a new buffer (slow but sure)
  809. int nOldLength = GetData()->nDataLength;
  810. int nNewLength = nOldLength + (nReplacementLen-nSourceLen)*nCount;
  811. if (GetData()->nAllocLength < nNewLength || GetData()->nRefs > 1)
  812. {
  813. CBsStringData* pOldData = GetData();
  814. LPTSTR pstr = m_pchData;
  815. AllocBuffer(nNewLength);
  816. memcpy(m_pchData, pstr, pOldData->nDataLength*sizeof(TCHAR));
  817. CBsString::Release(pOldData);
  818. }
  819. // else, we just do it in-place
  820. lpszStart = m_pchData;
  821. lpszEnd = m_pchData + GetData()->nDataLength;
  822. // loop again to actually do the work
  823. while (lpszStart < lpszEnd)
  824. {
  825. while ( (lpszTarget = _tcsstr(lpszStart, lpszOld)) != NULL)
  826. {
  827. int nBalance = nOldLength - (int)(lpszTarget - m_pchData + nSourceLen);
  828. memmove(lpszTarget + nReplacementLen, lpszTarget + nSourceLen,
  829. nBalance * sizeof(TCHAR));
  830. memcpy(lpszTarget, lpszNew, nReplacementLen*sizeof(TCHAR));
  831. lpszStart = lpszTarget + nReplacementLen;
  832. lpszStart[nBalance] = '\0';
  833. nOldLength += (nReplacementLen - nSourceLen);
  834. }
  835. lpszStart += lstrlen(lpszStart) + 1;
  836. }
  837. ASSERT(m_pchData[nNewLength] == '\0');
  838. GetData()->nDataLength = nNewLength;
  839. }
  840. return nCount;
  841. }
  842. int CBsString::Remove(TCHAR chRemove)
  843. {
  844. CopyBeforeWrite();
  845. LPTSTR pstrSource = m_pchData;
  846. LPTSTR pstrDest = m_pchData;
  847. LPTSTR pstrEnd = m_pchData + GetData()->nDataLength;
  848. while (pstrSource < pstrEnd)
  849. {
  850. if (*pstrSource != chRemove)
  851. {
  852. *pstrDest = *pstrSource;
  853. pstrDest = _tcsinc(pstrDest);
  854. }
  855. pstrSource = _tcsinc(pstrSource);
  856. }
  857. *pstrDest = '\0';
  858. int nCount = ( int )( pstrSource - pstrDest );
  859. GetData()->nDataLength -= nCount;
  860. return nCount;
  861. }
  862. //////////////////////////////////////////////////////////////////////////////
  863. // Very simple sub-string extraction
  864. CBsString CBsString::Mid(int nFirst) const
  865. {
  866. return Mid(nFirst, GetData()->nDataLength - nFirst);
  867. }
  868. CBsString CBsString::Mid(int nFirst, int nCount) const
  869. {
  870. // out-of-bounds requests return sensible things
  871. if (nFirst < 0)
  872. nFirst = 0;
  873. if (nCount < 0)
  874. nCount = 0;
  875. if (nFirst + nCount > GetData()->nDataLength)
  876. nCount = GetData()->nDataLength - nFirst;
  877. if (nFirst > GetData()->nDataLength)
  878. nCount = 0;
  879. ASSERT(nFirst >= 0);
  880. ASSERT(nFirst + nCount <= GetData()->nDataLength);
  881. // optimize case of returning entire string
  882. if (nFirst == 0 && nFirst + nCount == GetData()->nDataLength)
  883. return *this;
  884. CBsString dest;
  885. AllocCopy(dest, nCount, nFirst, 0);
  886. return dest;
  887. }
  888. CBsString CBsString::Right(int nCount) const
  889. {
  890. if (nCount < 0)
  891. nCount = 0;
  892. if (nCount >= GetData()->nDataLength)
  893. return *this;
  894. CBsString dest;
  895. AllocCopy(dest, nCount, GetData()->nDataLength-nCount, 0);
  896. return dest;
  897. }
  898. CBsString CBsString::Left(int nCount) const
  899. {
  900. if (nCount < 0)
  901. nCount = 0;
  902. if (nCount >= GetData()->nDataLength)
  903. return *this;
  904. CBsString dest;
  905. AllocCopy(dest, nCount, 0, 0);
  906. return dest;
  907. }
  908. // strspn equivalent
  909. CBsString CBsString::SpanIncluding(LPCTSTR lpszCharSet) const
  910. {
  911. ASSERT(BsAfxIsValidString(lpszCharSet));
  912. return Left((INT)_tcsspn(m_pchData, lpszCharSet));
  913. }
  914. // strcspn equivalent
  915. CBsString CBsString::SpanExcluding(LPCTSTR lpszCharSet) const
  916. {
  917. ASSERT(BsAfxIsValidString(lpszCharSet));
  918. return Left((INT)_tcscspn(m_pchData, lpszCharSet));
  919. }
  920. //////////////////////////////////////////////////////////////////////////////
  921. // Finding
  922. int CBsString::ReverseFind(TCHAR ch) const
  923. {
  924. // find last single character
  925. LPTSTR lpsz = _tcsrchr(m_pchData, (_TUCHAR)ch);
  926. // return -1 if not found, distance from beginning otherwise
  927. return (lpsz == NULL) ? -1 : (int)(lpsz - m_pchData);
  928. }
  929. // find a sub-string (like strstr)
  930. int CBsString::Find(LPCTSTR lpszSub) const
  931. {
  932. return Find(lpszSub, 0);
  933. }
  934. int CBsString::Find(LPCTSTR lpszSub, int nStart) const
  935. {
  936. ASSERT(BsAfxIsValidString(lpszSub));
  937. int nLength = GetData()->nDataLength;
  938. if (nStart > nLength)
  939. return -1;
  940. // find first matching substring
  941. LPTSTR lpsz = _tcsstr(m_pchData + nStart, lpszSub);
  942. // return -1 for not found, distance from beginning otherwise
  943. return (lpsz == NULL) ? -1 : (int)(lpsz - m_pchData);
  944. }
  945. /////////////////////////////////////////////////////////////////////////////
  946. // CBsString formatting
  947. #define TCHAR_ARG TCHAR
  948. #define WCHAR_ARG WCHAR
  949. #define CHAR_ARG char
  950. #ifdef _X86_
  951. #define DOUBLE_ARG _BSAFX_DOUBLE
  952. #else
  953. #define DOUBLE_ARG double
  954. #endif
  955. #define FORCE_ANSI 0x10000
  956. #define FORCE_UNICODE 0x20000
  957. #define FORCE_INT64 0x40000
  958. void CBsString::FormatV(LPCTSTR lpszFormat, va_list argList)
  959. {
  960. ASSERT(BsAfxIsValidString(lpszFormat));
  961. va_list argListSave = argList;
  962. // make a guess at the maximum length of the resulting string
  963. int nMaxLen = 16 * 1024; // Don't expect it to be larger than this - SRS
  964. #if 0
  965. for (LPCTSTR lpsz = lpszFormat; *lpsz != '\0'; lpsz = _tcsinc(lpsz))
  966. {
  967. // handle '%' character, but watch out for '%%'
  968. if (*lpsz != '%' || *(lpsz = _tcsinc(lpsz)) == '%')
  969. {
  970. nMaxLen += (INT)_tclen(lpsz);
  971. continue;
  972. }
  973. int nItemLen = 0;
  974. // handle '%' character with format
  975. int nWidth = 0;
  976. for (; *lpsz != '\0'; lpsz = _tcsinc(lpsz))
  977. {
  978. // check for valid flags
  979. if (*lpsz == '#')
  980. nMaxLen += 2; // for '0x'
  981. else if (*lpsz == '*')
  982. nWidth = va_arg(argList, int);
  983. else if (*lpsz == '-' || *lpsz == '+' || *lpsz == '0' ||
  984. *lpsz == ' ')
  985. ;
  986. else // hit non-flag character
  987. break;
  988. }
  989. // get width and skip it
  990. if (nWidth == 0)
  991. {
  992. // width indicated by
  993. nWidth = _ttoi(lpsz);
  994. for (; *lpsz != '\0' && _istdigit(*lpsz); lpsz = _tcsinc(lpsz))
  995. ;
  996. }
  997. ASSERT(nWidth >= 0);
  998. int nPrecision = 0;
  999. if (*lpsz == '.')
  1000. {
  1001. // skip past '.' separator (width.precision)
  1002. lpsz = _tcsinc(lpsz);
  1003. // get precision and skip it
  1004. if (*lpsz == '*')
  1005. {
  1006. nPrecision = va_arg(argList, int);
  1007. lpsz = _tcsinc(lpsz);
  1008. }
  1009. else
  1010. {
  1011. nPrecision = _ttoi(lpsz);
  1012. for (; *lpsz != '\0' && _istdigit(*lpsz); lpsz = _tcsinc(lpsz))
  1013. ;
  1014. }
  1015. ASSERT(nPrecision >= 0);
  1016. }
  1017. // should be on type modifier or specifier
  1018. int nModifier = 0;
  1019. if (_tcsncmp(lpsz, _T("I64"), 3) == 0)
  1020. {
  1021. lpsz += 3;
  1022. nModifier = FORCE_INT64;
  1023. #if !defined(_X86_) && !defined(_ALPHA_)
  1024. // __int64 is only available on X86 and ALPHA platforms
  1025. ASSERT(FALSE);
  1026. #endif
  1027. }
  1028. else
  1029. {
  1030. switch (*lpsz)
  1031. {
  1032. // modifiers that affect size
  1033. case 'h':
  1034. nModifier = FORCE_ANSI;
  1035. lpsz = _tcsinc(lpsz);
  1036. break;
  1037. case 'l':
  1038. nModifier = FORCE_UNICODE;
  1039. lpsz = _tcsinc(lpsz);
  1040. break;
  1041. // modifiers that do not affect size
  1042. case 'F':
  1043. case 'N':
  1044. case 'L':
  1045. lpsz = _tcsinc(lpsz);
  1046. break;
  1047. }
  1048. }
  1049. // now should be on specifier
  1050. switch (*lpsz | nModifier)
  1051. {
  1052. // single characters
  1053. case 'c':
  1054. case 'C':
  1055. nItemLen = 2;
  1056. va_arg(argList, TCHAR_ARG);
  1057. break;
  1058. case 'c'|FORCE_ANSI:
  1059. case 'C'|FORCE_ANSI:
  1060. nItemLen = 2;
  1061. va_arg(argList, CHAR_ARG);
  1062. break;
  1063. case 'c'|FORCE_UNICODE:
  1064. case 'C'|FORCE_UNICODE:
  1065. nItemLen = 2;
  1066. va_arg(argList, WCHAR_ARG);
  1067. break;
  1068. // strings
  1069. case 's':
  1070. {
  1071. LPCTSTR pstrNextArg = va_arg(argList, LPCTSTR);
  1072. if (pstrNextArg == NULL)
  1073. nItemLen = 6; // "(null)"
  1074. else
  1075. {
  1076. nItemLen = lstrlen(pstrNextArg);
  1077. nItemLen = max(1, nItemLen);
  1078. }
  1079. }
  1080. break;
  1081. case 'S':
  1082. {
  1083. #ifndef _UNICODE
  1084. LPWSTR pstrNextArg = va_arg(argList, LPWSTR);
  1085. if (pstrNextArg == NULL)
  1086. nItemLen = 6; // "(null)"
  1087. else
  1088. {
  1089. nItemLen = wcslen(pstrNextArg);
  1090. nItemLen = max(1, nItemLen);
  1091. }
  1092. #else
  1093. LPCSTR pstrNextArg = va_arg(argList, LPCSTR);
  1094. if (pstrNextArg == NULL)
  1095. nItemLen = 6; // "(null)"
  1096. else
  1097. {
  1098. nItemLen = lstrlenA(pstrNextArg);
  1099. nItemLen = max(1, nItemLen);
  1100. }
  1101. #endif
  1102. }
  1103. break;
  1104. case 's'|FORCE_ANSI:
  1105. case 'S'|FORCE_ANSI:
  1106. {
  1107. LPCSTR pstrNextArg = va_arg(argList, LPCSTR);
  1108. if (pstrNextArg == NULL)
  1109. nItemLen = 6; // "(null)"
  1110. else
  1111. {
  1112. nItemLen = lstrlenA(pstrNextArg);
  1113. nItemLen = max(1, nItemLen);
  1114. }
  1115. }
  1116. break;
  1117. case 's'|FORCE_UNICODE:
  1118. case 'S'|FORCE_UNICODE:
  1119. {
  1120. LPWSTR pstrNextArg = va_arg(argList, LPWSTR);
  1121. if (pstrNextArg == NULL)
  1122. nItemLen = 6; // "(null)"
  1123. else
  1124. {
  1125. nItemLen = (INT)wcslen(pstrNextArg);
  1126. nItemLen = max(1, nItemLen);
  1127. }
  1128. }
  1129. break;
  1130. }
  1131. // adjust nItemLen for strings
  1132. if (nItemLen != 0)
  1133. {
  1134. if (nPrecision != 0)
  1135. nItemLen = min(nItemLen, nPrecision);
  1136. nItemLen = max(nItemLen, nWidth);
  1137. }
  1138. else
  1139. {
  1140. switch (*lpsz)
  1141. {
  1142. // integers
  1143. case 'd':
  1144. case 'i':
  1145. case 'u':
  1146. case 'x':
  1147. case 'X':
  1148. case 'o':
  1149. if (nModifier & FORCE_INT64)
  1150. va_arg(argList, __int64);
  1151. else
  1152. va_arg(argList, int);
  1153. nItemLen = 32;
  1154. nItemLen = max(nItemLen, nWidth+nPrecision);
  1155. break;
  1156. case 'e':
  1157. case 'g':
  1158. case 'G':
  1159. va_arg(argList, DOUBLE_ARG);
  1160. nItemLen = 128;
  1161. nItemLen = max(nItemLen, nWidth+nPrecision);
  1162. break;
  1163. case 'f':
  1164. {
  1165. double f;
  1166. LPTSTR pszTemp;
  1167. // 312 == strlen("-1+(309 zeroes).")
  1168. // 309 zeroes == max precision of a double
  1169. // 6 == adjustment in case precision is not specified,
  1170. // which means that the precision defaults to 6
  1171. pszTemp = (LPTSTR)_alloca(max(nWidth, 312+nPrecision+6));
  1172. f = va_arg(argList, double);
  1173. _stprintf( pszTemp, _T( "%*.*f" ), nWidth, nPrecision+6, f );
  1174. nItemLen = _tcslen(pszTemp);
  1175. }
  1176. break;
  1177. case 'p':
  1178. va_arg(argList, void*);
  1179. nItemLen = 32;
  1180. nItemLen = max(nItemLen, nWidth+nPrecision);
  1181. break;
  1182. // no output
  1183. case 'n':
  1184. va_arg(argList, int*);
  1185. break;
  1186. default:
  1187. ASSERT(FALSE); // unknown formatting option
  1188. }
  1189. }
  1190. // adjust nMaxLen for output nItemLen
  1191. nMaxLen += nItemLen;
  1192. }
  1193. #endif
  1194. GetBuffer(nMaxLen);
  1195. INT i = _vstprintf(m_pchData, lpszFormat, argListSave);
  1196. ASSERT( i <= GetAllocLength() );
  1197. ReleaseBuffer();
  1198. va_end(argListSave);
  1199. }
  1200. // formatting (using wsprintf style formatting)
  1201. const CBsString& BSAFX_CDECL CBsString::Format(LPCTSTR lpszFormat, ...)
  1202. {
  1203. ASSERT(BsAfxIsValidString(lpszFormat));
  1204. va_list argList;
  1205. va_start(argList, lpszFormat);
  1206. FormatV(lpszFormat, argList);
  1207. va_end(argList);
  1208. return *this;
  1209. }
  1210. void CBsString::TrimRight(LPCTSTR lpszTargetList)
  1211. {
  1212. // find beginning of trailing matches
  1213. // by starting at beginning (DBCS aware)
  1214. CopyBeforeWrite();
  1215. LPTSTR lpsz = m_pchData;
  1216. LPTSTR lpszLast = NULL;
  1217. while (*lpsz != '\0')
  1218. {
  1219. if (_tcschr(lpszTargetList, *lpsz) != NULL)
  1220. {
  1221. if (lpszLast == NULL)
  1222. lpszLast = lpsz;
  1223. }
  1224. else
  1225. lpszLast = NULL;
  1226. lpsz = _tcsinc(lpsz);
  1227. }
  1228. if (lpszLast != NULL)
  1229. {
  1230. // truncate at left-most matching character
  1231. *lpszLast = '\0';
  1232. GetData()->nDataLength = (int)(lpszLast - m_pchData);
  1233. }
  1234. }
  1235. void CBsString::TrimRight(TCHAR chTarget)
  1236. {
  1237. // find beginning of trailing matches
  1238. // by starting at beginning (DBCS aware)
  1239. CopyBeforeWrite();
  1240. LPTSTR lpsz = m_pchData;
  1241. LPTSTR lpszLast = NULL;
  1242. while (*lpsz != '\0')
  1243. {
  1244. if (*lpsz == chTarget)
  1245. {
  1246. if (lpszLast == NULL)
  1247. lpszLast = lpsz;
  1248. }
  1249. else
  1250. lpszLast = NULL;
  1251. lpsz = _tcsinc(lpsz);
  1252. }
  1253. if (lpszLast != NULL)
  1254. {
  1255. // truncate at left-most matching character
  1256. *lpszLast = '\0';
  1257. GetData()->nDataLength = (int)(lpszLast - m_pchData);
  1258. }
  1259. }
  1260. void CBsString::TrimRight()
  1261. {
  1262. // find beginning of trailing spaces by starting at beginning (DBCS aware)
  1263. CopyBeforeWrite();
  1264. LPTSTR lpsz = m_pchData;
  1265. LPTSTR lpszLast = NULL;
  1266. while (*lpsz != '\0')
  1267. {
  1268. if (_istspace(*lpsz))
  1269. {
  1270. if (lpszLast == NULL)
  1271. lpszLast = lpsz;
  1272. }
  1273. else
  1274. lpszLast = NULL;
  1275. lpsz = _tcsinc(lpsz);
  1276. }
  1277. if (lpszLast != NULL)
  1278. {
  1279. // truncate at trailing space start
  1280. *lpszLast = '\0';
  1281. GetData()->nDataLength = (int)(lpszLast - m_pchData);
  1282. }
  1283. }
  1284. void CBsString::TrimLeft(LPCTSTR lpszTargets)
  1285. {
  1286. // if we're not trimming anything, we're not doing any work
  1287. if (SafeStrlen(lpszTargets) == 0)
  1288. return;
  1289. CopyBeforeWrite();
  1290. LPCTSTR lpsz = m_pchData;
  1291. while (*lpsz != '\0')
  1292. {
  1293. if (_tcschr(lpszTargets, *lpsz) == NULL)
  1294. break;
  1295. lpsz = _tcsinc(lpsz);
  1296. }
  1297. if (lpsz != m_pchData)
  1298. {
  1299. // fix up data and length
  1300. int nDataLength = GetData()->nDataLength - (int)(lpsz - m_pchData);
  1301. memmove(m_pchData, lpsz, (nDataLength+1)*sizeof(TCHAR));
  1302. GetData()->nDataLength = nDataLength;
  1303. }
  1304. }
  1305. void CBsString::TrimLeft(TCHAR chTarget)
  1306. {
  1307. // find first non-matching character
  1308. CopyBeforeWrite();
  1309. LPCTSTR lpsz = m_pchData;
  1310. while (chTarget == *lpsz)
  1311. lpsz = _tcsinc(lpsz);
  1312. if (lpsz != m_pchData)
  1313. {
  1314. // fix up data and length
  1315. int nDataLength = GetData()->nDataLength - (int)(lpsz - m_pchData);
  1316. memmove(m_pchData, lpsz, (nDataLength+1)*sizeof(TCHAR));
  1317. GetData()->nDataLength = nDataLength;
  1318. }
  1319. }
  1320. void CBsString::TrimLeft()
  1321. {
  1322. // find first non-space character
  1323. CopyBeforeWrite();
  1324. LPCTSTR lpsz = m_pchData;
  1325. while (_istspace(*lpsz))
  1326. lpsz = _tcsinc(lpsz);
  1327. if (lpsz != m_pchData)
  1328. {
  1329. // fix up data and length
  1330. int nDataLength = GetData()->nDataLength - (int)(lpsz - m_pchData);
  1331. memmove(m_pchData, lpsz, (nDataLength+1)*sizeof(TCHAR));
  1332. GetData()->nDataLength = nDataLength;
  1333. }
  1334. }
  1335. //
  1336. // From validadd.cpp
  1337. //
  1338. BOOL BSAFXAPI BsAfxIsValidString(LPCWSTR lpsz, int nLength)
  1339. {
  1340. if (lpsz == NULL)
  1341. return FALSE;
  1342. return ::IsBadStringPtrW(lpsz, nLength) == 0;
  1343. }
  1344. BOOL BSAFXAPI BsAfxIsValidString(LPCSTR lpsz, int nLength)
  1345. {
  1346. if (lpsz == NULL)
  1347. return FALSE;
  1348. return ::IsBadStringPtrA(lpsz, nLength) == 0;
  1349. }
  1350. BOOL BSAFXAPI BsAfxIsValidAddress(const void* lp, UINT nBytes, BOOL bReadWrite)
  1351. {
  1352. // simple version using Win-32 APIs for pointer validation.
  1353. return (lp != NULL && !IsBadReadPtr(lp, nBytes) &&
  1354. (!bReadWrite || !IsBadWritePtr((LPVOID)lp, nBytes)));
  1355. }
  1356. ///////////////////////////////////////////////////////////////////////////////