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.

1567 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 "bsstring.h"
  18. #include "malloc.h"
  19. #ifdef _DEBUG
  20. #undef THIS_FILE
  21. static char THIS_FILE[] = __FILE__;
  22. #endif
  23. struct _BSAFX_DOUBLE { BYTE doubleBits[sizeof(double)]; };
  24. struct _BSAFX_FLOAT { BYTE floatBits[sizeof(float)]; };
  25. // #define new DEBUG_NEW
  26. /////////////////////////////////////////////////////////////////////////////
  27. // static class data, special inlines
  28. TCHAR bsafxChNil = '\0';
  29. // For an empty string, m_pchData will point here
  30. // (note: avoids special case of checking for NULL m_pchData)
  31. // empty string data (and locked)
  32. static int _bsafxInitData[] = { -1, 0, 0, 0 };
  33. static CBsStringData* _bsafxDataNil = (CBsStringData*)&_bsafxInitData;
  34. static LPCTSTR _bsafxPchNil = (LPCTSTR)(((BYTE*)&_bsafxInitData)+sizeof(CBsStringData));
  35. // special function to make bsafxEmptyString work even during initialization
  36. const CBsString& BSAFXAPI BsAfxGetEmptyString()
  37. { return *(CBsString*)&_bsafxPchNil; }
  38. //////////////////////////////////////////////////////////////////////////////
  39. // Construction/Destruction
  40. CBsString::CBsString()
  41. {
  42. Init();
  43. }
  44. CBsString::CBsString(const CBsString& stringSrc)
  45. {
  46. ASSERT(stringSrc.GetData()->nRefs != 0);
  47. if (stringSrc.GetData()->nRefs >= 0)
  48. {
  49. ASSERT(stringSrc.GetData() != _bsafxDataNil);
  50. m_pchData = stringSrc.m_pchData;
  51. InterlockedIncrement(&GetData()->nRefs);
  52. }
  53. else
  54. {
  55. Init();
  56. *this = stringSrc.m_pchData;
  57. }
  58. }
  59. CBsString::CBsString(GUID guid)
  60. {
  61. Init();
  62. AllocBuffer(38);
  63. _stprintf( m_pchData, _T("{%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X}"),
  64. guid.Data1, guid.Data2, guid.Data3, guid.Data4[0], guid.Data4[1],
  65. guid.Data4[2], guid.Data4[3], guid.Data4[4], guid.Data4[5],
  66. guid.Data4[6], guid.Data4[7] );
  67. }
  68. #ifndef _DEBUG
  69. #pragma warning(disable: 4074)
  70. #pragma init_seg(compiler)
  71. #define ROUND(x,y) (((x)+(y-1))&~(y-1))
  72. #define ROUND4(x) ROUND(x, 4)
  73. static CBsFixedAlloc _bsafxAlloc8(ROUND4(9*sizeof(TCHAR)+sizeof(CBsStringData)), 1024);
  74. static CBsFixedAlloc _bsafxAlloc16(ROUND4(17*sizeof(TCHAR)+sizeof(CBsStringData)), 512);
  75. static CBsFixedAlloc _bsafxAlloc32(ROUND4(33*sizeof(TCHAR)+sizeof(CBsStringData)), 256);
  76. static CBsFixedAlloc _bsafxAlloc64(ROUND4(65*sizeof(TCHAR)+sizeof(CBsStringData)));
  77. static CBsFixedAlloc _bsafxAlloc128(ROUND4(129*sizeof(TCHAR)+sizeof(CBsStringData)));
  78. static CBsFixedAlloc _bsafxAlloc256(ROUND4(257*sizeof(TCHAR)+sizeof(CBsStringData)));
  79. static CBsFixedAlloc _bsafxAlloc512(ROUND4(513*sizeof(TCHAR)+sizeof(CBsStringData)));
  80. #endif //!_DEBUG
  81. void CBsString::AllocBuffer(int nLen)
  82. // always allocate one extra character for '\0' termination
  83. // assumes [optimistically] that data length will equal allocation length
  84. // Throws E_OUTOFMEMORY when out of memory
  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 ) // Prefix #118828
  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 = 0;
  964. for (LPCTSTR lpsz = lpszFormat; *lpsz != '\0'; lpsz = _tcsinc(lpsz))
  965. {
  966. // handle '%' character, but watch out for '%%'
  967. if (*lpsz != '%' || *(lpsz = _tcsinc(lpsz)) == '%')
  968. {
  969. nMaxLen += (INT)_tclen(lpsz);
  970. continue;
  971. }
  972. int nItemLen = 0;
  973. // handle '%' character with format
  974. int nWidth = 0;
  975. for (; *lpsz != '\0'; lpsz = _tcsinc(lpsz))
  976. {
  977. // check for valid flags
  978. if (*lpsz == '#')
  979. nMaxLen += 2; // for '0x'
  980. else if (*lpsz == '*')
  981. nWidth = va_arg(argList, int);
  982. else if (*lpsz == '-' || *lpsz == '+' || *lpsz == '0' ||
  983. *lpsz == ' ')
  984. ;
  985. else // hit non-flag character
  986. break;
  987. }
  988. // get width and skip it
  989. if (nWidth == 0)
  990. {
  991. // width indicated by
  992. nWidth = _ttoi(lpsz);
  993. for (; *lpsz != '\0' && _istdigit(*lpsz); lpsz = _tcsinc(lpsz))
  994. ;
  995. }
  996. ASSERT(nWidth >= 0);
  997. int nPrecision = 0;
  998. if (*lpsz == '.')
  999. {
  1000. // skip past '.' separator (width.precision)
  1001. lpsz = _tcsinc(lpsz);
  1002. // get precision and skip it
  1003. if (*lpsz == '*')
  1004. {
  1005. nPrecision = va_arg(argList, int);
  1006. lpsz = _tcsinc(lpsz);
  1007. }
  1008. else
  1009. {
  1010. nPrecision = _ttoi(lpsz);
  1011. for (; *lpsz != '\0' && _istdigit(*lpsz); lpsz = _tcsinc(lpsz))
  1012. ;
  1013. }
  1014. ASSERT(nPrecision >= 0);
  1015. }
  1016. // should be on type modifier or specifier
  1017. int nModifier = 0;
  1018. if (_tcsncmp(lpsz, _T("I64"), 3) == 0)
  1019. {
  1020. lpsz += 3;
  1021. nModifier = FORCE_INT64;
  1022. #if !defined(_X86_) && !defined(_ALPHA_)
  1023. // __int64 is only available on X86 and ALPHA platforms
  1024. ASSERT(FALSE);
  1025. #endif
  1026. }
  1027. else
  1028. {
  1029. switch (*lpsz)
  1030. {
  1031. // modifiers that affect size
  1032. case 'h':
  1033. nModifier = FORCE_ANSI;
  1034. lpsz = _tcsinc(lpsz);
  1035. break;
  1036. case 'l':
  1037. nModifier = FORCE_UNICODE;
  1038. lpsz = _tcsinc(lpsz);
  1039. break;
  1040. // modifiers that do not affect size
  1041. case 'F':
  1042. case 'N':
  1043. case 'L':
  1044. lpsz = _tcsinc(lpsz);
  1045. break;
  1046. }
  1047. }
  1048. // now should be on specifier
  1049. switch (*lpsz | nModifier)
  1050. {
  1051. // single characters
  1052. case 'c':
  1053. case 'C':
  1054. nItemLen = 2;
  1055. va_arg(argList, TCHAR_ARG);
  1056. break;
  1057. case 'c'|FORCE_ANSI:
  1058. case 'C'|FORCE_ANSI:
  1059. nItemLen = 2;
  1060. va_arg(argList, CHAR_ARG);
  1061. break;
  1062. case 'c'|FORCE_UNICODE:
  1063. case 'C'|FORCE_UNICODE:
  1064. nItemLen = 2;
  1065. va_arg(argList, WCHAR_ARG);
  1066. break;
  1067. // strings
  1068. case 's':
  1069. {
  1070. LPCTSTR pstrNextArg = va_arg(argList, LPCTSTR);
  1071. if (pstrNextArg == NULL)
  1072. nItemLen = 6; // "(null)"
  1073. else
  1074. {
  1075. nItemLen = lstrlen(pstrNextArg);
  1076. nItemLen = max(1, nItemLen);
  1077. }
  1078. }
  1079. break;
  1080. case 'S':
  1081. {
  1082. #ifndef _UNICODE
  1083. LPWSTR pstrNextArg = va_arg(argList, LPWSTR);
  1084. if (pstrNextArg == NULL)
  1085. nItemLen = 6; // "(null)"
  1086. else
  1087. {
  1088. nItemLen = wcslen(pstrNextArg);
  1089. nItemLen = max(1, nItemLen);
  1090. }
  1091. #else
  1092. LPCSTR pstrNextArg = va_arg(argList, LPCSTR);
  1093. if (pstrNextArg == NULL)
  1094. nItemLen = 6; // "(null)"
  1095. else
  1096. {
  1097. nItemLen = lstrlenA(pstrNextArg);
  1098. nItemLen = max(1, nItemLen);
  1099. }
  1100. #endif
  1101. }
  1102. break;
  1103. case 's'|FORCE_ANSI:
  1104. case 'S'|FORCE_ANSI:
  1105. {
  1106. LPCSTR pstrNextArg = va_arg(argList, LPCSTR);
  1107. if (pstrNextArg == NULL)
  1108. nItemLen = 6; // "(null)"
  1109. else
  1110. {
  1111. nItemLen = lstrlenA(pstrNextArg);
  1112. nItemLen = max(1, nItemLen);
  1113. }
  1114. }
  1115. break;
  1116. case 's'|FORCE_UNICODE:
  1117. case 'S'|FORCE_UNICODE:
  1118. {
  1119. LPWSTR pstrNextArg = va_arg(argList, LPWSTR);
  1120. if (pstrNextArg == NULL)
  1121. nItemLen = 6; // "(null)"
  1122. else
  1123. {
  1124. nItemLen = (INT)wcslen(pstrNextArg);
  1125. nItemLen = max(1, nItemLen);
  1126. }
  1127. }
  1128. break;
  1129. }
  1130. // adjust nItemLen for strings
  1131. if (nItemLen != 0)
  1132. {
  1133. if (nPrecision != 0)
  1134. nItemLen = min(nItemLen, nPrecision);
  1135. nItemLen = max(nItemLen, nWidth);
  1136. }
  1137. else
  1138. {
  1139. switch (*lpsz)
  1140. {
  1141. // integers
  1142. case 'd':
  1143. case 'i':
  1144. case 'u':
  1145. case 'x':
  1146. case 'X':
  1147. case 'o':
  1148. if (nModifier & FORCE_INT64)
  1149. va_arg(argList, __int64);
  1150. else
  1151. va_arg(argList, int);
  1152. nItemLen = 32;
  1153. nItemLen = max(nItemLen, nWidth+nPrecision);
  1154. break;
  1155. case 'e':
  1156. case 'g':
  1157. case 'G':
  1158. va_arg(argList, DOUBLE_ARG);
  1159. nItemLen = 128;
  1160. nItemLen = max(nItemLen, nWidth+nPrecision);
  1161. break;
  1162. case 'f':
  1163. {
  1164. double f;
  1165. LPTSTR pszTemp;
  1166. // 312 == strlen("-1+(309 zeroes).")
  1167. // 309 zeroes == max precision of a double
  1168. // 6 == adjustment in case precision is not specified,
  1169. // which means that the precision defaults to 6
  1170. pszTemp = (LPTSTR)_alloca(max(nWidth, 312+nPrecision+6));
  1171. f = va_arg(argList, double);
  1172. _stprintf( pszTemp, _T( "%*.*f" ), nWidth, nPrecision+6, f );
  1173. nItemLen = _tcslen(pszTemp);
  1174. }
  1175. break;
  1176. case 'p':
  1177. va_arg(argList, void*);
  1178. nItemLen = 32;
  1179. nItemLen = max(nItemLen, nWidth+nPrecision);
  1180. break;
  1181. // no output
  1182. case 'n':
  1183. va_arg(argList, int*);
  1184. break;
  1185. default:
  1186. ASSERT(FALSE); // unknown formatting option
  1187. }
  1188. }
  1189. // adjust nMaxLen for output nItemLen
  1190. nMaxLen += nItemLen;
  1191. }
  1192. GetBuffer(nMaxLen);
  1193. INT i = _vstprintf(m_pchData, lpszFormat, argListSave);
  1194. ASSERT( i <= GetAllocLength() );
  1195. ReleaseBuffer();
  1196. va_end(argListSave);
  1197. }
  1198. // formatting (using wsprintf style formatting)
  1199. void BSAFX_CDECL CBsString::Format(LPCTSTR lpszFormat, ...)
  1200. {
  1201. ASSERT(BsAfxIsValidString(lpszFormat));
  1202. va_list argList;
  1203. va_start(argList, lpszFormat);
  1204. FormatV(lpszFormat, argList);
  1205. va_end(argList);
  1206. }
  1207. void CBsString::TrimRight(LPCTSTR lpszTargetList)
  1208. {
  1209. // find beginning of trailing matches
  1210. // by starting at beginning (DBCS aware)
  1211. CopyBeforeWrite();
  1212. LPTSTR lpsz = m_pchData;
  1213. LPTSTR lpszLast = NULL;
  1214. while (*lpsz != '\0')
  1215. {
  1216. if (_tcschr(lpszTargetList, *lpsz) != NULL)
  1217. {
  1218. if (lpszLast == NULL)
  1219. lpszLast = lpsz;
  1220. }
  1221. else
  1222. lpszLast = NULL;
  1223. lpsz = _tcsinc(lpsz);
  1224. }
  1225. if (lpszLast != NULL)
  1226. {
  1227. // truncate at left-most matching character
  1228. *lpszLast = '\0';
  1229. GetData()->nDataLength = (int)(lpszLast - m_pchData);
  1230. }
  1231. }
  1232. void CBsString::TrimRight(TCHAR chTarget)
  1233. {
  1234. // find beginning of trailing matches
  1235. // by starting at beginning (DBCS aware)
  1236. CopyBeforeWrite();
  1237. LPTSTR lpsz = m_pchData;
  1238. LPTSTR lpszLast = NULL;
  1239. while (*lpsz != '\0')
  1240. {
  1241. if (*lpsz == chTarget)
  1242. {
  1243. if (lpszLast == NULL)
  1244. lpszLast = lpsz;
  1245. }
  1246. else
  1247. lpszLast = NULL;
  1248. lpsz = _tcsinc(lpsz);
  1249. }
  1250. if (lpszLast != NULL)
  1251. {
  1252. // truncate at left-most matching character
  1253. *lpszLast = '\0';
  1254. GetData()->nDataLength = (int)(lpszLast - m_pchData);
  1255. }
  1256. }
  1257. void CBsString::TrimRight()
  1258. {
  1259. // find beginning of trailing spaces by starting at beginning (DBCS aware)
  1260. CopyBeforeWrite();
  1261. LPTSTR lpsz = m_pchData;
  1262. LPTSTR lpszLast = NULL;
  1263. while (*lpsz != '\0')
  1264. {
  1265. if (_istspace(*lpsz))
  1266. {
  1267. if (lpszLast == NULL)
  1268. lpszLast = lpsz;
  1269. }
  1270. else
  1271. lpszLast = NULL;
  1272. lpsz = _tcsinc(lpsz);
  1273. }
  1274. if (lpszLast != NULL)
  1275. {
  1276. // truncate at trailing space start
  1277. *lpszLast = '\0';
  1278. GetData()->nDataLength = (int)(lpszLast - m_pchData);
  1279. }
  1280. }
  1281. void CBsString::TrimLeft(LPCTSTR lpszTargets)
  1282. {
  1283. // if we're not trimming anything, we're not doing any work
  1284. if (SafeStrlen(lpszTargets) == 0)
  1285. return;
  1286. CopyBeforeWrite();
  1287. LPCTSTR lpsz = m_pchData;
  1288. while (*lpsz != '\0')
  1289. {
  1290. if (_tcschr(lpszTargets, *lpsz) == NULL)
  1291. break;
  1292. lpsz = _tcsinc(lpsz);
  1293. }
  1294. if (lpsz != m_pchData)
  1295. {
  1296. // fix up data and length
  1297. int nDataLength = GetData()->nDataLength - (int)(lpsz - m_pchData);
  1298. memmove(m_pchData, lpsz, (nDataLength+1)*sizeof(TCHAR));
  1299. GetData()->nDataLength = nDataLength;
  1300. }
  1301. }
  1302. void CBsString::TrimLeft(TCHAR chTarget)
  1303. {
  1304. // find first non-matching character
  1305. CopyBeforeWrite();
  1306. LPCTSTR lpsz = m_pchData;
  1307. while (chTarget == *lpsz)
  1308. lpsz = _tcsinc(lpsz);
  1309. if (lpsz != m_pchData)
  1310. {
  1311. // fix up data and length
  1312. int nDataLength = GetData()->nDataLength - (int)(lpsz - m_pchData);
  1313. memmove(m_pchData, lpsz, (nDataLength+1)*sizeof(TCHAR));
  1314. GetData()->nDataLength = nDataLength;
  1315. }
  1316. }
  1317. void CBsString::TrimLeft()
  1318. {
  1319. // find first non-space character
  1320. CopyBeforeWrite();
  1321. LPCTSTR lpsz = m_pchData;
  1322. while (_istspace(*lpsz))
  1323. lpsz = _tcsinc(lpsz);
  1324. if (lpsz != m_pchData)
  1325. {
  1326. // fix up data and length
  1327. int nDataLength = GetData()->nDataLength - (int)(lpsz - m_pchData);
  1328. memmove(m_pchData, lpsz, (nDataLength+1)*sizeof(TCHAR));
  1329. GetData()->nDataLength = nDataLength;
  1330. }
  1331. }
  1332. //
  1333. // From validadd.cpp
  1334. //
  1335. BOOL BSAFXAPI BsAfxIsValidString(LPCWSTR lpsz, int nLength)
  1336. {
  1337. if (lpsz == NULL)
  1338. return FALSE;
  1339. return ::IsBadStringPtrW(lpsz, nLength) == 0;
  1340. }
  1341. BOOL BSAFXAPI BsAfxIsValidString(LPCSTR lpsz, int nLength)
  1342. {
  1343. if (lpsz == NULL)
  1344. return FALSE;
  1345. return ::IsBadStringPtrA(lpsz, nLength) == 0;
  1346. }
  1347. BOOL BSAFXAPI BsAfxIsValidAddress(const void* lp, UINT nBytes, BOOL bReadWrite)
  1348. {
  1349. // simple version using Win-32 APIs for pointer validation.
  1350. return (lp != NULL && !IsBadReadPtr(lp, nBytes) &&
  1351. (!bReadWrite || !IsBadWritePtr((LPVOID)lp, nBytes)));
  1352. }
  1353. ///////////////////////////////////////////////////////////////////////////////