rstring.cpp 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624
  1. ////////////////////////////////////////////////////////////////////////////////
  2. //
  3. // Copyright 2016 RWS Inc, All Rights Reserved
  4. //
  5. // This program is free software; you can redistribute it and/or modify
  6. // it under the terms of version 2 of the GNU General Public License as published by
  7. // the Free Software Foundation
  8. //
  9. // This program is distributed in the hope that it will be useful,
  10. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. // GNU General Public License for more details.
  13. //
  14. // You should have received a copy of the GNU General Public License along
  15. // with this program; if not, write to the Free Software Foundation, Inc.,
  16. // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  17. //
  18. ////////////////////////////////////////////////////////////////////////////////
  19. //
  20. // string.cpp
  21. // Project: Nostril (aka Postal)
  22. //
  23. // History:
  24. // 01/17/97 MJR Started.
  25. // 01/18/97 MJR First (tentative) release.
  26. // 01/20/97 MJR Revised lots of stuff, ready for testing/prerelease.
  27. // 01/21/97 MJR Did a bunch of testing, fixed one minor typo-bug.
  28. // 01/28/97 MJR Fixed ASSERT(1) to be ASSERT(0), as it should have been.
  29. // Added char* operator.
  30. // Added Update() function.
  31. // 02/10/97 MJR Minor tweak to Save() -- got rid of (strange) unecessary
  32. // cast. Also got rid of one constructor varation which had
  33. // allowed you to create an RString based on the value of a
  34. // char. The problem was that this caused an "abiguous"
  35. // overload when you were trying to create an RString of
  36. // a specified length, as in RString str(5). Hopefully the
  37. // removed constructor will not be missed.
  38. //
  39. // 06/28/97 MJR Fixed typo in mac-specific code that had never been
  40. // compiled before (naturally).
  41. //
  42. // 07/12/97 MJR RString::Load() didn't initialize sResult, so it would
  43. // return random error codes instead of 0 for success.
  44. //
  45. ////////////////////////////////////////////////////////////////////////////////
  46. //
  47. // The primary purpose of RString is to make it easier to work with text strings
  48. // by hiding the memory and storage aspects from the user. It lets the user
  49. // focus on the real task without getting caught up in the nasty details.
  50. //
  51. //
  52. // Most RString functions and operators are overloaded to accept any of the
  53. // following types. These are often referred to as the "supported types".
  54. //
  55. // * Other RString's
  56. //
  57. // * C-style strings (char*)
  58. //
  59. // * Characters (char), which are treated as ASCII codes
  60. //
  61. // * Integers (short, unsigned short, long, and unsigned long) converted
  62. // into string representations of their numerical values. The exception
  63. // is that there are no constructors to take these types, only because
  64. // it caused a conflict with other, more usefull constructors.
  65. //
  66. //
  67. // This is an overview of the available functions. Check the comments on
  68. // the individual functions for details.
  69. //
  70. // RString() Various constructors that allow the creation of a string,
  71. // optionally based on any supported type, and optionally
  72. // with a specific buffer size
  73. //
  74. // ~RString() Destructor
  75. //
  76. // GetSize() Get size of buffer (always larger than string length)
  77. //
  78. // Grow() Grow buffer to at least the specified size
  79. //
  80. // Shrink() Shrink buffer to at most the specified size
  81. //
  82. // Compact() Compact buffer to minimum size required by current string
  83. //
  84. // Clear() Clear string (length will be 0)
  85. //
  86. // GetLen() Get string length
  87. //
  88. // GetAt() Get character at specified position in string
  89. //
  90. // operator[] Get character at specified position in string
  91. //
  92. // SetAt() Set character at specified position in string
  93. //
  94. // operator char* Casts RString to C-style string
  95. //
  96. // Update() Updates string if it was modified via the char* pointer
  97. //
  98. // Format() Create string using sprintf-like method
  99. //
  100. // Left() Returns new RString based on a portion of this string
  101. //
  102. // Right() Returns new RString based on a portion of this string
  103. //
  104. // Mid() Returns new RString based on a portion of this string
  105. //
  106. // Range() Returns new RString based on a portion of this string
  107. //
  108. // Insert() Insert any supported type into string
  109. //
  110. // Delete() Delete portion of string
  111. //
  112. // ToUpper() Convert string to upper case
  113. //
  114. // ToLower() Convert string to lower case
  115. //
  116. // Load() Load previously saved string
  117. //
  118. // Save() Save string
  119. //
  120. // operator= Assigns any supported type into string
  121. //
  122. // operator+= Appends supported types onto current string
  123. //
  124. // operator+ Concatinates string and any supported type
  125. //
  126. // operator== Full gamut of comparison operators
  127. // operator!=
  128. // operator<=
  129. // operator>=
  130. // operator<
  131. // operator>
  132. //
  133. //
  134. // Functions to be added some time soon...
  135. //
  136. // I'm thinking Insert() could be easily extended to make it much more
  137. // usefull -- see the Insert() comment block (in the header file!) for
  138. // details.
  139. //
  140. // Along the same lines as Insert(), I think a Copy() would be usefull.
  141. // It would be just like Insert() except it would overwrite the data in
  142. // the destination string.
  143. //
  144. // Together, just about everything else could be implimented based on
  145. // Insert() and Copy()! I'm not sure it's worth switching over to that,
  146. // though. Too bad I didn't create them first.
  147. //
  148. //
  149. // More Information
  150. //
  151. // An RString object will automatically increase the size of its memory buffer
  152. // as necessary to accomodate the growth of the text string it holds. It will
  153. // never reduce the buffer's size automatically, although the user can trigger
  154. // such a reduction at any time by calling Compact().
  155. //
  156. // The user can also explicitly set the size of the buffer, although it will
  157. // still be grown automatically if necessary. The idea is to allow the user to
  158. // make the buffer large enough so that additional growth isn't necessary,
  159. // thereby improving performance and reducing memory fragmentation.
  160. //
  161. // When working with RStrings, knowing the length of a string becomes much less
  162. // important than it is with C-style strings. In light of this, all RString
  163. // functions that take a length as a parameter will automatically clip that
  164. // length to the actual size of the string. For instance, if the first 8
  165. // characters of a string are requested but the string is only 5 characters
  166. // long, then the requested length is clipped, and only 5 characters are
  167. // returned.
  168. //
  169. // In constract, NO clipping is performed at the beginning of a string. The
  170. // first character of a string is always at position 0. If a negative position
  171. // is passed to any RString function, it will do nothing or return an empty
  172. // RString, whichever is appropriate for that function. Negative positions
  173. // are basically considered meaningless, but are handled without any complaints
  174. // (no TRACE's or ASSERT's), the idea being to leave it up to the user to
  175. // decide whether to validate positions beforehand or to check results
  176. // afterwards.
  177. //
  178. // Put another way, functions will generally clip lengths to fit
  179. // When an RString function takes a length as a parameter, that length will
  180. // be truncated if necessary to avoid going past the end of the string. When
  181. // a function takes a position as a parameter, the function won't do anything
  182. // if that position is negative.
  183. //
  184. // As a general rule, clipping is performed on the right of strings but not
  185. // on the left end. The reason for these two different approaches is actually
  186. // pretty straightforward.
  187. //
  188. ////////////////////////////////////////////////////////////////////////////////
  189. #define RSTRING_CPP
  190. #include <ctype.h>
  191. #include "rstring.h"
  192. // Static pointer that is initialized to point at an empty string. Used
  193. // in cases where string is being cast as char* but doesn't have a buffer
  194. // of it's own.
  195. char* RString::ms_pszEmpty = "";
  196. ////////////////////////////////////////////////////////////////////////////////
  197. // Grow the buffer to the specified size. If the buffer is already greater than
  198. // or equal to the specified size, then this will have no effect. Otherwise,
  199. // the buffer is grown to the specified size. The string is unaffected.
  200. ////////////////////////////////////////////////////////////////////////////////
  201. void RString::Grow(long lMinimumSize)
  202. {
  203. // New size must be greater than current size (which might be 0)
  204. // (This comparison also filters out negative sizes, which are stupid)
  205. if (lMinimumSize > m_lBufSize)
  206. {
  207. if (m_lBufSize > 0)
  208. {
  209. // Change buffer size
  210. char* pOld = m_pBuf;
  211. m_pBuf = (char*)malloc(lMinimumSize);
  212. ASSERT(m_pBuf != 0); // should be caught by new_handler, but just in case...
  213. memcpy(m_pBuf, pOld, m_lBufSize);
  214. free(pOld);
  215. }
  216. else
  217. {
  218. // Create new buffer
  219. m_pBuf = (char*)malloc(lMinimumSize);
  220. ASSERT(m_pBuf != 0); // should be caught by new_handler, but just in case...
  221. *m_pBuf = 0; // write terminating null (string length must be 0 at this point)
  222. }
  223. m_lBufSize = lMinimumSize;
  224. }
  225. }
  226. ////////////////////////////////////////////////////////////////////////////////
  227. // Shrink the buffer to the specified size. If the buffer is already less than
  228. // or equal to the specified size, then this will have no effect. Otherwise,
  229. // the buffer is reduced to the specified size. If the specified size is 0,
  230. // the buffer, if any, will be freed. If the specified size is less than is
  231. // required to hold the current string (including the terminating null), then
  232. // the string is truncated to a length one less than the specified size, and a
  233. // new terminating null is written. Note that a size of 1 will result in an
  234. // empty string since there will only be room for the terminating null.
  235. ////////////////////////////////////////////////////////////////////////////////
  236. void RString::Shrink(long lMaximumSize)
  237. {
  238. // Guard against stupid values
  239. if (lMaximumSize >= 0)
  240. {
  241. // New size must be less than current size
  242. // (This comparison also ensures that m_lBufSize > 0, meaning there is a buffer)
  243. if (lMaximumSize < m_lBufSize)
  244. {
  245. if (lMaximumSize == 0)
  246. {
  247. FreeBuf();
  248. }
  249. else
  250. {
  251. // Change buffer size
  252. char* pOld = m_pBuf;
  253. m_pBuf = (char*)malloc(lMaximumSize);
  254. ASSERT(m_pBuf != 0); // should be caught by new_handler, but just in case...
  255. memcpy(m_pBuf, pOld, m_lBufSize);
  256. free(pOld);
  257. m_lBufSize = lMaximumSize;
  258. // See if we truncated the string
  259. if (m_lBufSize <= m_lStrLen)
  260. {
  261. // Write new terminating null at end of buffer
  262. m_pBuf[m_lBufSize - 1] = 0;
  263. // Set new string length
  264. m_lStrLen = m_lBufSize - 1;
  265. }
  266. }
  267. }
  268. }
  269. }
  270. ////////////////////////////////////////////////////////////////////////////////
  271. // Compact the buffer to the minimum size required to hold the current string.
  272. // If the string is empty, the buffer is freed.
  273. ////////////////////////////////////////////////////////////////////////////////
  274. void RString::Compact(void)
  275. {
  276. // If there's a string, shrink buffer to fit it, otherwise get rid of it
  277. if (m_lStrLen > 0)
  278. Shrink(m_lStrLen + 1); // shrink buffer (if necessary) to 1 larger than string
  279. else
  280. FreeBuf();
  281. }
  282. ////////////////////////////////////////////////////////////////////////////////
  283. // Format string using sprintf-like method. The specified size is used to make
  284. // sure the buffer is large enough to hold the generated string (the specified
  285. // size is passed to the Grow() function -- see it for more details).
  286. //
  287. // WARNING: The ANSI standard vsprintf function is used to format the string,
  288. // which is good because the results will be consistant with the rest of the
  289. // printf family, but is also very bad because there is no way to limit the
  290. // number of characters that are actually written to the buffer! Therefore,
  291. // this function is fully reliant on the user to ensure the buffer is large
  292. // enough to hold the resulting string. This represents a gaping hole in
  293. // RString's functionality.
  294. //
  295. // Note #1: At some point, I would like to modify the source to vsprintf so it
  296. // works directly with RString and takes advantage of its dynamic sizing. If
  297. // that were done, the size parameter could be removed from this function, and
  298. // other internal RString code that uses sprintf could be simplified as well.
  299. //
  300. // Note #2: Microsoft DOES supply a non-ANSI variation called _vsnprintf() that
  301. // takes a "maximum output size" parameter. For now, we use this variation
  302. // whenever we're compiled under Microsoft.
  303. //
  304. // Returns number of characters written, or -1 if an error occurred (this is
  305. // basically the value returned by vsprintf.)
  306. ////////////////////////////////////////////////////////////////////////////////
  307. long RString::Format(long lMinimumSize, char* format, ...)
  308. {
  309. long lWritten = 0;
  310. if (lMinimumSize >= 0)
  311. {
  312. // Grow buffer
  313. Grow(lMinimumSize);
  314. // Since minimum size might be 0, we have to make sure buffer exists
  315. if (m_lBufSize > 0)
  316. {
  317. va_list varp;
  318. va_start(varp, format);
  319. #ifdef _MSC_VER
  320. lWritten = _vsnprintf(m_pBuf, m_lBufSize - 1, format, varp);
  321. #else
  322. lWritten = vsprintf(m_pBuf, format, varp);
  323. #ifdef _DEBUG
  324. if ((lWritten >= 0) && ((lWritten + 1) > m_lBufSize))
  325. {
  326. TRACE("RString::Format(): String buffer was overwritten! String will be cleared!\n");
  327. Clear();
  328. ASSERT(0); // memory has been corrupted!
  329. }
  330. #endif
  331. #endif
  332. va_end(varp);
  333. if (lWritten >= 0)
  334. {
  335. m_lStrLen = lWritten;
  336. }
  337. else
  338. {
  339. TRACE("RString::Format(): Error returned by vsprintf()! String will be cleared!\n");
  340. Clear();
  341. }
  342. }
  343. }
  344. return lWritten;
  345. }
  346. ////////////////////////////////////////////////////////////////////////////////
  347. // Create a new RString based on this string's first 'lLen' characters. If
  348. // lLen is negative, the returned string will be empty. Otherwise, the returned
  349. // string's length will be lLen or this string's length, whichever is less.
  350. ////////////////////////////////////////////////////////////////////////////////
  351. RString RString::Left(long lLen) const
  352. {
  353. RString str;
  354. if ((lLen > 0) && (m_lStrLen > 0))
  355. {
  356. if (lLen > m_lStrLen)
  357. lLen = m_lStrLen;
  358. str.Grow(lLen + 1); // size is always > 0, so this will always return with a valid buffer
  359. memcpy(str.m_pBuf, m_pBuf, lLen);
  360. str.m_pBuf[lLen] = 0;
  361. str.m_lStrLen = lLen;
  362. }
  363. return str;
  364. }
  365. ////////////////////////////////////////////////////////////////////////////////
  366. // Create a new RString based on this string's last 'lLen' characters. If lLen
  367. // is negative, the returned string will be empty. Otherwise, the returned
  368. // string's length will be lLen or this string's length, whichever is less.
  369. ////////////////////////////////////////////////////////////////////////////////
  370. RString RString::Right(long lLen) const
  371. {
  372. RString str;
  373. if ((lLen > 0) && (m_lStrLen > 0))
  374. {
  375. if (lLen > m_lStrLen)
  376. lLen = m_lStrLen;
  377. str.Grow(lLen + 1); // size is always > 0, so this will always return with a valid buffer
  378. memcpy(str.m_pBuf, m_pBuf + (m_lStrLen - lLen), lLen);
  379. str.m_pBuf[lLen] = 0;
  380. str.m_lStrLen = lLen;
  381. }
  382. return str;
  383. }
  384. ////////////////////////////////////////////////////////////////////////////////
  385. // Create a new RString based on a portion of this string, starting at lPos and
  386. // proceeding for lLen characters. If lPos is negative or beyond the end of
  387. // this string, or if lLen is negative, the returned string will be empty. The
  388. // specified lLen will be clipped if necessary to avoid going past the end
  389. // of this string.
  390. ////////////////////////////////////////////////////////////////////////////////
  391. RString RString::Mid(long lPos, long lLen) const
  392. {
  393. RString str;
  394. if ((lPos >= 0) && (lPos < m_lStrLen) && (lLen > 0)) // implies m_lStrLen > 0
  395. {
  396. if ((lPos + lLen) > m_lStrLen)
  397. lLen = m_lStrLen - lPos;
  398. str.Grow(lLen + 1); // size is always > 0, so this will always return with a valid buffer
  399. memcpy(str.m_pBuf, m_pBuf + lPos, lLen);
  400. str.m_pBuf[lLen] = 0;
  401. str.m_lStrLen = lLen;
  402. }
  403. return str;
  404. }
  405. ////////////////////////////////////////////////////////////////////////////////
  406. // Create a new RString based on the specified range of characters from this
  407. // string. If either position is negative the returned string will be empty.
  408. // The positions are clipped if they are past the end of this string. lPos1
  409. // must be less than or equal to lPos2.
  410. ////////////////////////////////////////////////////////////////////////////////
  411. RString RString::Range(long lPos1, long lPos2) const
  412. {
  413. RString str;
  414. if ((lPos1 >= 0) && (lPos1 <= lPos2) && (lPos1 < m_lStrLen)) // implies m_lStrLen > 0
  415. {
  416. if (lPos2 >= m_lStrLen)
  417. lPos2 = m_lStrLen - 1;
  418. long lLen = (lPos2 - lPos1) + 1;
  419. str.Grow(lLen + 1); // size is always > 0, so this will always return with a valid buffer
  420. memcpy(str.m_pBuf, m_pBuf + lPos1, lLen);
  421. str.m_pBuf[lLen] = 0;
  422. str.m_lStrLen = lLen;
  423. }
  424. return str;
  425. }
  426. ////////////////////////////////////////////////////////////////////////////////
  427. // Insert any of the supported types into the string at the specified position.
  428. // The current string contents, from the character at the specified position up
  429. // to the last character, inclusive, are moved forward to make room for the new
  430. // characters. The position is handled slightly differently by this function
  431. // in that it is valid for it to equal the string length, in which case the new
  432. // characters are basically appended onto the end of the string (in most other
  433. // functions, the position must be less than the string length). If the
  434. // position is negative or greater than the string length, then this function
  435. // will do nothing.
  436. ////////////////////////////////////////////////////////////////////////////////
  437. void RString::Insert(long lPos, const RString& str)
  438. {
  439. if ((lPos >= 0) && (lPos <= m_lStrLen) && (str.m_lStrLen > 0))
  440. {
  441. Grow(m_lStrLen + str.m_lStrLen + 1);
  442. // Shift existing characters to the right (including terminating null!)
  443. memmove(m_pBuf + lPos + str.m_lStrLen, m_pBuf + lPos, (m_lStrLen + 1) - lPos);
  444. // Copy new characters
  445. memcpy(m_pBuf + lPos, str.m_pBuf, str.m_lStrLen);
  446. m_lStrLen += str.m_lStrLen;
  447. }
  448. }
  449. void RString::Insert(long lPos, const char* psz)
  450. {
  451. long lLen = strlen(psz);
  452. if ((lPos >= 0) && (lPos <= m_lStrLen) && (lLen > 0))
  453. {
  454. Grow(m_lStrLen + lLen + 1);
  455. // Shift existing characters to the right (including terminating null!)
  456. memmove(m_pBuf + lPos + lLen, m_pBuf + lPos, (m_lStrLen + 1) - lPos);
  457. // Copy new characters
  458. memcpy(m_pBuf + lPos, psz, lLen);
  459. m_lStrLen += lLen;
  460. }
  461. }
  462. void RString::Insert(long lPos, char c)
  463. {
  464. if ((lPos >= 0) && (lPos <= m_lStrLen))
  465. {
  466. Grow(m_lStrLen + 1 + 1);
  467. // Shift existing characters to the right (including terminating null!)
  468. memmove(m_pBuf + lPos + 1, m_pBuf + lPos, (m_lStrLen + 1) - lPos);
  469. // Copy new character
  470. m_pBuf[lPos] = c;
  471. m_lStrLen++;
  472. }
  473. }
  474. ////////////////////////////////////////////////////////////////////////////////
  475. // Delete the specified number of characters starting at the specified position.
  476. // If the position or length are negative, the string is left unmodified.
  477. ////////////////////////////////////////////////////////////////////////////////
  478. void RString::Delete(long lPos, long lLen)
  479. {
  480. if ((lPos >= 0) && (lPos < m_lStrLen) && (lLen > 0)) // implies m_lStrLen > 0
  481. {
  482. if ((lPos + lLen) > m_lStrLen)
  483. lLen = m_lStrLen - lPos;
  484. // Shift existing characters to the left (including terminating null!)
  485. memmove(m_pBuf + lPos, m_pBuf + lPos + lLen, (m_lStrLen + 1) - (lPos + lLen));
  486. m_lStrLen -= lLen;
  487. }
  488. }
  489. ////////////////////////////////////////////////////////////////////////////////
  490. // Convert string to upper case
  491. ////////////////////////////////////////////////////////////////////////////////
  492. void RString::ToUpper(void)
  493. {
  494. long lLen = m_lStrLen;
  495. char* p = m_pBuf;
  496. if (lLen > 0)
  497. {
  498. do {
  499. *p = toupper(*p);
  500. p++;
  501. } while (--lLen);
  502. }
  503. }
  504. ////////////////////////////////////////////////////////////////////////////////
  505. // Convert string to lower case
  506. ////////////////////////////////////////////////////////////////////////////////
  507. void RString::ToLower(void)
  508. {
  509. long lLen = m_lStrLen;
  510. char* p = m_pBuf;
  511. if (lLen > 0)
  512. {
  513. do {
  514. *p = tolower(*p);
  515. p++;
  516. } while (--lLen);
  517. }
  518. }
  519. ////////////////////////////////////////////////////////////////////////////////
  520. // Load a previously saved string from the specified RFile. Calls Clear() if
  521. // an error occurs while loading. Returns 0 if successfull, non-zero otherwise.
  522. ////////////////////////////////////////////////////////////////////////////////
  523. short RString::Load(RFile* pFile)
  524. {
  525. short sResult = 0;
  526. // Read length to separate var to avoid corrupting real one in case of read error
  527. long lLen;
  528. if (pFile->Read(&lLen) == 1)
  529. {
  530. // Check if there's more to read
  531. if (lLen > 0)
  532. {
  533. // Grow buffer
  534. Grow(lLen + 1);
  535. if (pFile->Read(m_pBuf, lLen) == lLen)
  536. {
  537. // Add null and set length
  538. m_pBuf[lLen] = 0;
  539. m_lStrLen = lLen;
  540. }
  541. else
  542. {
  543. Clear();
  544. TRACE("RString::Load(): Error reading string!\n");
  545. sResult = -1;
  546. }
  547. }
  548. else
  549. {
  550. Clear();
  551. }
  552. }
  553. else
  554. {
  555. Clear();
  556. TRACE("RString::Load(): Error reading string length!\n");
  557. sResult = -1;
  558. }
  559. return sResult;
  560. }
  561. ////////////////////////////////////////////////////////////////////////////////
  562. // Save this string to the specified RFile. This RString is not modified by the
  563. // save, even if an error occurs. Returns 0 if successfull, non-zero otherwise.
  564. ////////////////////////////////////////////////////////////////////////////////
  565. short RString::Save(RFile* pFile) const
  566. {
  567. // Save string length
  568. pFile->Write(m_lStrLen);
  569. // See if there's more to save
  570. if (m_lStrLen > 0)
  571. pFile->Write(m_pBuf, m_lStrLen);
  572. // Check for errors
  573. if (pFile->Error())
  574. {
  575. TRACE("RString::Save(): Error writing string!\n");
  576. return -1; // error
  577. }
  578. return 0; // success
  579. }
  580. ////////////////////////////////////////////////////////////////////////////////
  581. // EOF
  582. ////////////////////////////////////////////////////////////////////////////////