home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / yacl-012.zip / base / string.cxx < prev    next >
C/C++ Source or Header  |  1995-04-04  |  27KB  |  1,209 lines

  1.  
  2.  
  3.  
  4.  
  5. /*
  6.  *
  7.  *          Copyright (C) 1994, M. A. Sridhar
  8.  *  
  9.  *
  10.  *     This software is Copyright M. A. Sridhar, 1994. You are free
  11.  *     to copy, modify or distribute this software  as you see fit,
  12.  *     and to use  it  for  any  purpose, provided   this copyright
  13.  *     notice and the following   disclaimer are included  with all
  14.  *     copies.
  15.  *
  16.  *                        DISCLAIMER
  17.  *
  18.  *     The author makes no warranties, either expressed or implied,
  19.  *     with respect  to  this  software, its  quality, performance,
  20.  *     merchantability, or fitness for any particular purpose. This
  21.  *     software is distributed  AS IS.  The  user of this  software
  22.  *     assumes all risks  as to its quality  and performance. In no
  23.  *     event shall the author be liable for any direct, indirect or
  24.  *     consequential damages, even if the  author has been  advised
  25.  *     as to the possibility of such damages.
  26.  *
  27.  */
  28.  
  29.  
  30.  
  31.  
  32. #ifdef __GNUC__
  33. #pragma implementation
  34. #endif
  35.  
  36.  
  37.  
  38. #include <string.h>
  39. #include <ctype.h>
  40. #include <stdlib.h>
  41. #include <stdarg.h>
  42. #include <stdio.h>
  43. #include <math.h>
  44. #include <iostream.h>
  45.  
  46. #include "base/string.h"
  47. #include "base/strsplit.h"
  48. #include "base/bytstrng.h"
  49. #include "base/error.h"
  50. #include "base/strgseq.h"
  51. #include "base/binding.h"
  52. #include "base/stream.h"
  53.  
  54.  
  55.  
  56.  
  57. #ifdef DEBUG
  58. #include "base/memory.h"
  59. #endif
  60.  
  61. #if defined(__BORLANDC__) && !defined(__OS2__)
  62. #define STRNCMP _fstrncmp
  63. #else
  64. #define STRNCMP strncmp
  65. #endif
  66.  
  67.  
  68. #ifdef __DEC_ULTRIX__
  69. extern "C" int gcvt (double, int, char*);
  70. #endif
  71.  
  72.  
  73. #define NEW_OP new
  74.  
  75.  
  76.  
  77. const short DEFAULT_START_SIZE = 12; // Must be at least 10 to
  78.                                      // accommodate long integers
  79.  
  80.  
  81. // Some utility functions
  82.  
  83. void int_to_as (long val, char out[], short minwidth, char padChar = ' ')
  84. {
  85.     char tmp[50]; // Won't have more than this many digits
  86.     short i, n, sign = 0;
  87.  
  88.     if (val == 0) {
  89.         n = (short) maxl (minwidth, 1);
  90.         for (i = 0; i < n-1; i++)
  91.             out[i] = padChar;
  92.         out[n-1] = '0';
  93.         out[n] = '\0';
  94.         return;
  95.     }
  96.     if (val < 0) {
  97.         sign = -1;
  98.         val = -val;
  99.     }
  100.  
  101.     for (n = 0; val > 0; n++) {
  102.         tmp[n] = (char) (val % 10 + '0');
  103.         val /= 10;
  104.     }
  105.     if (sign < 0) minwidth--;
  106.     if (padChar != ' ') {
  107.         for (; n < minwidth; n++) {
  108.             tmp[n] = padChar;
  109.         }
  110.         if (sign == -1) 
  111.             tmp[n++] = '-';
  112.     }
  113.     else {
  114.         if (sign == -1) 
  115.             tmp[n++] = '-';
  116.         for (; n < minwidth; n++) {
  117.             tmp[n] = padChar;
  118.         }
  119.     }
  120.     for (i = n-1; i >= 0; i--) {
  121.         out[n-1-i] = tmp[i];
  122.     }
  123.     out [n] = '\0';
  124. }
  125.  
  126.  
  127. #ifdef __GNUC__
  128. // G++ does not have the strstr function for finding substring
  129. // occurrences. So here we implement the naive algorithm for string
  130. // search.
  131. static char* strstr (char* s1, const char* s2)
  132. {
  133.     if (!s1 || !s2)
  134.         return NULL;
  135.     long m = strlen (s1);
  136.     long n = strlen (s2);
  137.     long i, j;
  138.     for (i = 0; i < m-n+1; i++) {
  139.         for (j = 0; j < n; j++)
  140.             if (s2[j] != s1[i+j])
  141.                 break;
  142.         if (j == n)
  143.             return s1+i;
  144.     }
  145.     return NULL;
  146. }
  147. #endif        
  148.  
  149.     
  150.         
  151. CL_DEFINE_CLASS(CL_String, _CL_String_CLASSID);
  152.  
  153. CL_String::CL_String () 
  154. {
  155.     _Init (DEFAULT_START_SIZE);
  156. }
  157.  
  158.  
  159. CL_String::CL_String (const char* strg)
  160. {
  161.     if (strg != NULL) {
  162.         long n = strlen (strg);
  163.         if (!_Init (n + DEFAULT_START_SIZE + 1))
  164.             return;
  165.         strcpy (_string, strg);
  166.         _size = n;
  167.     }
  168.     else
  169.         _Init (DEFAULT_START_SIZE+1);
  170. }
  171.  
  172.  
  173. CL_String::CL_String (const CL_String& strg)
  174. {
  175.     long n = strg.Size();
  176.     if (!_Init (n + DEFAULT_START_SIZE + 1))
  177.         return;
  178.     *this = strg;
  179.     _size = n;
  180. }
  181.  
  182.  
  183. CL_String::CL_String (long value, short minwidth, char padChar)
  184. {
  185.     if (!_Init ((minwidth > DEFAULT_START_SIZE ? minwidth :
  186.                 DEFAULT_START_SIZE) + 1))
  187.         return; 
  188.     int_to_as (value, _string, minwidth, padChar);
  189.     _size = strlen (_string);
  190. }
  191.  
  192.  
  193.  
  194. CL_String::CL_String (CL_ByteArray& b)
  195. {
  196.     char* b_ptr = (char*) b.AsPtr();
  197.     char* p = b_ptr;
  198.     long i;
  199.     long n = b.Size();
  200.     for (i = 0; i < n; i++, p++)
  201.         if (*p == 0)
  202.             break;
  203.     if (!_Init (i))
  204.         return;
  205.     strncpy (_string, b_ptr, i);
  206.     _string[i] = '\0';
  207.     _size = i;
  208. }
  209.  
  210.  
  211.  
  212.  
  213. CL_String::CL_String (char c, short count)
  214. {
  215.     if (!_Init (count+1))
  216.         return;
  217.     for (short i=0; i < count; i++)
  218.         _string[i] = c;
  219.     _string[count] = '\0';
  220.     _size = strlen (_string); // Might not equal count, if
  221.                               // c is the null character!
  222. }
  223.  
  224.  
  225.  
  226.     
  227.     
  228. CL_String::~CL_String()
  229. {
  230.     if (_string)
  231.         delete [] _string;
  232. }
  233.  
  234.  
  235.  
  236. long CL_String::AsLong() const
  237. {
  238.     short i = 0, sign = 1;
  239.     long value = 0;
  240.  
  241.     while (i < Size() && _string[i] == ' ') i++;
  242.     if (i >= Size())
  243.         return 0;
  244.     if (_string[i] == '-') {
  245.         i++;
  246.         sign = -1;
  247.     }
  248.     while (_string[i] >= '0' && _string[i] <= '9') {
  249.         value = value * 10 + _string[i] - '0';
  250.         i++;
  251.     }
  252.     return value * sign;
  253. }
  254.  
  255.  
  256. double CL_String::AsDouble () const
  257. {
  258.     return atof (_string);
  259. }
  260.  
  261.  
  262.  
  263. bool CL_String::Insert (const char* p, long pos)
  264. {
  265.     if (!PrepareToChange())
  266.         return FALSE;
  267.     if (!_DoInsert (p, pos))
  268.         return FALSE;
  269.     Notify();
  270.     return TRUE;
  271. }
  272.  
  273.  
  274. bool CL_String::_DoInsert (const char* p, long pos)
  275. {
  276.     if (pos < -1 || pos >= _size)
  277.         return FALSE;
  278.     long n = strlen (p);
  279.     if (n <= 0)
  280.         return TRUE;
  281.     if (n + _size >= _capacity) {
  282.         // Not enough room: expand the string
  283.         long new_cap = _capacity + n + DEFAULT_START_SIZE;
  284.         char* new_data = new char[new_cap];
  285.         if (!new_data)
  286.             return FALSE;
  287.         _capacity = new_cap;
  288.         strncpy (new_data, _string, pos+1);
  289.         strncpy (new_data + pos + 1, p, n);
  290.         if (pos < _size - 1)
  291.             strcpy  (new_data + pos + n + 1, _string + pos + 1);
  292.         else
  293.             new_data [_size + n] = '\0';
  294.         delete [] _string;
  295.         _string = new_data;
  296.     }
  297.     else {
  298.         if (pos < _size - 1) {
  299.             memmove (_string+pos+n+1, _string+pos+1, _size-pos-1);
  300.             memcpy  (_string+pos+1, p, n);
  301.         }
  302.         else
  303.             strcpy (_string + _size, p);
  304.     }
  305.     _size += n;
  306.     return TRUE;
  307. }
  308.  
  309.  
  310. bool CL_String::Insert (char c, long pos)
  311. {
  312.     char buf[2];
  313.     buf[0] = c;
  314.     buf[1] = '\0';
  315.     return Insert (buf, pos);
  316. }
  317.  
  318.  
  319.  
  320. bool CL_String::PadTo (long num_chars, char pad_char,
  321.                        bool left_justified)
  322. {
  323.     if (!PrepareToChange())
  324.         return FALSE;
  325.     if (num_chars <= _size)
  326.         return FALSE;
  327.     char* pad_strg = new char [_capacity = num_chars+DEFAULT_START_SIZE+1];
  328.     if (!pad_strg)
  329.         return FALSE;
  330.     memset (pad_strg, pad_char, num_chars);
  331.     pad_strg[num_chars]  = '\0';
  332.     if (left_justified) 
  333.         strncpy (pad_strg, _string, _size);
  334.     else
  335.         strncpy (pad_strg + num_chars - _size,
  336.                  _string, _size);
  337.     delete _string;
  338.     _string = pad_strg;
  339.     _size = num_chars;
  340.     Notify ();
  341.     return TRUE;
  342. }
  343.  
  344.  
  345.  
  346. CL_String CL_String::InUpperCase () const
  347. {
  348.     CL_String t (*this);
  349.     t.ToUpper ();
  350.     return t;
  351. }
  352.  
  353.  
  354. CL_String CL_String::InLowerCase () const
  355. {
  356.     CL_String t (*this);
  357.     t.ToLower ();
  358.     return t;
  359. }
  360.  
  361.  
  362. long CL_String::ToUpper ()
  363. {
  364.     if (!PrepareToChange())
  365.         return 0;
  366.     long count = 0;
  367.     for (long i = 0; i < _size; i++)
  368.         if (islower (_string[i])) {
  369.             _string[i] = toupper (_string[i]);
  370.             count++;
  371.         };
  372.     Notify ();
  373.     return count;
  374. }
  375.  
  376.  
  377. long CL_String::ToLower ()
  378. {
  379.     if (!PrepareToChange())
  380.         return 0;
  381.     long count = 0;
  382.     for (long i = 0; i < _size; i++)
  383.         if (isupper (_string[i])) {
  384.             _string[i] = tolower (_string[i]);
  385.             count++;
  386.         };
  387.     Notify ();
  388.     return count;
  389. }
  390.  
  391.  
  392. bool CL_String::WordCapitalize ()
  393. {
  394.     if (!PrepareToChange())
  395.         return FALSE;
  396.     if (_size <= 0 || !isalpha (_string[0]))
  397.         return FALSE;
  398.     if (islower (_string[0]))
  399.         _string[0] = toupper (_string[0]);
  400.     for (long i = 1; i < _size; i++) {
  401.         if (!isalpha (_string[i]))
  402.             break;
  403.         if (isupper (_string[i]))
  404.             _string[i] = tolower (_string[i]);
  405.     }
  406.     Notify ();
  407.     return TRUE;
  408. }
  409.  
  410.  
  411.         
  412.  
  413. //
  414. // Substructure extraction
  415. //
  416.  
  417.  
  418. long CL_String::CharIndex (char c, long pos) const
  419. {
  420.     for (long i=pos; i < _size; i++) {
  421.         if (_string[i] == c) return i;
  422.     }
  423.     return -1;
  424. }
  425.  
  426.  
  427.  
  428. long CL_String::NCharIndex (char c, long pos) const
  429. {
  430.     for (long i=pos; i < _size; i++) {
  431.         if (_string[i] != c) return i;
  432.     }
  433.     return -1;
  434. }
  435.  
  436.  
  437. CL_Substring CL_String::operator () (long position, long size)
  438. {
  439.     if (position >= _size)
  440.         return CL_Substring (*this, _size, 0);
  441.     long len = minl (size,  _size-position);
  442.     return CL_Substring (*this, position, len);
  443.     // return sub;
  444. }
  445.  
  446.  
  447.  
  448.          
  449. long CL_String::CharIndex (const char* s, long pos)  const
  450. {
  451.     if (!s)
  452.         return 0;
  453.     long n = strlen(s);
  454.  
  455.     for (long i=pos; i < _size; i++) {
  456.         for (long j = 0; j < n; j++)
  457.             if (_string[i] == s[j]) return i;
  458.     }
  459.     return -1;
  460. }
  461.  
  462.  
  463.  
  464. long CL_String::NCharIndex (const char* s, long pos) const
  465. {
  466.     if (!s)
  467.         return 0;
  468.     long n = strlen(s);
  469.     for (long i = pos; i < _size; i++) {
  470.         for (long j = 0; j < n; j++) {
  471.             if (_string[i] == s[j]) break;
  472.         }
  473.         if (j >= n) return i;
  474.     }
  475.     return -1;
  476. }
  477.     
  478.  
  479. bool CL_String::IsPrefixOf (const char* p) const 
  480. {
  481.     if (!p)
  482.         return FALSE;
  483.     long l = strlen (p);
  484.     
  485.     if (_size > l)
  486.         return FALSE;
  487.     short b = STRNCMP (_string, p, _size);
  488.     return (b == 0) ? TRUE : FALSE;
  489. }
  490.  
  491.  
  492. long CL_String::Index (const char* p, long pos, long n) const
  493. {
  494.     if (!p || !_string)
  495.         return -1;
  496.     long len = strlen(p);
  497.     if (pos > _size - len)
  498.         return -1;
  499.     char* q = _string + pos;
  500.     for (; n > 0; n--) {
  501.         q = strstr (q, p);
  502.         if (q == NULL)
  503.             return -1;
  504.         if (n > 1) q++;
  505.     }
  506.     return (q == NULL) ? -1 : (q - _string);
  507. }
  508.  
  509.  
  510.  
  511.  
  512. long CL_String::Replace (const char* s1, const char* s2, long pos, long n)
  513. {
  514.     if (!PrepareToChange())
  515.         return 0;
  516.     long len1, len2;
  517.     if (!s1 || (len1 = strlen (s1)) == 0)
  518.         return 0;
  519.     const char* repl = (s2) ?  s2 : "";
  520.     len2 = strlen (repl);
  521.     long new_cap = maxl (_capacity + n* (len2 - len1), _capacity);
  522.     char* new_array = new char [new_cap];
  523.     if (!new_array)
  524.         return 0;
  525.     char* in = _string+pos;
  526.     char* out = new_array;
  527.     strncpy (out, _string, pos);
  528.     out += pos;
  529.     char* q;
  530.     long repl_count = 0;
  531.     while (repl_count < n && in - _string < _size) {
  532.         q = strstr (in, s1);
  533.         if (!q) break;
  534.         long copy_len = q-in;
  535.         strncpy (out, in, copy_len);
  536.         strncpy (out + copy_len, repl, len2);
  537.         out += copy_len + len2;
  538.         in = q+len1;
  539.         repl_count ++;
  540.     }
  541.     strcpy (out, in);
  542.     _size = strlen (new_array);
  543.     _capacity = new_cap;
  544.     delete [] _string;
  545.     _string = new_array;
  546.     Notify();
  547.     return repl_count;
  548. }
  549.  
  550.  
  551. // Beginning at position pos, replace all occurrences of s1 with
  552. // s2, as in Replace. Return the number of replacements that took
  553. // place.
  554. long CL_String::ReplaceAll (const char* s1, const char* s2, long pos)
  555. {
  556.     return Replace (s1, s2, _size, pos);
  557. }
  558.  
  559.  
  560.  
  561. CL_String CL_String::Field (long n, const char field_seps[]) const
  562. {
  563.     if (_size <= 0)
  564.         return "";
  565.     if (n == 0)
  566.         return *this;
  567.     CL_StringSplitter splitter (*this, field_seps);
  568.     CL_String s;
  569.     for (splitter.Reset(); n > 0; n--) {
  570.         s = splitter.Next();
  571.         if (s.Length() == 0) break;
  572.     }
  573.     return s;
  574. }
  575.  
  576.  
  577. CL_StringSequence CL_String::Split (const char field_seps[]) const
  578. {
  579.     CL_StringSplitter splitter (*this, field_seps);
  580.     CL_StringSequence seq;
  581.     CL_String s;
  582.  
  583.     do {
  584.         s = splitter.Next();
  585.     if (s.Length() == 0) break;
  586.         seq.Add (s);
  587.     } while (1);
  588.     return seq;
  589. }
  590.  
  591.  
  592. CL_StringSequence CL_String::Split (char c) const
  593. {
  594.     CL_StringSequence seq;
  595.     CL_String s;
  596.  
  597.     if (!_string)
  598.         return seq; // No memory
  599.     for (long i = 0; i < _size; i++) {
  600.         if (_string[i] == c) {
  601.             seq.Add (s);
  602.             s = "";
  603.         }
  604.         else
  605.             s.Append (_string[i]);
  606.     }
  607.     seq.Add (s); // Add the last field
  608.     return seq;
  609. }
  610.  
  611.  
  612.  
  613. long CL_String::Split (CL_String fld[], long n, const char field_seps[]) const
  614. {
  615.     CL_StringSplitter splitter (*this, field_seps);
  616.     for (long i = 0; i < n-1; i++) {
  617.         fld[i] = splitter.Next();
  618.         if (fld[i].Length() == 0) break;
  619.     }
  620.     if (i >= n-1) {
  621.         fld[i] = splitter.Remaining();
  622.         return fld[i].Size() > 0 ? n : n-1;
  623.     }
  624.     return i;
  625. }
  626.  
  627.  
  628.  
  629.  
  630.  
  631.  
  632. //
  633. // Comparison
  634. //
  635. bool CL_String::operator<  (const CL_Object& strg) const
  636. {
  637.     if (!IsA (strg))
  638.         return FALSE;
  639.     return (strcmp (_string, ((const CL_String&) strg)._string) < 0);
  640. }
  641.  
  642. bool CL_String::operator<= (const CL_Object& strg) const
  643. {
  644.     if (!IsA (strg))
  645.         return FALSE;
  646.     return (strcmp (_string, ((const CL_String&) strg)._string) <= 0);
  647. }
  648.  
  649. bool CL_String::operator== (const CL_Object& strg) const
  650. {
  651.     if (!IsA (strg))
  652.         return FALSE;
  653.     return (strcmp (_string, ((const CL_String&) strg)._string) == 0);
  654. }
  655.  
  656. bool CL_String::operator>  (const CL_Object& strg) const
  657. {
  658.     if (!IsA (strg))
  659.         return FALSE;
  660.     return (strcmp (_string, ((const CL_String&) strg)._string) > 0);
  661. }
  662.  
  663. bool CL_String::operator>= (const CL_Object& strg) const
  664. {
  665.     if (!IsA (strg))
  666.         return FALSE;
  667.     return (strcmp (_string, ((const CL_String&) strg)._string) >= 0);
  668. }
  669.  
  670. bool CL_String::operator!= (const CL_Object& strg) const
  671. {
  672.     if (!IsA (strg))
  673.         return FALSE;
  674.     return (strcmp (_string, ((const CL_String&) strg)._string) != 0);
  675. }
  676.  
  677.  
  678.  
  679.  
  680.  
  681. bool CL_String::operator<  (const char* strg) const
  682. {
  683.     if (!strg)
  684.         return FALSE;
  685.     return (strcmp (_string, strg) < 0);
  686. }
  687.  
  688. bool CL_String::operator<= (const char* strg) const
  689. {
  690.     if (!strg)
  691.         return (_size == 0);
  692.     return (strcmp (_string, strg) <= 0);
  693. }
  694.  
  695. bool CL_String::operator== (const char* strg) const
  696. {
  697.     if (!strg)
  698.         return (_size == 0);
  699.     return (strcmp (_string, strg) == 0);
  700. }
  701.  
  702. bool CL_String::operator>  (const char* strg) const
  703. {
  704.     if (!strg)
  705.         return (_size > 0);
  706.     return (strcmp (_string, strg) > 0);
  707. }
  708.  
  709. bool CL_String::operator>= (const char* strg) const
  710. {
  711.     if (!strg)
  712.         return TRUE;
  713.     return (strcmp (_string, strg) >= 0);
  714. }
  715.  
  716. bool CL_String::operator!= (const char* strg) const
  717. {
  718.     if (!strg)
  719.         return (_size > 0);
  720.     return (strcmp (_string, strg) != 0);
  721. }
  722.  
  723.  
  724.  
  725. short CL_String::Compare (const CL_Object& obj) const
  726. {
  727.     if (!IsA (obj))
  728.         return (this < (CL_String*) &obj ? -1 : 1);
  729.     return (strcmp (_string, ((const CL_String&) obj)._string));
  730. }
  731.  
  732.  
  733. short CL_String::Compare (const CL_String& obj) const
  734. {
  735.     return (strcmp (_string,  obj._string));
  736. }
  737.  
  738.  
  739. bool CL_String::CompareWith (const CL_String& obj,
  740.                              CL_Object::ComparisonOperator op) const
  741. {
  742.     switch (op) {
  743.     case CL_Object::OP_EQUAL:
  744.         return (strcmp (_string, obj._string) == 0);
  745.         
  746.     case CL_Object::OP_LESSTHAN:
  747.         return (strcmp (_string, obj._string) < 0);
  748.         
  749.     case CL_Object::OP_GTRTHAN:
  750.         return (strcmp (_string, obj._string) > 0);
  751.         
  752.     case CL_Object::OP_LESSEQ:
  753.         return (strcmp (_string, obj._string) <= 0);
  754.         
  755.     case CL_Object::OP_GTREQ:
  756.         return (strcmp (_string, obj._string) >= 0);
  757.         
  758.     case CL_Object::OP_NOTEQUAL:
  759.         return (strcmp (_string, obj._string) != 0);
  760.         
  761.     case CL_Object::OP_PREFIX:
  762.         return (obj._size <= _size &&
  763.                 strncmp (_string, obj._string, obj._size) == 0);
  764.         
  765.     case CL_Object::OP_CONTAINS:
  766.         return (obj._size <= _size &&
  767.                 strstr (_string, obj._string) != NULL);
  768.  
  769.  
  770.     default:
  771.         CL_Error::Warning ("CL_String::CompareWith: bad operator %d",
  772.                            (short) op);
  773.     }
  774.     return FALSE;
  775. }
  776.  
  777.  
  778.  
  779. //
  780. // Assignment
  781. //
  782. void CL_String::operator= (const char* strg)
  783. {
  784.     if (!PrepareToChange())
  785.         return;
  786.     if (!strg) {
  787.         _size = 0;
  788.         _string[0] = '\0';
  789.         Notify();
  790.         return;
  791.     }
  792.     long n = strlen (strg);
  793.  
  794.     if (strg == _string) return; // Nothing to do
  795.     if (_capacity >= n+1) {
  796.         strcpy (_string, strg);
  797.         _size = n;
  798.     }
  799.     else {
  800.         char* p = NEW_OP char [_capacity = n + DEFAULT_START_SIZE + 1];
  801.         if (!p)
  802.             return; // No memory -- return without notifying dependents
  803.         strcpy (p, strg);
  804.         _size = n;
  805.         delete [] _string;
  806.         _string = p;
  807.     }
  808.     Notify ();
  809. }
  810.  
  811.     
  812. void CL_String::operator= (const CL_String& strg)
  813. {
  814.     if (this == &strg || !PrepareToChange())
  815.         return;
  816.     long n = strg.Size();
  817.     if (strg._string == _string) return; // Nothing to do
  818.  
  819.     if (_capacity >= strg.Size() + 1) {
  820.         strcpy (_string, strg._string);
  821.         _size = n;
  822.     }
  823.     else {
  824.         char* p = NEW_OP char [_capacity = n + DEFAULT_START_SIZE + 1];
  825.         if (!p)
  826.             return; // No memory
  827.         strcpy (p, strg._string);
  828.         _size = n;
  829.         delete [] _string;
  830.         _string = p;
  831.     }
  832.     Notify ();
  833. }
  834.  
  835.  
  836. void CL_String::operator= (long value)
  837. {
  838.     if (!PrepareToChange())
  839.         return;
  840.     if (_capacity < DEFAULT_START_SIZE) {
  841.         delete [] _string;
  842.         _string = new char [_capacity = DEFAULT_START_SIZE];
  843.         if (!_string) {
  844.             _capacity = 0; // No memory
  845.             return;
  846.         }
  847.     }
  848.     int_to_as (value, _string, 1);
  849.     _size = strlen (_string);
  850.     Notify ();
  851. }
  852.  
  853.  
  854.  
  855.  
  856. void CL_String::operator= (double d)
  857. {
  858.     if (!PrepareToChange())
  859.         return;
  860.     if (_capacity < DEFAULT_START_SIZE) {
  861.         delete [] _string;
  862.         _string = new char [_capacity = DEFAULT_START_SIZE];
  863.         if (!_string) {
  864.             _capacity = 0; // No memory
  865.             return;
  866.         }
  867.     }
  868.     gcvt (d, _capacity-1, _string);
  869.     _size = strlen (_string);
  870.     Notify();
  871. }
  872.  
  873.  
  874.  
  875. //
  876. // Concatenation
  877. //
  878. CL_String CL_String::operator+  (const char* strg) const
  879. {
  880.     long len;
  881.     if (!strg || (len = strlen(strg)) == 0)
  882.         return *this;
  883.     long n = _size + len + 1;
  884.     CL_String q;
  885.     if (q._capacity < n) {
  886.         delete [] q._string;
  887.         q._string = NEW_OP char [q._capacity = n +
  888.                                  DEFAULT_START_SIZE + 1];
  889.         if (!q._string)
  890.             return ""; // No memory
  891.     }
  892.     strcpy (q._string, _string);
  893.     strcpy (q._string + _size, strg);
  894.     q._size = n-1;
  895.     return q;
  896. }
  897.  
  898.         
  899. CL_String CL_String::operator+  (const CL_String& strg) const
  900. {
  901.     long n = _size + strg.Size() + 1;
  902.     CL_String q;
  903.     if (q._capacity < n) {
  904.         delete [] q._string;
  905.         q._string = NEW_OP char [q._capacity = n +
  906.                                  DEFAULT_START_SIZE + 1];
  907.         if (!q._string)
  908.             return ""; // No memory
  909.     }
  910.     strcpy (q._string, _string);
  911.     strcpy (q._string + _size, strg._string);
  912.     q._size = n-1;
  913.     return q;
  914. }
  915.  
  916. CL_String CL_String::operator+ (long v) const
  917. {
  918.     return *this + CL_String (v);
  919. }
  920.  
  921.         
  922. CL_String& CL_String::operator+= (const char* strg)
  923. {
  924.     if (!PrepareToChange())
  925.         return *this;
  926.     long len;
  927.     if (!strg || (len = strlen(strg)) == 0)
  928.         return *this;
  929.     long n = _size + len + 1;
  930.     if (_capacity >= n) {
  931.         strcpy (_string + _size, strg);
  932.         _size += strlen (strg);
  933.     }
  934.     else {
  935.         char* p = NEW_OP char [_capacity = n + DEFAULT_START_SIZE + 1];
  936.         if (!p) // No memory
  937.             return *this;
  938.         strcpy (p, _string);
  939.         strcpy (p + _size, strg);
  940.         delete [] _string;
  941.         _string = p;
  942.         _size = n-1;
  943.     }
  944.     Notify ();
  945.     return *this;
  946. }
  947.  
  948. CL_String& CL_String::operator+= (const CL_String& strg)
  949. {
  950.     if (!PrepareToChange())
  951.         return *this;
  952.     long n = _size + strg.Size() + 1;
  953.     if (_capacity >= n) {
  954.         strcpy (_string + _size, strg._string);
  955.         _size += strg._size;
  956.     }
  957.     else {
  958.         char* p = NEW_OP char [_capacity = n + DEFAULT_START_SIZE + 1];
  959.         if (!p)
  960.             return *this;
  961.         strcpy (p, _string);
  962.         strcpy (p + _size, strg);
  963.         delete [] _string;
  964.         _string = p;
  965.         _size = n-1;
  966.     }
  967.     Notify ();
  968.     return *this;
  969. }
  970.  
  971.     
  972.  
  973.  
  974.  
  975.  
  976. bool CL_String::AssignWithFormat (const char* fmt, ...)
  977. {
  978.     va_list args;
  979.     CL_ByteString msg (1024);
  980.  
  981.     va_start (args, fmt);
  982.     vsprintf ((char*) msg.AsPtr(), fmt,  args);
  983.     if (msg[1023] != '\0') {
  984.         CL_Error::Warning ("CL_String::AssignWithFormat: arg too large, "
  985.                            "possible memory corruption");
  986.         return FALSE;
  987.     }
  988.     *this = (const char*) msg.AsPtr(); // This does the notification
  989.     return TRUE;
  990. }
  991.  
  992.  
  993.  
  994. CL_String CL_String::operator- (const char* s) const
  995. {
  996.     long ndx = Index (s);
  997.     if (ndx >= 0)
  998.         return CL_String (_string, ndx);
  999.     else return *this;
  1000. }
  1001.  
  1002.  
  1003.  
  1004. bool CL_String::ReadFrom (const CL_Stream& s)
  1005. {
  1006.     if (!PrepareToChange() || !ReadClassId (s) )
  1007.         return FALSE;
  1008.     long len;
  1009.     if (!s.Read (len) || len <= 0)
  1010.         return FALSE;
  1011.     long cap = len + DEFAULT_START_SIZE;
  1012.     char* p = new char [cap];
  1013.     if (!p)
  1014.         return FALSE;
  1015.     if (s.Read ((uchar*) p, len) != len)
  1016.         return FALSE;
  1017.     _capacity = cap;
  1018.     _size = len - 1;
  1019.     if (_string)
  1020.         delete [] _string;
  1021.     _string = p;
  1022.     Notify ();
  1023.     return TRUE;
  1024. }
  1025.  
  1026. bool CL_String::WriteTo (CL_Stream& s) const
  1027. {
  1028.     return s.Write (ClassId())  &&
  1029.         s.Write (_size+1) && s.Write ((uchar*) _string, _size+1);
  1030. }
  1031.  
  1032.  
  1033. long CL_String::StorableFormWidth () const
  1034. {
  1035.     return sizeof (CL_ClassId) + (sizeof _size) + (_size + 1);
  1036. }
  1037.  
  1038.  
  1039.  
  1040.  
  1041.  
  1042. void CL_String::FromStream (istream& stream)
  1043. {
  1044.     if (!PrepareToChange ())
  1045.         return;
  1046.     register char fill_char = stream.fill ();
  1047.     CL_String tmp;
  1048.     char c;
  1049.     while (stream.get (c) && c == fill_char); // Skip fill characters
  1050.     while (c != fill_char && stream.good ()) {
  1051.         tmp.Append (c);
  1052.         stream.get (c);
  1053.     }
  1054.     if (!stream.eof())
  1055.         stream.putback (c);
  1056.     // Now a dirty trick:
  1057.     delete _string;
  1058.     _string = tmp._string;
  1059.     _capacity = tmp._capacity;
  1060.     _size = tmp._size;
  1061.     tmp._string = 0;
  1062.     Notify ();
  1063. }
  1064.  
  1065.  
  1066. istream& CL_String::ReadLine (istream& stream)
  1067. {
  1068.     if (!PrepareToChange ())
  1069.         return stream;
  1070.     CL_String tmp;
  1071.     char c;
  1072.     while (stream.get (c) && c != '\n') {
  1073.         tmp.Append (c);
  1074.     }
  1075.  
  1076.     // Now a dirty trick:
  1077.     delete _string;
  1078.     _string = tmp._string;
  1079.     _capacity = tmp._capacity;
  1080.     _size = tmp._size;
  1081.     tmp._string = 0;
  1082.     Notify ();
  1083.     return stream;
  1084. }
  1085.  
  1086.  
  1087.  
  1088. //
  1089. // Protected methods
  1090. //
  1091.  
  1092.  
  1093.  
  1094.  
  1095. bool CL_String::_Init (long size)
  1096. {
  1097.     _capacity = size;
  1098.     _string = NEW_OP char [size];
  1099.     if (!_string) {
  1100.         _capacity = _size = 0L;
  1101.         return FALSE;
  1102.     }
  1103.     _size = 0;
  1104.     _string[0] = '\0';
  1105.     return TRUE;
  1106. }
  1107.  
  1108.  
  1109.  
  1110. CL_String::CL_String (const char* s, long len)
  1111. {
  1112.     if (_Init (len + DEFAULT_START_SIZE)) {
  1113.         strncpy (_string, s, len);
  1114.         _string[len] = '\0';
  1115.         _size = len;
  1116.     }
  1117. }
  1118.  
  1119.  
  1120.  
  1121.  
  1122. // ------------------- CL_Substring methods ----------------------
  1123.  
  1124.  
  1125. typedef CL_Binding<CL_Substring> Bind;
  1126.  
  1127. CL_Substring::CL_Substring (CL_String& s, long pos, long length)
  1128. : CL_String (s), _client (s), _len (length)
  1129. {
  1130.     Bind b (this, &CL_Substring::_Modified);
  1131.     AddDependent (b, 1);
  1132.     _pos = maxl (0, minl (pos, s.Size()));
  1133.     if (pos >= 0 && pos < s.Size()) {
  1134.         _size = minl (length, s.Size() - pos);
  1135.         strncpy (_string, s._string + pos, _size);
  1136.         _string[_size] = '\0';
  1137.     }
  1138.     else { 
  1139.         _size = 0;
  1140.         _string[0] = '\0';
  1141.     }
  1142. }
  1143.  
  1144. CL_Substring::CL_Substring (const CL_Substring& s)
  1145. : CL_String (s), _client (s._client), _pos (s._pos), _len (s._len)
  1146. {
  1147.     Bind b (this, &CL_Substring::_Modified);
  1148.     AddDependent (b, 1);
  1149. }
  1150.  
  1151.  
  1152.     
  1153.  
  1154. // Modified: the method called to notify us when our value changes
  1155. bool CL_Substring::_Modified (CL_Object&, long)
  1156. {
  1157.     if (!_client.PrepareToChange())
  1158.         return FALSE;
  1159.     // Find the new size of the client
  1160.     long new_size = _client._size + _size - _len;
  1161.     if (new_size == _client._size) { // No change in client size
  1162.         strncpy (_client._string + _pos, _string, _len);
  1163.     }
  1164.     else if (new_size < _client._capacity) {
  1165.         // Enough room for modification
  1166.         memmove (_client._string + _pos + _size,
  1167.                  _client._string + _pos + _len,
  1168.                  _client._size - _pos - _len);
  1169.         memcpy (_client._string + _pos, _string, _size);
  1170.         _client._size += _size - _len;
  1171.     }
  1172.     else {
  1173.         // Not enough room: have to reallocate space
  1174.         long new_cap = new_size + 1 + DEFAULT_START_SIZE;
  1175.         char* p = new char [new_cap];
  1176.         if (!p)
  1177.             return FALSE; // No memory
  1178.         strncpy (p, _client._string, _pos);
  1179.         strncpy (p + _pos, _string, _size);
  1180.         strncpy (p + _pos + _size, _client._string + _pos + _len,
  1181.                  _client._size - _pos - _len);
  1182.         _client._size += _size - _len;
  1183.         _client._capacity = new_cap;
  1184.         delete [] _client._string;
  1185.         _client._string = p;
  1186.     }
  1187.     _client._string[_client._size] = '\0';
  1188.     _client.Notify();
  1189.     return TRUE;
  1190. }
  1191.  
  1192.  
  1193.  
  1194. CL_Substring::~CL_Substring ()
  1195. {
  1196. }
  1197.  
  1198.  
  1199.  
  1200. #if defined(__GNUC__) && __GNUC_MINOR__ >= 6
  1201. #include "base/cmparatr.h"
  1202. #include "base/basicops.h"
  1203.  
  1204. template class CL_Binding<CL_Substring>;
  1205. template class CL_Comparator<CL_String>;
  1206. template class CL_Basics<CL_String>;
  1207. #endif
  1208.  
  1209.