ViewVC Help
View File | Revision Log | Show Annotations | View Changeset | Root Listing
root/gclib/gclib/GStr.cpp
Revision: 109
Committed: Tue Oct 11 19:50:14 2011 UTC (8 years, 1 month ago) by gpertea
File size: 34536 byte(s)
Log Message:
added GStr::reverse(), GAlnExtend stringency tweaks

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