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.

1624 lines
38 KiB

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