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