source: trunk/src/cppbase/bs_string_base.cpp

Last change on this file was 390, checked in by pr, 14 years ago

Add nlsThousandsLong to fix signed/unsigned display error.

  • Property svn:eol-style set to CRLF
  • Property svn:keywords set to Author Date Id Revision
File size: 17.7 KB
Line 
1
2/*
3 *@@sourcefile bs_string.cpp:
4 * BSString implementation.
5 *
6 *@@header "cppbase\bs_string.h"
7 */
8
9/*
10 * This file Copyright (C) 1999-2011 Ulrich M”ller.
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation, in version 2 as it comes in the COPYING
14 * file of this distribution.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 */
20
21#define OS2EMX_PLAIN_CHAR
22 // this is needed for "os2emx.h"; if this is defined,
23 // emx will define PSZ as _signed_ char, otherwise
24 // as unsigned char
25
26#include <os2.h>
27
28#include <stdio.h>
29#include <stdlib.h>
30#include <string.h>
31#include <stdarg.h>
32
33#include "setup.h"
34
35#include "helpers\nls.h"
36#include "helpers\stringh.h"
37#include "helpers\xstring.h"
38
39// base includes
40#include "cppbase\bs_base.h"
41#include "cppbase\bs_string.h"
42
43#pragma hdrstop
44
45// #define dprintf printf
46
47DEFINE_CLASS(BSStringBuf, BSRoot);
48
49DEFINE_CLASS(BSStringBase, BSRoot);
50
51// extern BSMutex G_mtxStrings = 1;
52
53/* ******************************************************************
54 *
55 * BSStringBase implementation
56 *
57 ********************************************************************/
58
59// define npos; this must be in the implementation, or we'll
60// get duplicate symbols from the linker
61const size_type BSStringBase::npos = (size_type)(-1);
62
63/*
64 *@@ Init:
65 *
66 *@@added V0.9.19 (2002-05-07) [umoeller]
67 */
68
69void BSStringBase::Init()
70{
71 _pBuf = NULL;
72}
73
74/*
75 *@@ FreeBuf:
76 * protected helper method to free
77 * allocated resources.
78 *
79 *@@changed V0.9.18 (2002-03-08) [umoeller]: renamed from Cleanup()
80 *@@changed V0.9.19 (2002-05-07) [umoeller]: fixed crash when buffers were shared, thanks knut
81 */
82
83void BSStringBase::FreeBuf()
84{
85 if (_pBuf)
86 {
87 if (_pBuf->_cShared)
88 {
89 // buffer is still shared: don't delete it,
90 // just lower share count
91 --(_pBuf->_cShared);
92 }
93 else
94 {
95 // buffer is not or no longer shared:
96 // delete the buffer
97 delete _pBuf;
98 // _pBuf = NULL; V0.9.19 (2002-05-07) [umoeller]
99 }
100
101 // always set _pBuf to NULL, otherwise any following
102 // code will still use the buffer
103 // V0.9.19 (2002-05-07) [umoeller]
104 _pBuf = NULL;
105 }
106}
107
108/*
109 *@@ CopyFrom:
110 * copies from another string.
111 *
112 * Preconditions: String must have
113 * been cleared before calling this.
114 *
115 *@@added V0.9.18 (2002-03-08) [umoeller]
116 */
117
118void BSStringBase::CopyFrom(const BSStringBase &s)
119{
120 if (_pBuf != s._pBuf)
121 {
122 ++(s._pBuf->_cShared);
123 _pBuf = s._pBuf;
124 }
125}
126
127/*
128 *@@ CopyFrom:
129 * copies from another string.
130 *
131 * Preconditions: String must have
132 * been cleared before calling this.
133 *
134 *@@added V0.9.18 (2002-03-08) [umoeller]
135 *@@changed V0.9.18 (2002-03-16) [umoeller]: fixed overflow
136 */
137
138void BSStringBase::CopyFrom(const BSStringBase &s,
139 size_type ulPos, // in: start copy, defaults to 0
140 size_type n) // in: copy count, defaults to npos
141{
142 if (s._pBuf)
143 {
144 if ( (ulPos == 0)
145 && (n == npos)
146 )
147 {
148 // copy full string:
149 // we can reuse the buf then
150 CopyFrom(s);
151 }
152 else
153 {
154 size_type lenSource = s._pBuf->_str.ulLength;
155 if (ulPos < lenSource)
156 {
157 // start is valid:
158 if (n == npos)
159 n = 0;
160 else if ( (n > lenSource) // V0.9.18 (2002-03-16) [umoeller]
161 || (ulPos + n > s._pBuf->_str.ulLength)
162 )
163 // n is too large: delimit then
164 n = s._pBuf->_str.ulLength - ulPos;
165
166 // create a new buffer
167 _pBuf = new BSStringBuf(s._pBuf->_str.psz + ulPos,
168 n, // length
169 0); // allocation: default
170 }
171 }
172 }
173}
174
175/*
176 *@@ CopyFrom:
177 * copies from another string.
178 *
179 * Preconditions: String must have
180 * been cleared before calling this.
181 *
182 *@@added V0.9.18 (2002-03-08) [umoeller]
183 */
184
185void BSStringBase::CopyFrom(const char *psz)
186{
187 if (psz)
188 _pBuf = new BSStringBuf(psz,
189 0, // length: default
190 0); // allocation: default
191}
192
193/*
194 *@@ CopyFrom:
195 * copies from a C string, starting with
196 * p1 up to p2, which is not included.
197 *
198 * Preconditions: String must have
199 * been cleared before calling this.
200 */
201
202void BSStringBase::CopyFrom(const char *p1, const char *p2)
203{
204 if (p2 > p1)
205 _pBuf = new BSStringBuf(p1,
206 p2-p1, // length: default
207 0); // allocation: default
208}
209
210/*
211 *@@ CopyFrom:
212 * copies from another single character.
213 *
214 * Preconditions: String must have
215 * been cleared before calling this.
216 *
217 *@@added V0.9.18 (2002-03-08) [umoeller]
218 */
219
220void BSStringBase::CopyFrom(char c) // in: character to copy
221{
222 if (c)
223 {
224 char sz[2];
225 sz[0] = c;
226 sz[1] = 0;
227 _pBuf = new BSStringBuf(sz,
228 1, // length
229 2); // allocation
230 }
231}
232
233/*
234 *@@ BSStringBase:
235 * default constructor to create an empty string.
236 * This is protected so that only subclasses
237 * can create such a thing.
238 */
239
240BSStringBase::BSStringBase(BSClassID &Class)
241 : BSRoot(Class)
242{
243 STRINGLOCK;
244 Init();
245}
246
247/*
248 *@@ BSStringBase:
249 * destructor.
250 */
251
252BSStringBase::~BSStringBase()
253{
254 STRINGLOCK;
255 FreeBuf();
256}
257
258/*
259 *@@ BSString:
260 * copy constructor.
261 *
262 * This gets called explicitly by the
263 * subclasses since I made the default
264 * copy constructor private.
265 *
266 * This gets called when BSStrings
267 * are thrown as exception information!
268 *
269 *@@added V0.9.1 (2000-01-07) [umoeller]
270 */
271
272BSStringBase::BSStringBase(const BSStringBase &s,
273 BSClassID &Class)
274 : BSRoot(Class)
275{
276 STRINGLOCK;
277 Init();
278 CopyFrom(s);
279}
280
281/*
282 *@@ reserve:
283 * reserves additional memory for the string.
284 *
285 * This serves two purposes:
286 *
287 * 1) As an internal method that is called every
288 * time before the string gets modified, to
289 * make sure we have a modifiable copy of the
290 * string with enough space if the string buffer
291 * is currently shared.
292 *
293 * 2) As a user method to prepare a string for
294 * multiple subsequent operations to avoid having
295 * to reallocate the string every time.
296 *
297 * If the string buf is currently shared, a new
298 * buffer is always allocated, even if stExtra is 0.
299 *
300 * If the string buf is not shared, this checks if
301 * the string buffer has at least stExtra bytes. If
302 * so, nothing happens. Otherwise the buffer is
303 * reallocated to have at least stExtra bytes.
304 *
305 *@@added V0.9.7 (2001-01-07) [umoeller]
306 */
307
308void BSStringBase::reserve(size_type stExtra) // in: total bytes to reserve, defaults to 0
309{
310 STRINGLOCK;
311
312 if (!_pBuf)
313 {
314 // string is empty:
315 if (stExtra)
316 _pBuf = new BSStringBuf(NULL,
317 0, // length
318 stExtra); // allocation
319 }
320 else
321 {
322 // we have a buffer:
323 if (_pBuf->_cShared)
324 {
325 // we're sharing that buffer:
326 // create a new one then
327 PXSTRING pstr = &_pBuf->_str;
328 if (stExtra < pstr->ulLength + 1)
329 // caller wants less memory than the string size:
330 // that's not a good idea, so raise that
331 stExtra = pstr->ulLength + 1;
332
333 FreeBuf(); // doesn't free because buf is shared
334 _pBuf = new BSStringBuf(pstr->psz,
335 pstr->ulLength,
336 stExtra);
337 }
338 else
339 {
340 // buffer is not shared: expand that one
341 // (this does nothing if stExtra is less than
342 // the current size)
343 xstrReserve(&_pBuf->_str, stExtra);
344 _pBuf->ClearShiftTable();
345 }
346 }
347}
348
349/*
350 *@@ _take_from:
351 * takes over the string buffers from an
352 * existing XSTRING without reallocating.
353 *
354 * This also zeroes the XSTRING buffer
355 * then because the memory pointers
356 * must not be freed afterwards.
357 */
358
359void BSStringBase::_take_from(XSTRING &str)
360{
361 STRINGLOCK;
362
363 FreeBuf();
364 _pBuf = new BSStringBuf(str);
365}
366
367/*
368 *@@ _itoa10:
369 * sets the string to the decimal representation
370 * of the given int value.
371 *
372 * If cThousands is non-null, it is used as
373 * a thousands separator.
374 *
375 *@@added V0.9.18 (2002-03-08) [umoeller]
376 *@@changed WarpIN V1.0.20 (2011-05-30) [pr]: fix signed/unsigned error
377 */
378
379void BSStringBase::_itoa10(int value,
380 char cThousands) // in: thousands separator or 0 if none
381{
382 STRINGLOCK;
383
384 CHAR sz[100];
385 if (cThousands)
386 nlsThousandsLong(sz, value, cThousands);
387 else
388 _itoa(value, sz, 10);
389
390 FreeBuf();
391 CopyFrom(sz);
392}
393
394/*
395 *@@ _printf:
396 * this replaces the current string with
397 * a new string, which is formatted according
398 * to printf() rules.
399 *
400 * Note: as far as I know, something like this
401 * is NOT defined with the C++ string class.
402 */
403
404size_type BSStringBase::_printf(const char *pszFormatString,
405 ...)
406{
407 STRINGLOCK;
408
409 size_type ul = 0;
410
411 char szBuf[5000];
412 va_list arg_ptr;
413
414 // allocate memory
415 va_start(arg_ptr, pszFormatString);
416 ul = vsprintf(szBuf, pszFormatString, arg_ptr);
417 va_end(arg_ptr);
418
419 FreeBuf();
420 CopyFrom(szBuf);
421
422 return (ul);
423}
424
425/*
426 *@@ erase:
427 * removes at max n characters from the
428 * string, starting at ulPos.
429 *
430 * Always returns *this.
431 */
432
433BSStringBase& BSStringBase::erase(size_type ulPos, // in: start pos, defaults to 0
434 size_type n) // in: count, defaults to npos
435{
436 STRINGLOCK;
437
438 size_type ulMyLen;
439
440 if ( (ulMyLen = size())
441 && (ulPos < ulMyLen)
442 && (n)
443 )
444 {
445 // we have a buffer:
446 if ( (n == npos)
447 && (ulPos == 0)
448 )
449 // delete all: that's easy
450 FreeBuf();
451 else
452 {
453 // assert that we have a modifiable
454 // copy with enough space
455 reserve(0);
456
457 if (n == npos)
458 {
459 // delete rest of string:
460 _pBuf->_str.psz[ulPos] = '\0';
461 _pBuf->_str.ulLength = ulPos;
462 // leave allocation
463 }
464 else
465 {
466 // delete something in the middle:
467 // overwrite the part of the string at ulPos
468 // with the following part, starting at ulPos + n
469 char *pSource = _pBuf->_str.psz + ulPos + n,
470 *pTarget = _pBuf->_str.psz + ulPos;
471 strcpy(pTarget, pSource);
472 _pBuf->_str.ulLength -= n;
473 // allocation doesn't change
474 }
475
476 _pBuf->ClearShiftTable();
477 }
478 }
479
480 return *this;
481}
482
483/*
484 *@@ compare:
485 * returns 0 if the two strings are the
486 * same, -1 if "this" is smaller, 1 if
487 * "s" is smaller.
488 */
489
490int BSStringBase::compare(const BSStringBase &s)
491 const
492{
493 STRINGLOCK;
494
495 // if both buffers are the same, that's easy
496 if (_pBuf == s._pBuf)
497 return 0;
498
499 size_type ul1 = size(),
500 ul2 = s.size();
501
502 if ((ul1) && (ul2))
503 return (strcmp(_pBuf->_str.psz, s._pBuf->_str.psz));
504 else if (ul1)
505 // ul1 defined, but ul2 not: string 2 is smaller
506 return (1);
507 else if (ul2)
508 // ul1 not defined, but ul2 is: this is smaller
509 return (-1);
510
511 // none defined:
512 return (0);
513}
514
515/*
516 *@@ compare:
517 * compares the given C string to this.
518 *
519 * Returns:
520 *
521 * -- 0: the two are the same.
522 *
523 * -- < 0: this is smaller than psz.
524 *
525 * -- > 0: psz is smaller than this.
526 *
527 *@@added V0.9.18 (2002-03-08) [umoeller]
528 */
529
530int BSStringBase::compare(const char *psz)
531 const
532{
533 STRINGLOCK;
534
535 // if both buffers are the same, that's easy
536 if ( (_pBuf)
537 && (_pBuf->_str.psz == psz)
538 )
539 return 0;
540
541 size_type ul1 = size();
542
543 if ((ul1) && (psz))
544 return (strcmp(_pBuf->_str.psz, psz));
545 else if (ul1)
546 // ul1 defined, but ul2 not: string 2 is smaller
547 return (1);
548 else if (psz)
549 // ul1 not defined, but ul2 is: this is smaller
550 return (-1);
551
552 // none defined:
553 return (0);
554}
555
556/*
557 *@@ _find_replace:
558 * replaces the first occurence of strFind
559 * with strReplaceWith which is found after
560 * the ulStartPos offset.
561 *
562 * Returns the offset at which strFind was
563 * found or BSString::npos if it wasn't found.
564 *
565 * To replace all occurences in the string,
566 * keep calling this function until BSString::npos
567 * is returned, like this:
568 *
569 + string str = "part 1, part 2";
570 + ULONG ulPos = 0;
571 + while ( (ulPos == str._find_replace("part", "piece", ulPos))
572 + != string::npos)
573 + ;
574 *
575 * Note: as far as I know, something like this
576 * is NOT defined with the C++ string class.
577 *
578 *@@changed V1.0.1 (2003-02-02) [umoeller]: fixed crash if strReplaceWith had null buffer
579 */
580
581size_type BSStringBase::_find_replace(BSStringBase &strFind,
582 const BSStringBase &strReplaceWith,
583 size_type *pulPos) // in: where to start search (defaults to 0)
584{
585 STRINGLOCK;
586
587 if ( (*pulPos < size())
588 && (strFind.size())
589 )
590 {
591 reserve(0);
592
593 // if this is the first time strFind is used as
594 // a "find" string, allocate a shift table...
595 if (strFind._pBuf->_pShiftTable == NULL)
596 {
597 strFind._pBuf->_pShiftTable = (size_t*)malloc(sizeof(size_t) * 256);
598 strFind._pBuf->_fRepeat = FALSE;
599 }
600
601 if ( (strFind._pBuf->_pShiftTable)
602 && (xstrFindReplace(&_pBuf->_str,
603 pulPos,
604 &strFind._pBuf->_str,
605 // fixed crash here V1.0.1 (2003-02-02) [umoeller]
606 (strReplaceWith._pBuf)
607 ? &strReplaceWith._pBuf->_str
608 : NULL,
609 strFind._pBuf->_pShiftTable,
610 (PBOOL)&strFind._pBuf->_fRepeat))
611 )
612 {
613 // found:
614 return (*pulPos);
615 }
616 }
617
618 // unchanged:
619 return (npos);
620}
621
622/*
623 *@@ _find_replace:
624 * second version of _find_replace, which takes
625 * a const PSZ as input.
626 *
627 * Note: as far as I know, something like this
628 * is NOT defined with the C++ string class.
629 */
630
631size_type BSStringBase::_find_replace(const char *pszFind,
632 const BSStringBase &strReplaceWith,
633 size_type *pulPos)
634{
635 STRINGLOCK;
636
637 if ( (*pulPos < size())
638 && (pszFind)
639 && (*pszFind)
640 )
641 {
642 // create temporary "find" string
643 BSString strFind(pszFind);
644 // initialize shift table
645 strFind._pBuf->_pShiftTable = (size_t*)malloc(sizeof(size_t) * 256);
646 strFind._pBuf->_fRepeat = FALSE;
647 return (_find_replace(strFind,
648 strReplaceWith,
649 pulPos));
650 }
651
652 return (npos);
653}
654
655/*
656 *@@ _find_replace:
657 * third version of _find_replace, which simply
658 * replaces the first occurence of one character
659 * with another.
660 *
661 * This is very inexpensive because no memory
662 * management is involved.
663 *
664 * Note: as far as I know, something like this
665 * is NOT defined with the C++ string class.
666 *
667 * Warning: Be careful when using this on
668 * BSUString instances. This can break UTF-8
669 * code sequences if the characters are non-ASCII.
670 *
671 *@@added V0.9.6 (2000-11-01) [umoeller]
672 */
673
674size_type BSStringBase::_find_replace(char cFind,
675 char cReplace,
676 size_type *pulPos)
677{
678 STRINGLOCK;
679
680 PSZ p;
681 if (p = strchr(_pBuf->_str.psz, cFind))
682 {
683 // offset:
684 *pulPos = p - _pBuf->_str.psz;
685 reserve(0);
686 _pBuf->_str.psz[*pulPos] = cReplace;
687 return (*pulPos);
688 }
689
690 return (npos);
691}
692
693
Note: See TracBrowser for help on using the repository browser.