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