Doxygen
Loading...
Searching...
No Matches
dstring.cpp
Go to the documentation of this file.
1/******************************************************************************
2 *
3 * Copyright (C) 1997-2015 by Dimitri van Heesch.
4 *
5 * Permission to use, copy, modify, and distribute this software and its
6 * documentation under the terms of the GNU General Public License is hereby
7 * granted. No representations are made about the suitability of this software
8 * for any purpose. It is provided "as is" without express or implied warranty.
9 * See the GNU General Public License for more details.
10 *
11 * Documents produced by Doxygen are derivative works derived from the
12 * input used in their production; they are not affected by this license.
13 *
14 */
15
16#include <limits.h>
17#include <stdlib.h>
18#include <stdio.h>
19#include <stdarg.h>
20#include <ctype.h>
21
22#include "dstring.h"
23#include "regex.h"
24
25inline char toLowerChar(char c)
26{
27 return c>='A' && c<='Z' ? c|0x20 : c;
28}
29
30DString &DString::sprintf( const char *format, ... )
31{
32 va_list ap;
33 va_start( ap, format );
34 const size_t minlen=256;
35 size_t l = length();
36 if (l<minlen) { resize(minlen+1); l=minlen; }
37 int n=vsnprintf( rawData(), l+1, format, ap);
38 if (n<0) n=static_cast<int>(l);
39 resize(n);
40 va_end( ap );
41 return *this;
42}
43
44size_t DString::rfind_insensitive( char c, size_t index) const
45{
46 const char *b = data();
47 const char *pos = nullptr;
48 size_t len = length();
49 if (len==0) return -1; // empty string
50 if (index==npos) // start from end
51 {
52 index=len;
53 }
54 else if (index>len) // bad index
55 {
56 return DString::npos;
57 }
58 pos = b+index;
59 c = toLowerChar(c);
60 while ( pos>=b && toLowerChar(*pos)!=c) pos--;
61 return pos>=b ? static_cast<size_t>(pos-b) : DString::npos;
62}
63
64size_t DString::rfind_insensitive( const char *str, size_t index) const
65{
66 size_t slen = dstrlen(str);
67 size_t len = length();
68 if (slen>len) return DString::npos; // length of search string is longer than this string
69 if (index==DString::npos) index = len-slen; // start from end
70 else if (index>len) return DString::npos; // bad index
71 else if (index+slen>len) index = len-slen; // str would be too long
72 const char *pos = data()+index;
73 for (size_t i=index+1; i>0; )
74 {
75 --i;
76 if (dstrnicmp(pos--,str,slen)==0) return i;
77 }
78 return DString::npos;
79}
80
81int DString::contains( char c, bool cs ) const
82{
83 if (length()==0) return 0;
84 int count=0;
85 const char *pos = data();
86 if (cs)
87 {
88 while (*pos) if (*pos++ == c) count++;
89 }
90 else
91 {
92 c = toLowerChar(c);
93 while (*pos)
94 {
95 if (toLowerChar(*pos)==c) count++;
96 pos++;
97 }
98 }
99 return count;
100}
101
102int DString::contains( const char *str, bool cs ) const
103{
104 if (str==nullptr || length()==0) return 0;
105 int count=0;
106 const char *pos = data();
107 int len = dstrlen(str);
108 while (*pos)
109 {
110 if (cs)
111 {
112 if (dstrncmp(pos,str,len)==0) count++;
113 }
114 else
115 {
116 if (dstrnicmp(pos,str,len)==0) count++;
117 }
118 pos++;
119 }
120 return count;
121}
122
124{
125 if ( empty() ) // nothing to do
126 return *this;
127
128 DString result( length(), ExplicitSize );
129 const char *from = data();
130 char *to = result.rawData();
131 char *first = to;
132 while ( true )
133 {
134 while ( *from && disspace(*from) )
135 from++;
136 while ( *from && !disspace(*from) )
137 *to++ = *from++;
138 if ( *from )
139 *to++ = 0x20; // ' '
140 else
141 break;
142 }
143 if ( to > first && *(to-1) == 0x20 )
144 to--;
145 *to = '\0';
146 result.resize( static_cast<int>(to - result.data()) );
147 return result;
148}
149
150DString &DString::replace( size_t index, size_t len, const char *s)
151{
152 remove( index, len );
153 insert( index, s );
154 return *this;
155}
156
157static bool ok_in_base( char c, int base )
158{
159 if ( base <= 10 )
160 return c>='0' && c<='9' && (c-'0') < base;
161 else
162 return (c>='0' && c<='9') ||
163 (c >= 'a' && c < char('a'+base-10)) ||
164 (c >= 'A' && c < char('A'+base-10));
165}
166
167short DString::toShort(bool *ok, int base) const
168{
169 long v = toLong( ok, base );
170 if ( ok && *ok && (v < -32768 || v > 32767) ) {
171 *ok = false;
172 v = 0;
173 }
174 return static_cast<short>(v);
175}
176
177uint16_t DString::toUShort(bool *ok,int base) const
178{
179 unsigned long v = toULong( ok, base );
180 if ( ok && *ok && (v > 65535) ) {
181 *ok = false;
182 v = 0;
183 }
184 return static_cast<uint16_t>(v);
185}
186
187int DString::toInt(bool *ok, int base) const
188{
189 return static_cast<int>(toLong( ok, base ));
190}
191
192uint32_t DString::toUInt(bool *ok,int base) const
193{
194 return static_cast<uint32_t>(toULong( ok, base ));
195}
196
197
198long DString::toLong(bool *ok,int base) const
199{
200 const char *p = data();
201 long val=0;
202 int l = static_cast<int>(length());
203 const long max_mult = INT_MAX / base;
204 bool is_ok = false;
205 int neg = 0;
206 if ( !p )
207 goto bye;
208 while ( l && disspace(*p) ) // skip leading space
209 {
210 l--;
211 p++;
212 }
213 if ( l && *p == '-' )
214 {
215 l--;
216 p++;
217 neg = 1;
218 } else if ( *p == '+' )
219 {
220 l--;
221 p++;
222 }
223
224 // NOTE: toULong() code is similar
225 if ( !l || !ok_in_base(*p,base) )
226 {
227 goto bye;
228 }
229 while ( l && ok_in_base(*p,base) )
230 {
231 l--;
232 int dv = 0;
233 if ( *p>='0' && *p<='9' )
234 {
235 dv = *p-'0';
236 }
237 else
238 {
239 if ( *p >= 'a' && *p <= 'z' )
240 {
241 dv = *p - 'a' + 10;
242 }
243 else
244 {
245 dv = *p - 'A' + 10;
246 }
247 }
248 if ( val > max_mult || (val == max_mult && dv > (INT_MAX%base)+neg) )
249 {
250 goto bye;
251 }
252 val = base*val + dv;
253 p++;
254 }
255 if ( neg )
256 {
257 val = -val;
258 }
259 while ( l && disspace(*p) ) // skip trailing space
260 {
261 l--;
262 p++;
263 }
264 if ( !l )
265 {
266 is_ok = true;
267 }
268bye:
269 if ( ok )
270 {
271 *ok = is_ok;
272 }
273 return is_ok ? val : 0;
274}
275
276unsigned long DString::toULong(bool *ok,int base) const
277{
278 const char *p = data();
279 unsigned long val=0;
280 int l = static_cast<int>(length());
281 const unsigned long max_mult = 429496729; // UINT_MAX/10, rounded down
282 bool is_ok = false;
283 if ( !p )
284 {
285 goto bye;
286 }
287 while ( l && disspace(*p) ) // skip leading space
288 {
289 l--;
290 p++;
291 }
292 if ( *p == '+' )
293 {
294 l--;
295 p++;
296 }
297
298 // NOTE: toLong() code is similar
299 if ( !l || !ok_in_base(*p,base) )
300 {
301 goto bye;
302 }
303 while ( l && ok_in_base(*p,base) )
304 {
305 l--;
306 uint32_t dv = 0;
307 if ( *p>='0' && *p<='9' )
308 {
309 dv = *p-'0';
310 }
311 else
312 {
313 if ( *p >= 'a' && *p <= 'z' )
314 {
315 dv = *p - 'a' + 10;
316 }
317 else
318 {
319 dv = *p - 'A' + 10;
320 }
321 }
322 if ( val > max_mult || (val == max_mult && dv > (UINT_MAX%base)) )
323 {
324 goto bye;
325 }
326 val = base*val + dv;
327 p++;
328 }
329
330 while ( l && disspace(*p) ) // skip trailing space
331 {
332 l--;
333 p++;
334 }
335 if ( !l )
336 {
337 is_ok = true;
338 }
339bye:
340 if ( ok )
341 {
342 *ok = is_ok;
343 }
344 return is_ok ? val : 0;
345}
346
347uint64_t DString::toUInt64(bool *ok,int base) const
348{
349 const char *p = data();
350 uint64_t val=0;
351 int l = static_cast<int>(length());
352 const uint64_t max_mult = 1844674407370955161ULL; // ULLONG_MAX/10, rounded down
353 bool is_ok = false;
354 if ( !p )
355 {
356 goto bye;
357 }
358 while ( l && disspace(*p) ) // skip leading space
359 {
360 l--;
361 p++;
362 }
363 if ( *p == '+' )
364 {
365 l--;
366 p++;
367 }
368
369 // NOTE: toULong() code is similar
370 if ( !l || !ok_in_base(*p,base) )
371 {
372 goto bye;
373 }
374 while ( l && ok_in_base(*p,base) )
375 {
376 l--;
377 uint32_t dv = 0;
378 if ( *p>='0' && *p<='9' )
379 {
380 dv = *p-'0';
381 }
382 else
383 {
384 if ( *p >= 'a' && *p <= 'z' )
385 {
386 dv = *p - 'a' + 10;
387 }
388 else
389 {
390 dv = *p - 'A' + 10;
391 }
392 }
393 if ( val > max_mult || (val == max_mult && dv > (ULLONG_MAX%base)) )
394 {
395 goto bye;
396 }
397 val = base*val + dv;
398 p++;
399 }
400
401 while ( l && disspace(*p) ) // skip trailing space
402 {
403 l--;
404 p++;
405 }
406 if ( !l )
407 {
408 is_ok = true;
409 }
410bye:
411 if ( ok )
412 {
413 *ok = is_ok;
414 }
415 return is_ok ? val : 0;
416}
417
418//-------------------------------------------------
419
420char *dstrdup( const char *str )
421{
422 if ( !str ) return nullptr;
423 char *dst = new char[dstrlen(str)+1];
424 return strcpy( dst, str );
425}
426
427void dstrfree( const char *str )
428{
429 delete [](str);
430}
431
432char *dstrncpy( char *dst, const char *src, size_t len )
433{
434 if ( !src ) return nullptr;
435 strncpy( dst, src, len );
436 if ( len > 0 ) dst[len-1] = '\0';
437 return dst;
438}
439
440int dstricmp( const char *s1, const char *s2 )
441{
442 if ( !s1 || !s2 )
443 {
444 return s1 == s2 ? 0 : static_cast<int>(s2 - s1);
445 }
446 int res = 0;
447 char c = 0;
448 for ( ; !(res = ((c=toLowerChar(*s1)) - toLowerChar(*s2))); s1++, s2++ )
449 {
450 if ( !c ) // strings are equal
451 {
452 break;
453 }
454 }
455 return res;
456}
457
458int dstrnicmp( const char *s1, const char *s2, size_t len )
459{
460 if ( !s1 || !s2 )
461 {
462 return static_cast<int>(s2 - s1);
463 }
464 for ( ; len--; s1++, s2++ )
465 {
466 char c = toLowerChar(*s1);
467 int res = c-toLowerChar(*s2);
468 if ( res!=0 ) // strings are not equal
469 {
470 return res;
471 }
472 if ( c==0 ) // strings are equal
473 {
474 break;
475 }
476 }
477 return 0;
478}
479
480/// substitute all occurrences of \a src in \a s by \a dst
481DString substitute(const DString &s,const DString &src,const DString &dst)
482{
483 if (s.empty() || src.empty()) return s;
484 const char *q = nullptr, *p = nullptr;
485 size_t srcLen = src.length();
486 size_t dstLen = dst.length();
487 size_t resLen = 0;
488 if (srcLen!=dstLen)
489 {
490 int count = 0;
491 for (p = s.data(); (q=strstr(p,src.data()))!=nullptr; p=q+srcLen) count++;
492 resLen = s.length()+count*(dstLen-srcLen);
493 }
494 else // result has same size as s
495 {
496 resLen = s.length();
497 }
498 DString result(resLen, DString::ExplicitSize);
499 char *r = result.rawData();
500 for (p = s.data(); (q=strstr(p,src.data()))!=nullptr; p=q+srcLen)
501 {
502 int l = static_cast<int>(q-p);
503 memcpy(r,p,l);
504 r+=l;
505
506 if (dstLen>0) memcpy(r,dst.data(),dstLen);
507 r+=dstLen;
508 }
509 if (r)
510 {
511 dstrcpy(r,p);
512 }
513 //printf("substitute(%s,%s,%s)->%s\n",s,src,dst,result.data());
514 return result;
515}
516
517
518/// substitute all occurrences of \a src in \a s by \a dst, but skip
519/// each consecutive sequence of \a src where the number consecutive
520/// \a src matches \a skip_seq; if \a skip_seq is negative, skip any
521/// number of consecutive \a src
522DString substitute(const DString &s,const DString &src,const DString &dst,int skip_seq)
523{
524 if (s.empty() || src.empty()) return s;
525 const char *p = nullptr, *q = nullptr;
526 size_t srcLen = src.length();
527 size_t dstLen = dst.length();
528 size_t resLen = 0;
529 if (srcLen!=dstLen)
530 {
531 int count = 0;
532 for (p=s.data(); (q=strstr(p,src.data()))!=nullptr; p=q+srcLen) count++;
533 resLen = s.length()+count*(dstLen-srcLen);
534 }
535 else // result has same size as s
536 {
537 resLen = s.length();
538 }
539 DString result(resLen, DString::ExplicitSize);
540 char *r = result.rawData();
541 for (p = s.data(); (q=strstr(p,src.data()))!=nullptr; p=q+srcLen)
542 {
543 // search a consecutive sequence of src
544 int seq = 0, skip = 0;
545 if (skip_seq)
546 {
547 for (const char *n=q+srcLen; dstrncmp(n,src.data(),srcLen)==0; seq=1+skip, n+=srcLen)
548 ++skip; // number of consecutive src after the current one
549
550 // verify the allowed number of consecutive src to skip
551 if (skip_seq > 0 && skip_seq != seq)
552 seq = skip = 0;
553 }
554
555 // skip a consecutive sequence of src when necessary
556 int l = static_cast<int>((q + seq * srcLen)-p);
557 memcpy(r,p,l);
558 r+=l;
559
560 if (skip)
561 {
562 // skip only the consecutive src found after the current one
563 q += skip * srcLen;
564 // the next loop will skip the current src, aka (p=q+srcLen)
565 continue;
566 }
567
568 if (dstLen>0) memcpy(r,dst.data(),dstLen);
569 r+=dstLen;
570 }
571 dstrcpy(r,p);
572 result.resize(strlen(result.data()));
573 //printf("substitute(%s,%s,%s)->%s\n",s,src,dst,result.data());
574 return result;
575}
576
578{
579 if (empty()) return DString();
580 const std::string &s = m_rep;
581 int end=static_cast<int>(s.length());
582 int start=0,p=0;
583 // skip leading empty lines
584 for (;;)
585 {
586 int c;
587 while ((c=s[p]) && (c==' ' || c=='\t')) p++;
588 if (s[p]=='\n')
589 {
590 start=++p;
591 }
592 else
593 {
594 break;
595 }
596 }
597 // skip trailing empty lines
598 p=end-1;
599 if (p>=start && s.at(p)=='\n') p--;
600 while (p>=start)
601 {
602 int c;
603 while ((c=s[p]) && (c==' ' || c=='\t')) p--;
604 if (s[p]=='\n')
605 {
606 end=p+1;
607 }
608 else
609 {
610 break;
611 }
612 p--;
613 }
614 //printf("stripLeadingAndTrailingEmptyLines(%d-%d)\n",start,end);
615 return s.substr(start,end-start);
616}
617
619{
620 DString result;
621 int residual = n;
622
623 char modVal[2];
624 modVal[1] = 0;
625 while (residual > 0)
626 {
627 modVal[0] = (upper ? 'A': 'a') + (residual-1)%26;
628 result = modVal + result;
629 residual = (residual-1) / 26;
630 }
631 return result;
632}
633
635{
636 static const char *str_romans_upper[] = { "M", "CM", "D", "CD", "C", "XC", "L", "XL", "X", "IX", "V", "IV", "I" };
637 static const char *str_romans_lower[] = { "m", "cm", "d", "cd", "c", "xc", "l", "xl", "x", "ix", "v", "iv", "i" };
638 static const int values[] = { 1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1 };
639 static const char **str_romans = upper ? str_romans_upper : str_romans_lower;
640
641 DString result;
642 int residual = n;
643
644 for (int i = 0; i < 13; ++i)
645 {
646 while (residual - values[i] >= 0)
647 {
648 result += str_romans[i];
649 residual -= values[i];
650 }
651 }
652
653 return result;
654}
655
656bool DString::findAndRemoveWord(const char *word)
657{
658 static reg::Ex re(R"(\s*(<\a+>)\s*)");
659 std::string s = m_rep;
660 reg::Iterator it(s,re);
662 std::string result;
663 bool found=false;
664 size_t p=0;
665 for ( ; it!=end ; ++it)
666 {
667 const auto &match = *it;
668 std::string part = match[1].str();
669 if (part!=word)
670 {
671 size_t i = match.position();
672 size_t l = match.length();
673 result+=s.substr(p,i-p);
674 result+=match.str();
675 p=i+l;
676 }
677 else
678 {
679 found=true;
680 size_t i = match[1].position();
681 size_t l = match[1].length();
682 result+=s.substr(p,i-p);
683 p=i+l;
684 }
685 }
686 result+=s.substr(p);
687 m_rep = DString(result).simplifyWhiteSpace().str();
688 return found;
689}
690
691bool DString::containsWord(const char *word) const
692{
693 if (m_rep.empty() || word==nullptr) return false;
694 static const reg::Ex re(R"(\a+)");
695 for (reg::Iterator it(m_rep,re) ; it!=reg::Iterator() ; ++it)
696 {
697 if (it->str()==word) return true;
698 }
699 return false;
700}
701
702
A String class for use with Doxygen wrapping std::string and adding some additional functionality off...
Definition dstring.h:88
static DString integerToAlpha(int n, bool upper=true)
Definition dstring.cpp:618
void resize(size_t newlen)
Definition dstring.h:213
DString stripLeadingAndTrailingEmptyLines() const
Definition dstring.cpp:577
DString()=default
unsigned long toULong(bool *ok=nullptr, int base=10) const
Definition dstring.cpp:276
DString upper() const
Definition dstring.h:335
uint16_t toUShort(bool *ok=nullptr, int base=10) const
Definition dstring.cpp:177
DString simplifyWhiteSpace() const
return a copy of this string with leading and trailing whitespace removed and multiple internal white...
Definition dstring.cpp:123
bool empty() const
Returns true iff the string is empty (std::string compatible alias for isEmpty()).
Definition dstring.h:152
iterator end()
Definition dstring.h:187
DString & replace(size_t index, size_t len, const char *s)
Definition dstring.cpp:150
uint32_t toUInt(bool *ok=nullptr, int base=10) const
Definition dstring.cpp:192
bool containsWord(const char *word) const
returns true if this string contains word as a whole word, false otherwise.
Definition dstring.cpp:691
static DString integerToRoman(int n, bool upper=true)
Definition dstring.cpp:634
uint64_t toUInt64(bool *ok=nullptr, int base=10) const
Definition dstring.cpp:347
char * rawData()
Returns a writable pointer to the data.
Definition dstring.h:170
short toShort(bool *ok=nullptr, int base=10) const
Definition dstring.cpp:167
DString & remove(size_t index, size_t len)
Definition dstring.h:539
static constexpr size_t npos
value used to indicate 'not found' or 'to the end of the string', matching std::string::npos
Definition dstring.h:182
bool findAndRemoveWord(const char *word)
removes occurrences of whole word from this string, while keeping internal spaces and reducing multip...
Definition dstring.cpp:656
size_t rfind_insensitive(char c, size_t pos=npos) const
Definition dstring.cpp:44
std::string m_rep
Definition dstring.h:715
int contains(char c, bool cs=true) const
Definition dstring.cpp:81
DString & sprintf(const char *format,...)
Definition dstring.cpp:30
int toInt(bool *ok=nullptr, int base=10) const
Definition dstring.cpp:187
@ ExplicitSize
Definition dstring.h:135
long toLong(bool *ok=nullptr, int base=10) const
Definition dstring.cpp:198
const std::string & str() const
Definition dstring.h:649
const char * data() const
Returns a pointer to the contents of the string in the form of a 0-terminated C string.
Definition dstring.h:161
DString & insert(size_t index, const DString &s)
Definition dstring.h:429
size_t length() const
Returns the length of the string, not counting the 0-terminator.
Definition dstring.h:155
Class representing a regular expression.
Definition regex.h:39
Class to iterate through matches.
Definition regex.h:239
void dstrfree(const char *str)
Frees the memory allocated using dstrdup().
Definition dstring.cpp:427
char * dstrdup(const char *str)
Definition dstring.cpp:420
static bool ok_in_base(char c, int base)
Definition dstring.cpp:157
char * dstrncpy(char *dst, const char *src, size_t len)
Definition dstring.cpp:432
char toLowerChar(char c)
Definition dstring.cpp:25
int dstrnicmp(const char *s1, const char *s2, size_t len)
Definition dstring.cpp:458
DString substitute(const DString &s, const DString &src, const DString &dst)
substitute all occurrences of src in s by dst
Definition dstring.cpp:481
int dstricmp(const char *s1, const char *s2)
Definition dstring.cpp:440
int dstrnicmp(const char *str1, const char *str2, size_t len)
Definition dstring.cpp:458
char * dstrcpy(char *dst, const char *src)
Definition dstring.h:46
int dstrncmp(const char *str1, const char *str2, size_t len)
Definition dstring.h:60
uint32_t dstrlen(const char *str)
Returns the length of string str, or 0 if a null pointer is passed.
Definition dstring.h:43
bool disspace(char c)
Definition dstring.h:66