Introduction of class SuperString
[libacl.git] / src / string.cpp
1 /**************************************************************************
2 **  (c) Copyright 1997, Andromeda Technology & Automation
3 ***************************************************************************
4 ** MODULE INFORMATION *
5 ***********************
6 **      FILE NAME      : string.cpp
7 **      SYSTEM NAME    : Andromeda X-Windows Encapsulation
8 **      VERSION NUMBER : $Revision: 1.5 $
9 **
10 **  DESCRIPTION      :  String class implementation.
11 **
12 **  EXPORTED OBJECTS : 
13 **  LOCAL    OBJECTS : 
14 **  MODULES  USED    :
15 ***************************************************************************
16 **  ADMINISTRATIVE INFORMATION *
17 ********************************
18 **      ORIGINAL AUTHOR : Arjen Baart - arjen@andromeda.nl
19 **      CREATION DATE   : Nov 17, 1997
20 **      LAST UPDATE     : Nov 30, 2003
21 **      MODIFICATIONS   : 
22 **************************************************************************/
23
24 /*****************************
25    $Log: string.cpp,v $
26    Revision 1.5  2007-05-04 13:55:18  arjen
27    Dynamically allocate more memory if the string buffer runs out of space when
28    reading a String object from an input stream.
29
30    Revision 1.4  2003/03/29 07:18:54  arjen
31    String constructor and assignment from char * are more robust fro NULL pointers.
32
33    Revision 1.3  2002/11/03 13:18:57  arjen
34    New functions - String::escape() and String::unescape()
35
36    Revision 1.2  2002/09/28 06:42:11  arjen
37    A few small bug fixes.
38
39    Revision 1.1  2002/07/25 08:01:27  arjen
40    First checkin, AXE release 0.2
41
42 *****************************/
43
44 static const char RCSID[] = "$Id: string.cpp,v 1.5 2007-05-04 13:55:18 arjen Exp $";
45
46 #include <stdio.h>
47 #include <ctype.h>
48 #include "String.h"
49
50  //  Constructors and destructors for the String class
51
52 String::String()       // Create an empty String
53 {
54    p = new srep;
55    p->s = new char[1];
56    p->s[0] = '\0';
57    p->n = 1;
58 }
59
60 String::String(char c)    //  Create a String from a char
61 {
62    p = new srep;
63    p->s = new char[2];
64    p->s[0] = c;
65    p->s[1] = '\0';
66    p->n = 1;
67 }
68
69 String::String(const char *s)    //  Create a String from a char *
70 {
71    p = new srep;
72
73    p->s = 0;
74    p->n = 1;
75
76    if (s != 0)
77    {
78       p->s = new char[strlen(s)+1];
79       strcpy(p->s, s);
80       p->n = 1;
81    }
82 }
83
84 String::String(const String& x)   // Create a String from another String
85 {
86    x.p->n++;
87    p = x.p;
88 }
89
90 String::String(const substring &x)
91 {
92    p = new srep;
93
94    p->s = new char[x.len+1];
95    strncpy(p->s, x.str->p->s+x.start, x.len);
96    p->s[x.len] = '\0';     // strncpy does not add the \0
97    p->n = 1;
98 }
99
100 String::~String()
101 {
102    if (--p->n == 0)
103    {
104       delete p->s;
105       delete p;
106    }
107 }
108
109 /*
110  *  Assignment operators must handle cleanup of their left-hand operand.
111  */
112
113 String& String::operator=(const char c)
114 {
115    if (p->n > 1)
116    {                //    Dsiconnect self
117       p->n--;
118       p = new srep;
119    }
120    else if (p->n == 1)
121       delete p->s;
122
123    p->s = new char[2];
124    p->s[0] = c;
125    p->s[1] = '\0';
126    p->n = 1;
127    return *this;
128 }
129
130 String& String::operator=(const char *s)
131 {
132    if (p->n > 1)
133    {                //    Disconnect self
134       p->n--;
135       p = new srep;
136    }
137    else if (p->n == 1)
138       delete p->s;
139
140    if (s != 0)
141    {
142       p->s = new char[strlen(s)+1];
143       strcpy(p->s, s);
144       p->n = 1;
145    }
146    return *this;
147 }
148
149 /*  Make sure that assignment of an object to itself works correctly:  */
150
151 String& String::operator=(const String& x)
152 {
153    x.p->n++;
154    if (--p->n == 0)
155    {
156       delete p->s;
157       delete p;
158    }
159
160    p = x.p;
161    return *this;
162 }
163
164 /*  Numerical conversion */
165
166 String::String(int i)
167 {
168    p = new srep;
169    p->s = new char[15];  //  A little longer than needed...
170    sprintf(p->s, "%d", i);
171    p->n = 1;
172 }
173
174 String::String(long i)
175 {
176    p = new srep;
177    p->s = new char[15];  //  A little longer than needed...
178    sprintf(p->s, "%ld", i);
179    p->n = 1;
180 }
181
182 String::String(unsigned long i)
183 {
184    p = new srep;
185    p->s = new char[15];  //  A little longer than needed...
186    sprintf(p->s, "%lu", i);
187    p->n = 1;
188 }
189
190 String::String(double d)
191 {
192    p = new srep;
193    p->s = new char[25];  //  A little longer than needed...
194    sprintf(p->s, "%.3f", d);
195    p->n = 1;
196 }
197
198 /*   String concatenation */
199
200 String& String::operator+=(const String& x)
201 {
202    char *s = new char[strlen(p->s) + strlen(x.p->s) + 1];
203
204    strcpy(s, p->s);
205    strcat(s, x.p->s);
206
207    if (p->n > 1)
208    {                //    Disconnect self
209       p->n--;
210       p = new srep;
211    }
212    else if (p->n == 1)
213       delete p->s;
214
215    p->s = s;
216    p->n = 1;
217    return *this;
218 }
219
220 String& String::operator+=(const char * str)
221 {
222    char *s = new char[strlen(p->s) + strlen(str) + 1];
223
224    strcpy(s, p->s);
225    strcat(s, str);
226
227    if (p->n > 1)
228    {                //    Disconnect self
229       p->n--;
230       p = new srep;
231    }
232    else if (p->n == 1)
233       delete p->s;
234
235    p->s = s;
236    p->n = 1;
237    return *this;
238 }
239
240 String operator+(const String& x, const String& y)
241 {
242    String  cat = x;
243
244    cat  += y;
245    return cat;
246 }
247
248 String operator+(const String& x, const char * y)
249 {
250    String  cat = x;
251
252    cat  += y;
253    return cat;
254 }
255
256 String operator+(const char * x, const String& y)
257 {
258    String  cat = x;
259
260    cat  += y;
261    return cat;
262 }
263
264 /*  Shift operators  */
265
266
267 String operator<<(const String &x, int n)
268 {
269    String s(x);
270
271    s <<= n;
272    return s;
273 }
274
275 String & String::operator<<=(int n)
276 {
277    //  Make sure we are the only one being shifted.
278
279    if (p->n > 1)
280    {                //    Disconnect self
281       char *s = new char[strlen(p->s) + 1];
282
283       strcpy(s, p->s);
284       p->n--;
285       p = new srep;
286       p->s = s;
287       p->n = 1;
288    }
289
290    //  Shift left means we really have to copy all characters.
291    
292    int len = strlen(p->s);
293    int i;
294
295    if (n >= len)
296    {
297       //  Shift more than we have: the String becomes empty.
298       p->s[0] = '\0';
299    }
300    else
301    {
302       for (i = 0; i <= len - n; i++)
303       {
304          p->s[i] = p->s[i+n];
305       }
306    }
307    
308    return *this;
309 }
310
311 String operator>>(const String &x, int n)
312 {
313    String s(x);
314
315    s >>= n;
316    return s;
317 }
318
319 String & String::operator>>=(int n)
320 {
321    //  Make sure we are the only one being shifted.
322
323    if (p->n > 1)
324    {                //    Disconnect self
325       char *s = new char[strlen(p->s) + 1];
326
327       strcpy(s, p->s);
328       p->n--;
329       p = new srep;
330       p->s = s;
331       p->n = 1;
332    }
333
334    // Shift right is simple: just put the '\0' n places back.
335
336    
337    int len = strlen(p->s);
338
339    if (n >= len)
340    {
341       //  Shift more than we have: the String becomes empty.
342       p->s[0] = '\0';
343    }
344    else
345    {
346       p->s[len - n] = '\0';
347    }
348
349    return *this;
350 }
351
352 /*  Substring selection and assignment */
353
354 substring String::operator()(int start, int len)
355 {
356    substring sub;
357    size_t    _start;
358
359    _start = start;   //  Proper type conversion
360
361    if (_start >= strlen(p->s) || _start + len >= strlen(p->s))
362    {
363       throw StringException("Substring Out of bounds: (" 
364                     + String(start) + ", " + String(len) + ")");
365    }
366    sub.str = this;
367    sub.start = start;
368    sub.len   = len;
369    return sub;
370 }
371
372 String& substring::operator=(const String &x)
373 {
374    char *s = new char[strlen(x.p->s) + ~*str - len + 1];
375
376    strncpy(s, str->p->s, start);
377    s[start] = '\0';
378    strcat(s, x.p->s);
379    strcat(s, str->p->s+start+len);
380
381    return *str = s;
382 }
383
384 /*   Input and output   */
385
386 std::ostream& operator<<(std::ostream& s, const String& x)
387 {
388    if (x.p->s)
389       s << x.p->s;
390    return s;
391 }
392
393 std::istream& operator>>(std::istream& s, String& x)
394 {
395    char  *buf;
396    int   i;
397
398    int   bufsize = 1024;
399
400    buf = new char[bufsize];
401
402    i = -1;
403    do
404    {
405       if (i >= bufsize)
406       {
407          //  Buffer is too small. Allocate some new space.
408
409          char *newbuf = new char[bufsize * 2];
410          memcpy(newbuf, buf, bufsize);
411          delete [] buf;
412          buf = newbuf;
413          bufsize *= 2;
414       }
415
416       i++;
417       s.get(buf[i]);
418    }
419    while (s && buf[i] != '\n');
420    if (buf[i] == '\n')
421    {
422       buf[i] = '\0';
423    }
424    x = buf;
425
426    delete [] buf;
427
428    return s;
429 }
430
431 /*
432  *   The subscript operator is provided for access to individual characters
433  */
434
435 // TODO: Assignment to an individual character does not decouple references (BUG)
436
437 char& String::operator[](size_t i)
438 {
439    if (i >= strlen(p->s))
440    {
441       throw StringException("Out of bounds: " + String((int)i));
442    }
443
444    return p->s[i];
445 }
446
447 String String::upper()
448 {
449    String  up;
450    int     i;
451
452    up.p->s = new char[strlen(p->s)+1];
453
454    for(i=0; p->s[i]; i++)
455    {
456       up.p->s[i] = toupper(p->s[i]);
457    }
458    up.p->s[i] = '\0';
459
460    return up;
461 }
462
463 String String::lower()
464 {
465    String  low;
466    int     i;
467
468    low.p->s = new char[strlen(p->s)+1];
469
470    for(i=0; p->s[i]; i++)
471    {
472       low.p->s[i] = tolower(p->s[i]);
473    }
474    low.p->s[i] = '\0';
475
476    return low;
477 }
478
479 String String::escape()
480 {
481    const int BUFSIZE = 500;
482
483    char   buffer[BUFSIZE];
484    String escaped = "";
485    int    i;              //  Index in buffer[]
486    int    j;              //  Index in *this
487
488    i = 0;
489    for (j = 0; p->s[j] != '\0'; j++)
490    {
491       if (i + 5 > BUFSIZE)
492       {
493          escaped += buffer;
494          i = 0;
495       }
496
497       switch (p->s[j])
498       {
499       case '\a':
500          buffer[i++] = '\\';
501          buffer[i++] = 'a';
502          break;
503
504       case '\b':
505          buffer[i++] = '\\';
506          buffer[i++] = 'b';
507          break;
508
509       case '\f':
510          buffer[i++] = '\\';
511          buffer[i++] = 'f';
512          break;
513
514       case '\n':
515          buffer[i++] = '\\';
516          buffer[i++] = 'n';
517          break;
518
519       case '\r':
520          buffer[i++] = '\\';
521          buffer[i++] = 'r';
522          break;
523
524       case '\t':
525          buffer[i++] = '\\';
526          buffer[i++] = 't';
527          break;
528
529       case '\v':
530          buffer[i++] = '\\';
531          buffer[i++] = 'v';
532          break;
533
534       case '\'':
535          buffer[i++] = '\\';
536          buffer[i++] = '\'';
537          break;
538
539       case '"':
540          buffer[i++] = '\\';
541          buffer[i++] = '"';
542          break;
543
544       case '\\':
545          buffer[i++] = '\\';
546          buffer[i++] = '\\';
547          break;
548
549       default:
550          if (p->s[j] > '\x20' && p->s[j] < '\x7F')
551          {
552             buffer[i++] = p->s[j];
553          }
554          else
555          {
556             short   nibble;
557
558             //  Turn into hexadecimal representation
559
560             buffer[i++] = '\\';
561             buffer[i++] = 'x';
562             nibble = (p->s[j] >> 4) & 0x0f;
563             buffer[i++] = nibble < 10 ? nibble + '0' : nibble - 10 + 'A';
564             nibble = p->s[j] & 0x0f;
565             buffer[i++] = nibble < 10 ? nibble + '0' : nibble - 10 + 'A';
566          }
567          break;
568       }
569    }
570
571    buffer[i] = '\0';
572    escaped += buffer;
573
574    return escaped;
575 }
576
577 String String::unescape()
578 {
579    String unescaped;
580    char   *s, *d;
581
582    unescaped.p->s = new char[strlen(p->s)+1];
583    s = p->s;
584    d = unescaped.p->s;
585
586    while (*s != '\0')
587    {
588       if (*s == '\\')
589       {
590          s++;
591
592          switch (*s)
593          {
594          case 'a':
595             *d = '\a';
596             break;
597
598          case 'b':
599             *d = '\b';
600             break;
601
602          case 'f':
603             *d = '\f';
604             break;
605
606          case 'n':
607             *d = '\n';
608             break;
609
610          case 'r':
611             *d = '\r';
612             break;
613
614          case 't':
615             *d = '\t';
616             break;
617
618          case 'v':
619             *d = '\v';
620             break;
621
622          case '\'':
623             *d = '\'';
624             break;
625
626          case '"':
627             *d = '"';
628             break;
629
630          case '\\':
631             *d = '\\';
632             break;
633
634          case '0':
635          case '1':
636          case '2':
637          case '3':
638          case '4':
639          case '5':
640          case '6':
641          case '7':
642             *d = 0;
643             while (*s >= '0' && *s <= '7')
644             {
645                *d *= 8;
646                *d += *s - '0';
647                s++;
648             }
649             s--;
650             break;
651
652          case 'x':
653             *d = 0;
654             s++;           //  Skip the initial 'x'
655             while (isxdigit(*s))
656             {
657                *d *= 16;
658                *d += *s > '9' ? toupper(*s) - 'A' + 10 : *s - '0';
659                s++;
660             }
661             s--;
662             break;
663
664          default:
665             *d = *s;
666             break;
667          }
668       }
669       else
670       {
671          *d = *s;
672       }
673       s++;
674       d++;
675    }
676
677    return unescaped;
678 }
679
680
681 // Find the first occurance of 'c'
682
683 int String::index(char c)
684 {
685    char *found;
686
687    found = strchr(p->s, c);
688    if (found)
689       return found - p->s;
690    else
691       return -1;
692 }
693
694 // Find the last occurance of 'c'
695
696 int String::rindex(char c)
697 {
698    char *found;
699
700    found = strrchr(p->s, c);
701    if (found)
702       return found - p->s;
703    else
704       return -1;
705 }
706
707 /* In: see if I am part of x, return -1 if not found */
708
709 int String::in(String & x)
710 {
711    char *match;
712
713    match = strstr(x.p->s, p->s);
714    if (match)
715       return match - x.p->s;
716    else
717       return -1;
718 }
719
720 SuperString String::split(const String &separator)
721 {
722    SuperString splitted;
723    char *sep, *part;
724
725    part = p->s;
726    sep = strstr(p->s, separator.p->s);
727
728    while (sep != NULL)
729    {
730       //  Create a new string from the part until the separator
731
732       int  len  = sep - part;
733       char *str = new char[len + 1];
734
735       strncpy(str, part, len);
736       str[len] = '\0';
737       splitted += String(str);
738
739       // Look for the next separator
740
741       sep += ~separator;
742       part = sep;
743       sep = strstr(sep, separator.p->s);
744    }
745
746    // Add the leftover after the last separator.
747
748    splitted += String(part);
749
750    return splitted;
751 }