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