1 //---------------------------------------------------------------------------
2 #include "GStr.h"
3 #include <stdio.h>
4 #include <string.h>
5 #include <ctype.h>
6 #include "GBase.h"
7 #include <stdarg.h>
8 #include <errno.h>
9 
10 //---------------------------------------------------------------------------
11 
12 GStr::Data GStr::null_data;
13 
14 //=========================================
15 
new_data(int length)16 GStr::Data * GStr::new_data(int length) {
17 //static method to return a new Data object (allocate length)
18 //content is undefined, but it's null terminated
19     if (length > 0) {
20         Data* data;
21         GMALLOC(data, sizeof(Data)+length);
22         data->ref_count = 0;
23         data->length = length;
24         data->chars[length] = '\0';
25         return data;
26         }
27     else
28         return &null_data;
29  }
30 
new_data(const char * str)31 GStr::Data* GStr::new_data(const char* str) {
32 //static method to return a new Data object (allocate length)
33 //as a copy of a given string
34  if (str==NULL) return &null_data;
35  int length=strlen(str);
36  if (length > 0) {
37         Data* data;
38         GMALLOC(data, sizeof(Data)+length);
39         strcpy(data->chars, str);
40         data->ref_count = 0;
41         data->length = length;
42         data->chars[length] = '\0';
43         return data;
44         }
45     else
46         return &null_data;
47  }
48 
replace_data(int len)49 void GStr::replace_data(int len) {
50 
51     if (len == my_data->length && my_data->ref_count <= 1)
52         return;
53 
54     if (my_data != &null_data && --my_data->ref_count == 0)
55         GFREE(my_data);
56 
57     if (len > 0) {
58         //my_data = (Data *) malloc(sizeof(Data) + len);
59         GMALLOC(my_data, sizeof(Data) + len);
60         my_data->ref_count = 1;
61         my_data->length = len;
62         my_data->chars[len] = '\0';
63     }
64     else
65         my_data = &null_data;
66 }
67 
replace_data(Data * data)68 void GStr::replace_data(Data *data) {
69     if (my_data != &null_data && --my_data->ref_count == 0)
70         GFREE(my_data);
71     if (data != &null_data)
72         data->ref_count++;
73     my_data = data;
74 }
75 
make_unique()76 void GStr::make_unique() {//make sure it's not a reference to other string
77     if (my_data->ref_count > 1) {
78         Data *data = new_data(length());
79         ::memcpy(data->chars, chars(), length());
80         my_data->ref_count--;
81         my_data = data;
82         my_data->ref_count++;
83     }
84 }
85 
operator ==(const char * s1,const GStr & s2)86 bool operator==(const char *s1, const GStr& s2){
87   if (s1==NULL) return s2.is_empty();
88   return (strcmp(s1, s2.chars()) == 0);
89   }
90 
operator <(const char * s1,const GStr & s2)91 bool operator<(const char *s1, const GStr& s2) {
92   if (s1==NULL) return !s2.is_empty();
93   return (strcmp(s1, s2.chars()) < 0);
94   }
95 
operator <=(const char * s1,const GStr & s2)96 bool operator<=(const char *s1, const GStr& s2){
97  if (s1==NULL) return true;
98  return (strcmp(s1, s2.chars()) <= 0);
99  }
100 
operator >(const char * s1,const GStr & s2)101 bool operator>(const char *s1, const GStr& s2) {
102   if (s1==NULL) return false;
103  return (strcmp(s1, s2.chars()) > 0);
104  }
105 
106 
GStr()107 GStr::GStr():my_data(&null_data) {
108  fTokenDelimiter=NULL;
109  fTokenizeMode=tkCharSet;
110  fLastTokenStart=0;
111  readbuf=NULL;
112  readbufsize=0;
113  }
114 
GStr(const GStr & s)115 GStr::GStr(const GStr& s): my_data(&null_data){
116  fTokenDelimiter=NULL;
117  fTokenizeMode=tkCharSet;
118  fLastTokenStart=0;
119  readbuf=NULL;
120  readbufsize=0;
121  replace_data(s.my_data);
122  }
123 
GStr(const char * s)124 GStr::GStr(const char *s): my_data(&null_data) {
125   fTokenDelimiter=NULL;
126   fTokenizeMode=tkCharSet;
127   fLastTokenStart=0;
128   readbuf=NULL;
129   readbufsize=0;
130   my_data=new_data(s);
131   my_data->ref_count = 1;
132  }
133 
GStr(const int i)134 GStr::GStr(const int i): my_data(&null_data) {
135  fTokenDelimiter=NULL;
136  fTokenizeMode=tkCharSet;
137  fLastTokenStart=0;
138  readbuf=NULL;
139  readbufsize=0;
140  char buf[20];
141  sprintf(buf,"%d",i);
142  const int len = ::strlen(buf);
143  replace_data(len);
144  ::memcpy(chrs(), buf, len);
145  }
146 
GStr(const double f)147 GStr::GStr(const double f): my_data(&null_data) {
148  fTokenDelimiter=NULL;
149  fTokenizeMode=tkCharSet;
150  fLastTokenStart=0;
151  readbuf=NULL;
152  readbufsize=0;
153  char buf[20];
154  sprintf(buf,"%f",f);
155  const int len = ::strlen(buf);
156  replace_data(len);
157  ::memcpy(chrs(), buf, len);
158  }
159 
GStr(char c,int n)160 GStr::GStr(char c, int n): my_data(&null_data) {
161   fTokenDelimiter=NULL;
162   fTokenizeMode=tkCharSet;
163   fLastTokenStart=0;
164   readbuf=NULL;
165   readbufsize=0;
166   replace_data(n); ::memset(chrs(), c, n);
167   }
168 
~GStr()169 GStr::~GStr() {
170   if (my_data != &null_data && --my_data->ref_count == 0)
171              GFREE(my_data);
172   GFREE(fTokenDelimiter);
173   GFREE(readbuf);
174   }
175 
operator [](int idx)176 char& GStr::operator[](int idx){
177 //returns reference to char (can be l-value)
178   if (idx < 0) idx += length();
179   if (idx < 0 || idx >= length()) invalid_index_error("operator[]");
180   make_unique();  //because the user will probably modify this char!
181   return chrs()[idx];
182   }
183 
operator [](int idx) const184 char GStr::operator[](int idx) const {
185 //returns char copy (cannot be l-value!)
186   if (idx < 0) idx += length();
187   if (idx < 0 || idx >= length()) invalid_index_error("operator[]");
188   return chars()[idx];
189   }
190 
operator =(const GStr & s)191 GStr& GStr::operator=(const GStr& s) {
192   make_unique(); //edit operation ahead
193   replace_data(s.my_data);
194   return *this;
195   }
196 
operator =(const char * s)197 GStr& GStr::operator=(const char *s) {
198   make_unique(); //edit operation ahead
199   if (s==NULL) {
200     replace_data(0);
201     return *this;
202     }
203   const int len = ::strlen(s); replace_data(len);
204   ::memcpy(chrs(), s, len);
205   return *this;
206   }
207 
operator =(const double f)208 GStr& GStr::operator=(const double f) {
209  make_unique(); //edit operation ahead
210  char buf[20];
211  sprintf(buf,"%f",f);
212  const int len = ::strlen(buf);
213  replace_data(len);
214  ::memcpy(chrs(), buf, len);
215  return *this;
216 }
217 
operator =(const int i)218 GStr& GStr::operator=(const int i) {
219  make_unique(); //edit operation ahead
220  char buf[20];
221  sprintf(buf,"%d",i);
222  const int len = ::strlen(buf);
223  replace_data(len);
224  ::memcpy(chrs(), buf, len);
225  return *this;
226 }
227 
operator ==(const GStr & s) const228 bool GStr::operator==(const GStr& s) const {
229   if (s.is_empty()) return is_empty();
230   return (length() == s.length()) &&
231     (memcmp(chars(), s.chars(), length()) == 0);
232   }
233 
operator ==(const char * s) const234 bool GStr::operator==(const char *s) const {
235  if (s==NULL) return is_empty();
236  return (strcmp(chars(), s) == 0);
237  }
238 
operator <(const GStr & s) const239 bool GStr::operator<(const GStr& s) const {
240  if (s.is_empty()) return false;
241  return (strcmp(chars(), s.chars()) < 0);
242  }
243 
operator <(const char * s) const244 bool GStr::operator<(const char *s) const {
245  if (s==NULL) return false;
246  return (strcmp(chars(), s) < 0);
247  }
248 
operator <=(const GStr & s) const249 bool GStr::operator<=(const GStr& s) const {
250  if (s.is_empty()) return is_empty();
251  return (strcmp(chars(), s.chars()) <= 0);
252  }
253 
operator <=(const char * s) const254 bool GStr::operator<=(const char *s) const {
255  if (s==NULL) return is_empty();
256  return (strcmp(chars(), s) <= 0);
257  }
258 
operator >(const GStr & s) const259 bool GStr::operator>(const GStr& s) const {
260  if (s.is_empty()) return !is_empty();
261  return (strcmp(chars(), s.chars()) > 0);
262  }
263 
operator >(const char * s) const264 bool GStr::operator>(const char *s) const {
265  if (s==NULL) return !is_empty();
266  return (strcmp(chars(), s) > 0);
267  }
268 
operator >=(const GStr & s) const269 bool GStr::operator>=(const GStr& s) const {
270  if (s.is_empty()) return true;
271  return (strcmp(chars(), s.chars()) >= 0);
272  }
273 
operator >=(const char * s) const274 bool GStr::operator>=(const char *s) const {
275  if (s==NULL) return true;
276  return (strcmp(chars(), s) >= 0);
277  }
278 
operator !=(const GStr & s) const279 bool GStr::operator!=(const GStr& s) const {
280   if (s.is_empty()) return !is_empty();
281   return (length() != s.length()) ||
282          (memcmp(chars(), s.chars(), length()) != 0);
283   }
284 
operator !=(const char * s) const285 bool GStr::operator!=(const char *s) const {
286  if (s==NULL) return !is_empty();
287  return (strcmp(chars(), s) != 0);
288  }
289 
append(char c)290 GStr& GStr::append(char c) {
291  char buf[5];
292  sprintf(buf,"%c",c);
293  return append(buf);
294  }
295 
append(int i)296 GStr& GStr::append(int i) {
297  char buf[20];
298  sprintf(buf,"%d",i);
299  return append(buf);
300  }
301 
append(uint i)302 GStr& GStr::append(uint i) {
303  char buf[20];
304  sprintf(buf,"%u",i);
305  return append(buf);
306  }
307 
append(long l)308 GStr& GStr::append(long l) {
309  char buf[20];
310  sprintf(buf,"%ld",l);
311  return append(buf);
312  }
313 
append(unsigned long l)314 GStr& GStr::append(unsigned long l) {
315  char buf[20];
316  sprintf(buf,"%lu", l);
317  return append(buf);
318  }
319 
append(double f)320 GStr& GStr::append(double f) {
321  char buf[30];
322  sprintf(buf,"%f",f);
323  return append(buf);
324  }
325 
is_empty() const326 bool GStr::is_empty() const {
327   //return my_data == &null_data;
328   return (length()==0);
329   }
330 
copy() const331 GStr GStr::copy() const {
332  GStr newstring(*this);
333  return newstring;
334  }
335 
clear()336 GStr& GStr::clear() {
337   make_unique(); //edit operation ahead
338   replace_data(0);
339   return *this;
340   }
341 
index(const GStr & s,int start_index) const342 int GStr::index(const GStr& s, int start_index) const {
343  return index(s.chars(), start_index);
344  }
345 
contains(const GStr & s) const346 bool GStr::contains(const GStr& s) const {
347  return (index(s, 0) >= 0);
348  }
349 
contains(const char * s) const350 bool GStr::contains(const char *s) const {
351  return (index(s, 0) >= 0);
352  }
353 
startsWith(const char * s) const354 bool GStr::startsWith(const char *s) const {
355  //return (index(s, 0) == 0);
356  return ::startsWith(this->chars(), s);
357  }
358 
startsWith(const GStr & s) const359 bool GStr::startsWith(const GStr& s) const {
360  //return (index(s, 0) == 0);
361  return ::startsWith(this->chars(), s.chars());
362  }
363 
endsWith(const char * s) const364 bool GStr::endsWith(const char *s) const {
365  //return (index(s, 0) == 0);
366  return ::endsWith(this->chars(), s);
367  }
368 
endsWith(const GStr & s) const369 bool GStr::endsWith(const GStr& s) const {
370  //return (index(s, 0) == 0);
371  return ::endsWith(this->chars(), s.chars());
372  }
373 
contains(char c) const374 bool GStr::contains(char c) const {
375  return (index(c, 0) >= 0);
376  }
377 
format(const char * fmt,...)378 GStr& GStr::format(const char *fmt,...) {
379 // Format as in sprintf
380   make_unique(); //edit operation ahead
381   char* buf;
382   GMALLOC(buf, strlen(fmt)+1024);
383   va_list arguments;
384   va_start(arguments,fmt);
385   //+1K buffer, should be enough for common expressions
386   int len=vsprintf(buf,fmt,arguments);
387   va_end(arguments);
388   replace_data(len); //this also adds the '\0' at the end!
389                      //and sets the right len
390   ::memcpy(chrs(), buf, len);
391   GFREE(buf);
392   return *this;
393   }
394 
appendfmt(const char * fmt,...)395 GStr& GStr::appendfmt(const char *fmt,...) {
396 // Format as in sprintf
397   make_unique(); //edit operation ahead
398   char* buf;
399   GMALLOC(buf, strlen(fmt)+1024);
400   va_list arguments;
401   va_start(arguments,fmt);
402   //+1K buffer, should be enough for common expressions
403   vsprintf(buf,fmt,arguments);
404   va_end(arguments);
405   append(buf);
406   GFREE(buf);
407   return *this;
408   }
409 
trim(char c)410 GStr& GStr::trim(char c) {
411  register int istart;
412  register int iend;
413  for (istart=0; istart<length() && chars()[istart]==c;istart++) ;
414  if (istart==length()) {
415        make_unique(); //edit operation ahead
416        replace_data(0); //string was entirely trimmed
417        return *this;
418        }
419  for (iend=length()-1; iend>istart && chars()[iend]==c;iend--) ;
420  int newlen=iend-istart+1;
421  if (newlen==length())  //nothing to trim
422            return *this;
423  make_unique(); //edit operation ahead
424  Data *data = new_data(newlen);
425  ::memcpy(data->chars, &chars()[istart], newlen);
426  replace_data(data);
427  return *this;
428  }
429 
trim(const char * c)430 GStr& GStr::trim(const char* c) {
431  register int istart;
432  register int iend;
433  for (istart=0; istart<length() && strchr(c, chars()[istart])!=NULL ;istart++) ;
434  if (istart==length()) {
435         replace_data(0); //string was entirely trimmed
436         return *this;
437         }
438  for (iend=length()-1; iend>istart && strchr(c, chars()[iend])!=NULL;iend--) ;
439  int newlen=iend-istart+1;
440  if (newlen==length())  //nothing to trim
441            return *this;
442  make_unique(); //edit operation ahead
443  Data *data = new_data(newlen);
444  ::memcpy(data->chars, &chars()[istart], newlen);
445  replace_data(data);
446  return *this;
447  }
448 
trimR(char c)449 GStr& GStr::trimR(char c) {
450  //only trim the right end
451  //register int istart;
452  register int iend;
453  for (iend=length()-1; iend>=0 && chars()[iend]==c;iend--) ;
454  if (iend==-1) {
455        replace_data(0); //string was entirely trimmed
456        return *this;
457        }
458  int newlen=iend+1;
459  if (newlen==length())  //nothing to trim
460            return *this;
461  make_unique(); //edit operation ahead
462 
463  Data *data = new_data(newlen);
464  ::memcpy(data->chars, chars(), newlen);
465  replace_data(data);
466  return *this;
467  }
468 
trimR(const char * c)469 GStr& GStr::trimR(const char* c) {
470  register int iend;
471  for (iend=length()-1; iend>=0 && strchr(c,chars()[iend])!=NULL;iend--) ;
472  if (iend==-1) {
473        replace_data(0); //string was entirely trimmed
474        return *this;
475        }
476  int newlen=iend+1;
477  if (newlen==length())  //nothing to trim
478            return *this;
479  make_unique(); //edit operation ahead
480  Data *data = new_data(newlen);
481  ::memcpy(data->chars, chars(), newlen);
482  replace_data(data);
483  return *this;
484  }
485 
486 
chomp(const char * cstr)487 GStr& GStr::chomp(const char* cstr) {
488  register int iend;
489  if (cstr==NULL || *cstr==0) return *this;
490  //check if this ends with cstr
491  int cend=strlen(cstr)-1;
492  iend=my_data->length-1;
493  while (iend>=0 && cend>=0) {
494   if (my_data->chars[iend]!=cstr[cend]) return *this;
495   iend--;
496   cend--;
497   }
498  if (iend==-1) {
499        replace_data(0); //string will be entirely trimmed
500        return *this;
501        }
502  int newlen=iend+1;
503  make_unique(); //edit operation ahead
504  Data *data = new_data(newlen);
505  ::memcpy(data->chars, chars(), newlen);
506  replace_data(data);
507  return *this;
508  }
509 
trimL(char c)510 GStr& GStr::trimL(char c) {
511  register int istart;
512  for (istart=0; istart<length() && chars()[istart]==c;istart++) ;
513  if (istart==length()) {
514        replace_data(0); //string was entirely trimmed
515        return *this;
516        }
517  int newlen=length()-istart;
518  if (newlen==length())  //nothing to trim
519            return *this;
520  make_unique(); //edit operation ahead
521  Data *data = new_data(newlen);
522  ::memcpy(data->chars, &chars()[istart], newlen);
523  replace_data(data);
524  return *this;
525  }
526 
trimL(const char * c)527 GStr& GStr::trimL(const char* c) {
528  register int istart;
529  for (istart=0; istart<length() && strchr(c,chars()[istart])!=NULL;istart++) ;
530  if (istart==length()) {
531        replace_data(0); //string was entirely trimmed
532        return *this;
533        }
534  int newlen=length()-istart;
535  if (newlen==length())  //nothing to trim
536            return *this;
537  make_unique(); //edit operation ahead
538 
539  Data *data = new_data(newlen);
540  ::memcpy(data->chars, &chars()[istart], newlen);
541  replace_data(data);
542  return *this;
543  }
544 
padR(int len,char c)545 GStr& GStr::padR(int len, char c) {
546  //actually means align right in len
547  if (length()>=len) return *this; //no room for padding
548  make_unique(); //edit operation ahead
549  Data *data = new_data(len);
550  ::memset(data->chars,c,len-length());
551  ::memcpy(&data->chars[len-length()], chars(), length());
552  replace_data(data);
553  return *this;
554  }
555 
padL(int len,char c)556 GStr& GStr::padL(int len, char c) { //align left the string
557  if (length()>=len) return *this; //no room for padding
558  make_unique(); //edit operation ahead
559  Data *data = new_data(len);
560  ::memcpy(data->chars, chars(), length());
561  ::memset(&data->chars[length()],c,len-length());
562  replace_data(data);
563  return *this;
564  }
565 
padC(int len,char c)566 GStr& GStr::padC(int len, char c) {
567  if (length()>=len) return *this; //no room for padding
568  make_unique(); //edit operation ahead
569  int istart=(len-length())/2;
570  Data *data = new_data(len);
571  if (istart>0)
572       ::memset(data->chars, c, istart);
573  ::memcpy(&data->chars[istart], chars(), length());
574  int iend=istart+length();
575  if (iend<len)
576       ::memset(&data->chars[iend],c,len-iend);
577  replace_data(data);
578  return *this;
579  }
580 
operator +(const char * s1,const GStr & s2)581 GStr operator+(const char *s1, const GStr& s2) {
582     const int s1_length = ::strlen(s1);
583 
584     if (s1_length == 0)
585         return s2;
586     else {
587         GStr newstring;
588         newstring.replace_data(s1_length + s2.length());
589         ::memcpy(newstring.chrs(), s1, s1_length);
590         ::memcpy(&(newstring.chrs())[s1_length], s2.chars(), s2.length());
591         return newstring;
592         }
593 }
594 
595 //=========================================
596 
operator +(const GStr & s) const597 GStr GStr::operator+(const GStr& s) const {
598     if (length() == 0)
599         return s;
600     else if (s.length() == 0)
601         return *this;
602     else {
603         GStr newstring;
604         newstring.replace_data(length() + s.length());
605         ::memcpy(newstring.chrs(), chars(), length());
606         ::memcpy(&(newstring.chrs())[length()], s.chars(), s.length());
607         return newstring;
608         }
609 }
610 
611 //=========================================
612 
operator +(const char * s) const613 GStr GStr::operator+(const char *s) const {
614 
615     const int s_length = ::strlen(s);
616 
617     if (s_length == 0)
618         return *this;
619     else {
620         GStr newstring;
621         newstring.replace_data(length() + s_length);
622         ::memcpy(newstring.chrs(), chars(), length());
623         ::memcpy(&(newstring.chrs())[length()], s, s_length);
624         return newstring;
625         }
626 }
627 
operator +(const int i) const628 GStr GStr::operator+(const int i) const {
629     char buf[20];
630     sprintf(buf, "%d", i);
631     const int s_length = ::strlen(buf);
632     GStr newstring;
633     newstring.replace_data(length() + s_length);
634     ::memcpy(newstring.chrs(), chars(), length());
635     ::memcpy(&(newstring.chrs())[length()], buf, s_length);
636     return newstring;
637 }
638 
operator +(const char c) const639 GStr GStr::operator+(const char c) const {
640     char buf[4];
641     sprintf(buf, "%c", c);
642     const int s_length = ::strlen(buf);
643     GStr newstring;
644     newstring.replace_data(length() + s_length);
645     ::memcpy(newstring.chrs(), chars(), length());
646     ::memcpy(&(newstring.chrs())[length()], buf, s_length);
647     return newstring;
648 }
649 
operator +(const double f) const650 GStr GStr::operator+(const double f) const {
651     char buf[30];
652     sprintf(buf, "%f", f);
653     const int s_length = ::strlen(buf);
654     GStr newstring;
655     newstring.replace_data(length() + s_length);
656     ::memcpy(newstring.chrs(), chars(), length());
657     ::memcpy(&(newstring.chrs())[length()], buf, s_length);
658     return newstring;
659 }
660 
661 
662 //=========================================
663 
is_space() const664 bool GStr::is_space() const {
665 
666     if (my_data == &null_data)
667         return false;
668 
669     for (register const char *p = chars(); *p; p++)
670         if (!isspace(*p))
671             return false;
672 
673     return true;
674 }
675 
676 //=========================================
677 
substr(int idx,int len) const678 GStr GStr::substr(int idx, int len) const {
679     // A negative idx specifies an idx from the right of the string.
680     if (idx < 0)
681         idx += length();
682 
683     // A length of -1 specifies the rest of the string.
684     if (len < 0  || len>length()-idx)
685         len = length() - idx;
686 
687     if (idx<0 || idx>=length() || len<0 )
688         invalid_args_error("substr()");
689 
690     GStr newstring;
691     newstring.replace_data(len);
692     ::memcpy(newstring.chrs(), &chars()[idx], len);
693     return newstring;
694 }
695 
reverse()696 GStr& GStr::reverse() {
697   make_unique();
698   int l=0;
699   int r=my_data->length-1;
700   char c;
701   while (l<r) {
702      c=my_data->chars[l];
703      my_data->chars[l]=my_data->chars[r];
704      my_data->chars[r]=c;
705      l++;r--;
706      }
707   return *this;
708 }
709 
710 
711 //transform: any character from 'from' is replaced with a coresponding
712 //char from 'to'
713 
tr(const char * rfrom,const char * rto)714 GStr&  GStr::tr(const char *rfrom, const char* rto) {
715  if (length() == 0 || rfrom==NULL || strlen(rfrom)==0)
716         return *this;
717  unsigned int l=strlen(rfrom);
718  if (rto!=NULL && strlen(rto)!=l)
719       invalid_args_error("tr()");
720  make_unique(); //edit operation ahead
721  Data *data = new_data(length());
722 
723  if (rto==NULL) { //deletion case
724    char* s = my_data->chars;
725    char* p;
726    char* dest = data->chars;
727    do {
728       if ((p=strpbrk(s,rfrom))!=NULL) {
729         memcpy(dest,s,p-s);
730         dest+=p-s;
731         s=p+1;
732         }
733        else {
734         strcpy(dest, s);
735         dest+=strlen(s);
736         }
737       } while (p!=NULL);
738    (*dest)='\0';
739    }
740   else { //char substitution case - easier!
741    const char* p;
742    for (int i=0; i<length(); i++) {
743     if ((p=strchr(rfrom, my_data->chars[i]))!=NULL)
744          my_data->chars[i]=rto[p-rfrom];
745     }
746    }
747  data->length=strlen(data->chars);
748  replace_data(data);
749  return *this;
750 }
751 
752 
753 // search and replace all the occurences of a string with another string
754 // or just remove the given string (if replacement is NULL)
replace(const char * rfrom,const char * rto)755 GStr&  GStr::replace(const char *rfrom, const char* rto) {
756  if (length() == 0 || rfrom==NULL || strlen(rfrom)==0)
757         return *this;
758  unsigned int l=strlen(rfrom);
759  unsigned int tl= (rto==NULL)?0:strlen(rto);
760  make_unique(); //edit operation ahead
761  char* p;
762  char* dest;
763  char* newdest=NULL;
764  char* s = my_data->chars;
765  if (tl!=l) { //reallocation
766    if (tl>l) {  //possible enlargement
767        GMALLOC(newdest, length()*(tl-l+1)+1);
768        }
769       else  {//delete or replace with a shorter string
770        GMALLOC(newdest, length() + 1);
771        }
772       dest=newdest;
773       if (tl==0) {//deletion
774            while ((p=strstr(s,rfrom))!=NULL) {
775                //rfrom found at position p
776                 memcpy(dest,s,p-s);
777                 dest+=p-s;
778                 s+=p-s+l; //s positioned in string after rfrom
779                 }
780            //no more occurences, copy the remaining string
781            strcpy(dest, s);
782           }
783         else { //replace with another string
784           while ((p=strstr(s,rfrom))!=NULL) {
785               memcpy(dest,s,p-s); //copy up rto the match
786               dest+=p-s;
787               memcpy(dest,rto,tl); //put the replacement string
788               dest+=tl;
789               s+=p-s+l;
790               }
791           //not found any more, copy rto end of string
792           strcpy(dest, s);
793           }
794        Data* data=new_data(newdest);
795        replace_data(data);
796        GFREE(newdest);
797        }
798   else { //inplace editing: no need rto reallocate
799     while ((p=strstr(s,rfrom))!=NULL) {
800         memcpy(p,rto,l);
801         s+=p-s+l;
802         }
803    }
804  return *this;
805 }
806 
807 
808 
cut(int idx,int len)809 GStr&  GStr::cut(int idx, int len) {
810 
811     if (len == 0)
812         return *this;
813     make_unique(); //edit operation ahead
814 
815     // A negative idx specifies an idx from the right of the string,
816     // so the left part will be cut out
817     if (idx < 0)
818         idx += length();
819 
820     // A length of -1 specifies the rest of the string.
821     if (len == -1)
822         len = length() - idx;
823 
824     if (idx<0 || idx>=length() || len<0 || len>length()-idx)
825         invalid_args_error("cut()");
826 
827     Data *data = new_data(length() - len);
828     if (idx > 0)
829         ::memcpy(data->chars, chars(), idx);
830     ::strcpy(&data->chars[idx], &chars()[idx+len]);
831     replace_data(data);
832 
833     return *this;
834 }
835 
836 //=========================================
837 
paste(const GStr & s,int idx,int len)838 GStr&  GStr::paste(const GStr& s, int idx, int len) {
839     // A negative idx specifies an idx from the right of the string.
840     if (idx < 0)
841         idx += length();
842     make_unique(); //edit operation ahead
843 
844     // A length of -1 specifies the rest of the string.
845     if (len == -1)
846         len = length() - idx;
847 
848     if (idx<0 || idx>=length() || len<0 || len>length()-idx)
849         invalid_args_error("replace()");
850 
851     if (len == s.length() && my_data->ref_count == 1)
852         ::memcpy(&chrs()[idx], s.chars(), len);
853     else {
854         Data *data = new_data(length() - len + s.length());
855         if (idx > 0)
856             ::memcpy(data->chars, chars(), idx);
857         if (s.length() > 0)
858             ::memcpy(&data->chars[idx], s.chars(), s.length());
859         ::strcpy(&data->chars[idx+s.length()], &chars()[idx+len]);
860         replace_data(data);
861     }
862 
863     return *this;
864 }
865 
866 //=========================================
867 
paste(const char * s,int idx,int len)868 GStr& GStr::paste(const char *s, int idx, int len) {
869 
870     // A negative idx specifies an idx from the right of the string.
871     make_unique(); //edit operation ahead
872     if (idx < 0)
873         idx += length();
874 
875     // A length of -1 specifies the rest of the string.
876     if (len == -1)
877         len = length() - idx;
878 
879     if (idx<0 || idx>=length() || len<0 || len>length()-idx)
880         invalid_args_error("replace()");
881 
882     const int s_length = ::strlen(s);
883 
884     if (len == s_length && my_data->ref_count == 1)
885         ::memcpy(&chrs()[idx], s, len);
886     else {
887         Data *data = new_data(length() - len + s_length);
888         if (idx > 0)
889             ::memcpy(data->chars, chars(), idx);
890         if (s_length > 0)
891             ::memcpy(&data->chars[idx], s, s_length);
892         ::strcpy(&data->chars[idx+s_length], &chars()[idx+len]);
893         replace_data(data);
894     }
895 
896     return *this;
897 }
898 
899 //=========================================
900 
insert(const GStr & s,int idx)901 GStr& GStr::insert(const GStr& s, int idx) {
902     make_unique(); //edit operation ahead
903 
904     // A negative idx specifies an idx from the right of the string.
905     if (idx < 0)
906         idx += length();
907 
908     if (idx < 0 || idx >= length())
909         invalid_index_error("insert()");
910 
911     if (s.length() > 0) {
912         Data *data = new_data(length() + s.length());
913         if (idx > 0)
914             ::memcpy(data->chars, chars(), idx);
915         ::memcpy(&data->chars[idx], s.chars(), s.length());
916         ::strcpy(&data->chars[idx+s.length()], &chars()[idx]);
917         replace_data(data);
918     }
919 
920     return *this;
921 }
922 
923 //=========================================
924 
insert(const char * s,int idx)925 GStr& GStr::insert(const char *s, int idx) {
926     // A negative idx specifies an idx from the right of the string.
927     make_unique(); //edit operation ahead
928     if (idx < 0)
929         idx += length();
930 
931     if (idx < 0 || idx >= length())
932         invalid_index_error("insert()");
933 
934     const int s_length = ::strlen(s);
935 
936     if (s_length > 0) {
937         Data *data = new_data(length() + s_length);
938         if (idx > 0)
939             ::memcpy(data->chars, chars(), idx);
940         ::memcpy(&data->chars[idx], s, s_length);
941         ::strcpy(&data->chars[idx+s_length], &chars()[idx]);
942         replace_data(data);
943     }
944 
945     return *this;
946 }
947 //=========================================
948 
append(const char * s)949 GStr& GStr::append(const char* s) {
950   make_unique(); //edit operation ahead
951   int len=::strlen(s);
952   int newlength=len+my_data->length;
953   if (newlength<=my_data->length) return *this;
954   if (my_data->length==0) {
955     replace_data(len);
956     ::memcpy(my_data->chars, s, len);
957     return *this;
958    }
959   //faster solution with realloc
960   GREALLOC(my_data, sizeof(Data)+newlength);
961   ::strcpy(&my_data->chars[my_data->length], s);
962   my_data->length=newlength;
963   my_data->chars[newlength]='\0';
964   return *this;
965 }
966 
append(const GStr & s)967 GStr& GStr::append(const GStr& s) {
968  return append((const char *)s);
969 }
970 
971 
upper()972 GStr& GStr::upper() {
973   make_unique(); //edit operation ahead
974   for (register char *p = chrs(); *p; p++)
975             *p = (char) toupper(*p);
976 
977     return *this;
978 }
979 
980 //=========================================
981 
lower()982 GStr& GStr::lower() {
983     make_unique();
984 
985     for (register char *p = chrs(); *p; p++)
986           *p = (char) tolower(*p);
987 
988     return *this;
989 }
990 
991 //=========================================
992 
index(const char * s,int start_index) const993 int GStr::index(const char *s, int start_index) const {
994     // A negative index specifies an index from the right of the string.
995     if (strlen(s)>(size_t)length()) return -1;
996     if (start_index < 0)
997         start_index += length();
998 
999     if (start_index < 0 || start_index >= length())
1000         invalid_index_error("index()");
1001     const char* idx = strstr(&chars()[start_index], s);
1002     if (!idx)
1003         return -1;
1004     else
1005         return idx - chars();
1006 }
1007 
1008 //=========================================
1009 
index(char c,int start_index) const1010 int GStr::index(char c, int start_index) const {
1011     // A negative index specifies an index from the right of the string.
1012     if (length()==0) return -1;
1013     if (start_index < 0)
1014         start_index += length();
1015 
1016     if (start_index < 0 || start_index >= length())
1017         invalid_index_error("index()");
1018 
1019 
1020     if (c == '\0')
1021         return -1;
1022     const char *idx=(char *) ::memchr(&chars()[start_index], c,
1023                                          length()-start_index);
1024     if (idx==NULL)
1025         return -1;
1026     else
1027         return idx - chars();
1028 }
1029 
rindex(char c,int end_index) const1030 int GStr::rindex(char c, int end_index) const {
1031     if (c == 0 || length()==0 || end_index>=length()) return -1;
1032     if (end_index<0) end_index=my_data->length-1;
1033     for (int i=end_index;i>=0;i--) {
1034       if (my_data->chars[i]==c) return i;
1035       }
1036     return -1;
1037 }
1038 
rindex(const char * str,int end_index) const1039 int GStr::rindex(const char* str, int end_index) const {
1040     if (str==NULL || *str == '\0' || length()==0 || end_index>=length())
1041         return -1;
1042     int slen=strlen(str);
1043     if (end_index<0) end_index=my_data->length-1;
1044     //end_index is the index of the right-side boundary
1045     //the scanning starts at the end
1046     if (end_index>=0 && end_index<slen-1) return -1;
1047     for (int i=end_index-slen+1;i>=0;i--) {
1048        if (memcmp((void*)(my_data->chars+i),(void*)str, slen)==0)
1049            return i;
1050        }
1051     return -1;
1052 }
1053 
split(const char * delim)1054 GStr GStr::split(const char* delim) {
1055            /* splits "this" in two parts, at the first (left)
1056                  encounter of delim:
1057                  1st would stay in "this",
1058                  2nd part will be returned
1059                  as a new string!
1060            */
1061  GStr result;
1062  int i=index(delim);
1063  if (i>=0){
1064       result=substr(i+strlen(delim));
1065       cut(i);
1066       return result;
1067       }
1068  return result;
1069 }
1070 
split(char c)1071 GStr GStr::split(char c) {
1072            /* splits "this" in two parts, at the first (left)
1073                  encounter of delim:
1074                  1st would stay in "this",
1075                  2nd part will be returned
1076                  as a new string!
1077            */
1078  GStr result;
1079  int i=index(c);
1080  if (i>=0){
1081       result=substr(i+1);
1082       cut(i);
1083       return result;
1084       }
1085  return result;
1086 }
1087 
splitr(const char * delim)1088 GStr GStr::splitr(const char* delim) {
1089  GStr result;
1090  int i=rindex(delim);
1091  if (i>=0){
1092       result=substr(i+strlen(delim));
1093       cut(i);
1094       return result;
1095       }
1096  return result;
1097 }
1098 
splitr(char c)1099 GStr GStr::splitr(char c) {
1100  GStr result;
1101  int i=rindex(c);
1102  if (i>=0){
1103       result=substr(i+1);
1104       cut(i);
1105       return result;
1106       }
1107  return result;
1108 }
1109 
1110 
startTokenize(const char * delimiter,enTokenizeMode tokenizemode)1111 void GStr::startTokenize(const char* delimiter, enTokenizeMode tokenizemode) {
1112  GFREE(fTokenDelimiter);
1113  if (delimiter) {
1114     GMALLOC(fTokenDelimiter,strlen(delimiter)+1);
1115     strcpy(fTokenDelimiter, delimiter);
1116     }
1117  fLastTokenStart=0;
1118  fTokenizeMode=tokenizemode;
1119 }
1120 
nextToken(GStr & token)1121 bool GStr::nextToken(GStr& token) {
1122  if (fTokenDelimiter==NULL) {
1123     GError("GStr:: no token delimiter; use StartTokenize first\n");
1124     }
1125  if (fLastTokenStart>=length()) {//no more
1126     GFREE(fTokenDelimiter);
1127     fLastTokenStart=0;
1128     return false;
1129     }
1130  int dlen=strlen(fTokenDelimiter);
1131  char* delpos=NULL; //delimiter position
1132  int tlen=0;
1133  if (fTokenizeMode==tkFullString) { //exact string as a delimiter
1134    delpos=(char*)strstr(chars()+fLastTokenStart,fTokenDelimiter);
1135    if (delpos==NULL) delpos=(char*)(chars()+length());
1136    //empty records may be returned
1137    if (chars()+fLastTokenStart == delpos) { //empty token
1138      fLastTokenStart=(delpos-chars())+dlen;
1139      token="";
1140      return true;
1141      }
1142     else {
1143      tlen=delpos-(chars()+fLastTokenStart);
1144      token.replace_data(tlen);
1145      ::memcpy(token.chrs(), &chars()[fLastTokenStart], tlen);
1146      fLastTokenStart=(delpos-chars())+dlen;
1147      return true;
1148      }
1149    }
1150   else { //tkCharSet - any character is a delimiter
1151    //empty records are never returned !
1152    if (fLastTokenStart==0) {//skip any starting delimiters
1153      delpos=(char*)chars();
1154      while (*delpos!='\0' && strchr(fTokenDelimiter, *delpos)!=NULL)
1155        delpos++;
1156      if (*delpos!='\0')
1157          fLastTokenStart = delpos-chars();
1158        else { //only delimiters here,no tokens
1159          GFREE(fTokenDelimiter);
1160          fLastTokenStart=0;
1161          return false;
1162          }
1163      }
1164    //now fLastTokenStart is on a non-delimiter char
1165    //GMessage("String at fLastTokenStart=%d is %s\n", fLastTokenStart, delpos);
1166    char* token_end=NULL;
1167    delpos=(char*)strpbrk(chars()+fLastTokenStart,fTokenDelimiter);
1168    if (delpos==NULL) delpos=(char*)(chars()+length());
1169    token_end=delpos-1;
1170    while (*delpos!='\0' && strchr(fTokenDelimiter, *delpos)!=NULL)
1171       delpos++; //skip any other delimiters in the set!
1172    //now we know that delpos is on the beginning of next token
1173    tlen=(token_end-chars())-fLastTokenStart+1;
1174    if (tlen==0) {
1175        GFREE(fTokenDelimiter);
1176        fLastTokenStart=0;
1177        return false;
1178        }
1179    token.replace_data(tlen);
1180    ::memcpy(token.chrs(), &chars()[fLastTokenStart], tlen);
1181    fLastTokenStart=delpos-chars();
1182    return true;
1183    }
1184  //return true;
1185 }
1186 
read(FILE * stream,const char * delimiter,size_t bufsize)1187 size_t GStr::read(FILE* stream, const char* delimiter, size_t bufsize) {
1188 //read up to (and including) the given delimiter string
1189  if (readbuf==NULL) {
1190     GMALLOC(readbuf, bufsize);
1191     readbufsize=bufsize;
1192     }
1193   else if (bufsize!=readbufsize) {
1194             GFREE(readbuf);
1195             if (bufsize>0) {
1196               GMALLOC(readbuf, bufsize);
1197               }
1198             readbufsize=bufsize;
1199             }
1200  if (bufsize==0) {
1201     replace_data(0);
1202     return 0; //clear the string and free the buffer
1203     }
1204  size_t numread;
1205  size_t acc_len=0; //accumulated length
1206  int seplen=strlen(delimiter);
1207  void* p=NULL;
1208  Data *data = new_data(0);
1209  do {
1210    numread=fread(readbuf, 1, bufsize, stream);
1211    if (numread) {
1212      p=Gmemscan(readbuf, bufsize, (void*) delimiter, seplen);
1213      if (p!=NULL) {//found the delimiter
1214            //position the stream after it
1215            int l = (char*)p-(char*)readbuf;
1216            fseek(stream, l+seplen-numread, SEEK_CUR);
1217            numread=l+seplen;
1218            }
1219         else {//not found, go back if not eof
1220            if (numread==bufsize) {
1221                fseek(stream, -seplen, SEEK_CUR); //check if this works!
1222                numread-=seplen;
1223                }
1224            }
1225       if (data==&null_data) {
1226         data=new_data(numread);
1227         ::memcpy(data->chars, readbuf, numread);
1228         acc_len+=numread;
1229         }
1230        else {
1231         GREALLOC(data, sizeof(Data)+acc_len+numread);
1232         memcpy(&data->chars[acc_len], readbuf, numread);
1233         acc_len+=numread;
1234         data->length=acc_len;
1235         data->chars[acc_len]='\0';
1236         }
1237       } //if something read
1238    } while (p==NULL && numread!=0);
1239   replace_data(data);
1240   return acc_len;
1241 }
1242 
1243 
asInt(int base)1244 int GStr::asInt(int base /*=10 */) {
1245  return strtol(text(), NULL, base);
1246 }
1247 
asInt(int & r,int base)1248 bool GStr::asInt(int& r, int base) {
1249  errno=0;
1250  char*endptr;
1251  long val=strtol(text(), &endptr, base);
1252  if (errno!=0) return false;
1253  if (endptr == text()) return false;
1254  /* If we got here, strtol() successfully parsed a number */
1255  r=val;
1256  return true;
1257 }
1258 
asReal()1259 double GStr::asReal() {
1260  return strtod(text(), NULL);
1261 }
1262 
asReal(double & r)1263 bool GStr::asReal(double& r) {
1264   errno=0;
1265   char* endptr;
1266   double val=strtod(text(), &endptr);
1267   if (errno!=0) return false;
1268   if (endptr == text()) return false; //no digits to parse
1269   r=val;
1270   return true;
1271 }
1272 
1273 
peelInt() const1274 int GStr::peelInt() const {
1275  if (is_empty()) return 0;
1276  char buf[24];
1277  bool started=false;
1278  int j=0;
1279  int i;
1280  for (i=0;i<length();i++) {
1281   if (started) {
1282     if (isdigit(my_data->chars[i])) j++; //set coord
1283                                else break; //finished
1284     }
1285    else
1286     if (isdigit(my_data->chars[i])) {
1287         j++; started=true;
1288         }
1289   }
1290  if (j>0) {
1291    strncpy(buf, &my_data->chars[i-j], j);
1292    buf[j]='\0';
1293    return strtol(buf, NULL, 10);
1294    }
1295  return 0;
1296 }
1297 
peelIntR() const1298 int GStr::peelIntR() const {
1299  if (is_empty()) return 0;
1300  char buf[24];
1301  bool started=false;
1302  int j=0;
1303  int i;
1304  for (i=length()-1;i>=0;i--) {
1305   if (started) {
1306     if (isdigit(my_data->chars[i])) j++; //set length
1307                                else break; //finished
1308     }
1309    else
1310     if (isdigit(my_data->chars[i])) {
1311       j++; started=true;
1312       }
1313   }
1314  if (j>0) {
1315    strncpy(buf, &my_data->chars[i+1], j);
1316    buf[j]='\0';
1317    return strtol(buf, NULL, 10);
1318    }
1319  return 0;
1320 }
1321 
to(char c)1322 GStr GStr::to(char c) { //return the first part up to first occurence of c
1323  int i=index(c);
1324  if (i>=0) return substr(0,i);
1325       else return (*this);
1326 }
1327                            //or whole string if c not found
from(char c)1328 GStr GStr::from(char c) { //same as to, but starting from the right side
1329  int i=rindex(c);
1330  if (i>=0) return substr(i+1);
1331       else return (*this);
1332 }
1333 
count(char c)1334 int GStr::count(char c){
1335  //return the number of occurences of char c within the string
1336  int result=0;
1337  for (int i=0;i<length();i++)
1338     if (my_data->chars[i]==c) result++;
1339  return result;
1340  }
1341 
1342 //=========================================
1343 
invalid_args_error(const char * fname)1344 void GStr::invalid_args_error(const char *fname) {
1345     GError("GStr:: %s  - invalid arguments\n", fname);
1346 }
1347 
1348 //****************************************************************************
1349 
invalid_index_error(const char * fname)1350 void GStr::invalid_index_error(const char *fname) {
1351     GError("GStr:: %s  - invalid index\n", fname);
1352 }
1353 //****************************************************************************
1354 
1355