ViewVC Help
View File | Revision Log | Show Annotations | View Changeset | Root Listing
root/gclib/gclib/GStr.cpp
Revision: 301
Committed: Fri Oct 26 12:44:32 2012 UTC (6 years, 11 months ago) by gpertea
File size: 34936 byte(s)
Log Message:
minor refactoring


Line File contents
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
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 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
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 fTokenizeMode=tkCharSet;
110 fLastTokenStart=0;
111 readbuf=NULL;
112 readbufsize=0;
113 }
114
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
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
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
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
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
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
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
184 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
191 GStr& GStr::operator=(const GStr& s) {
192 make_unique(); //edit operation ahead
193 replace_data(s.my_data);
194 return *this;
195 }
196
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
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
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
228 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
234 bool GStr::operator==(const char *s) const {
235 if (s==NULL) return is_empty();
236 return (strcmp(chars(), s) == 0);
237 }
238
239 bool GStr::operator<(const GStr& s) const {
240 if (s.is_empty()) return false;
241 return (strcmp(chars(), s.chars()) < 0);
242 }
243
244 bool GStr::operator<(const char *s) const {
245 if (s==NULL) return false;
246 return (strcmp(chars(), s) < 0);
247 }
248
249 bool GStr::operator<=(const GStr& s) const {
250 if (s.is_empty()) return is_empty();
251 return (strcmp(chars(), s.chars()) <= 0);
252 }
253
254 bool GStr::operator<=(const char *s) const {
255 if (s==NULL) return is_empty();
256 return (strcmp(chars(), s) <= 0);
257 }
258
259 bool GStr::operator>(const GStr& s) const {
260 if (s.is_empty()) return !is_empty();
261 return (strcmp(chars(), s.chars()) > 0);
262 }
263
264 bool GStr::operator>(const char *s) const {
265 if (s==NULL) return !is_empty();
266 return (strcmp(chars(), s) > 0);
267 }
268
269 bool GStr::operator>=(const GStr& s) const {
270 if (s.is_empty()) return true;
271 return (strcmp(chars(), s.chars()) >= 0);
272 }
273
274 bool GStr::operator>=(const char *s) const {
275 if (s==NULL) return true;
276 return (strcmp(chars(), s) >= 0);
277 }
278
279 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
285 bool GStr::operator!=(const char *s) const {
286 if (s==NULL) return !is_empty();
287 return (strcmp(chars(), s) != 0);
288 }
289
290 GStr& GStr::append(char c) {
291 char buf[5];
292 sprintf(buf,"%c",c);
293 return append(buf);
294 }
295
296 GStr& GStr::append(int i) {
297 char buf[20];
298 sprintf(buf,"%d",i);
299 return append(buf);
300 }
301
302 GStr& GStr::append(uint i) {
303 char buf[20];
304 sprintf(buf,"%u",i);
305 return append(buf);
306 }
307
308 GStr& GStr::append(long l) {
309 char buf[20];
310 sprintf(buf,"%ld",l);
311 return append(buf);
312 }
313
314 GStr& GStr::append(unsigned long l) {
315 char buf[20];
316 sprintf(buf,"%lu", l);
317 return append(buf);
318 }
319
320 GStr& GStr::append(double f) {
321 char buf[30];
322 sprintf(buf,"%f",f);
323 return append(buf);
324 }
325
326 bool GStr::is_empty() const {
327 //return my_data == &null_data;
328 return (length()==0);
329 }
330
331 GStr GStr::copy() const {
332 GStr newstring(*this);
333 return newstring;
334 }
335
336 GStr& GStr::clear() {
337 make_unique(); //edit operation ahead
338 replace_data(0);
339 return *this;
340 }
341
342 int GStr::index(const GStr& s, int start_index) const {
343 return index(s.chars(), start_index);
344 }
345
346 bool GStr::contains(const GStr& s) const {
347 return (index(s, 0) >= 0);
348 }
349
350 bool GStr::contains(const char *s) const {
351 return (index(s, 0) >= 0);
352 }
353
354 bool GStr::startsWith(const char *s) const {
355 //return (index(s, 0) == 0);
356 return ::startsWith(this->chars(), s);
357 }
358
359 bool GStr::startsWith(const GStr& s) const {
360 //return (index(s, 0) == 0);
361 return ::startsWith(this->chars(), s.chars());
362 }
363
364 bool GStr::endsWith(const char *s) const {
365 //return (index(s, 0) == 0);
366 return ::endsWith(this->chars(), s);
367 }
368
369 bool GStr::endsWith(const GStr& s) const {
370 //return (index(s, 0) == 0);
371 return ::endsWith(this->chars(), s.chars());
372 }
373
374 bool GStr::contains(char c) const {
375 return (index(c, 0) >= 0);
376 }
377
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
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
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
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
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
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
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
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
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
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
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
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
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
597 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
613 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
628 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
639 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
650 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
664 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
678 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
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
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)
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
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
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
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
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
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
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
967 GStr& GStr::append(const GStr& s) {
968 return append((const char *)s);
969 }
970
971
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
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
993 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
1010 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
1030 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
1039 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
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
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
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
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
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
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
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
1244 int GStr::asInt(int base /*=10 */) {
1245 return strtol(text(), NULL, base);
1246 }
1247
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
1259 double GStr::asReal() {
1260 return strtod(text(), NULL);
1261 }
1262
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
1274 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
1298 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
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
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
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
1344 void GStr::invalid_args_error(const char *fname) {
1345 GError("GStr:: %s - invalid arguments\n", fname);
1346 }
1347
1348 //****************************************************************************
1349
1350 void GStr::invalid_index_error(const char *fname) {
1351 GError("GStr:: %s - invalid index\n", fname);
1352 }
1353 //****************************************************************************