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