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