ucx/string.c

changeset 888
af685cc9d623
parent 854
1c8401ece69e
equal deleted inserted replaced
877:b60487c3ec36 888:af685cc9d623
23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 * POSSIBILITY OF SUCH DAMAGE. 26 * POSSIBILITY OF SUCH DAMAGE.
27 */ 27 */
28 #ifdef MEMRCHR_NEED_GNU
29 #define _GNU_SOURCE
30 #endif
31
28 #include "cx/string.h" 32 #include "cx/string.h"
29 33
30 #include <string.h> 34 #include <string.h>
31 #include <stdarg.h> 35 #include <stdarg.h>
32 #include <assert.h> 36 #include <assert.h>
33 #include <errno.h> 37 #include <errno.h>
34 #include <limits.h> 38 #include <limits.h>
35 #include <float.h> 39 #include <float.h>
40 #include <ctype.h>
36 41
37 #ifdef _WIN32 42 #ifdef _WIN32
38 #define cx_strcasecmp_impl _strnicmp 43 #define cx_strcasecmp_impl _strnicmp
39 #else 44 #else
40 #include <strings.h> 45 #include <strings.h>
41 #define cx_strcasecmp_impl strncasecmp 46 #define cx_strcasecmp_impl strncasecmp
42 #endif 47 #endif
43 48
44 cxmutstr cx_mutstr(char *cstring) { 49 cxmutstr cx_mutstr(char *cstring) {
45 return (cxmutstr) {cstring, strlen(cstring)}; 50 return (cxmutstr) {cstring, cstring == NULL ? 0 : strlen(cstring)};
46 } 51 }
47 52
48 cxmutstr cx_mutstrn( 53 cxmutstr cx_mutstrn(
49 char *cstring, 54 char *cstring,
50 size_t length 55 size_t length
51 ) { 56 ) {
52 return (cxmutstr) {cstring, length}; 57 return (cxmutstr) {cstring, length};
53 } 58 }
54 59
55 cxstring cx_str(const char *cstring) { 60 cxstring cx_str(const char *cstring) {
56 return (cxstring) {cstring, strlen(cstring)}; 61 return (cxstring) {cstring, cstring == NULL ? 0 : strlen(cstring)};
57 } 62 }
58 63
59 cxstring cx_strn( 64 cxstring cx_strn(
60 const char *cstring, 65 const char *cstring,
61 size_t length 66 size_t length
63 return (cxstring) {cstring, length}; 68 return (cxstring) {cstring, length};
64 } 69 }
65 70
66 void cx_strfree(cxmutstr *str) { 71 void cx_strfree(cxmutstr *str) {
67 if (str == NULL) return; 72 if (str == NULL) return;
68 free(str->ptr); 73 cxFreeDefault(str->ptr);
69 str->ptr = NULL; 74 str->ptr = NULL;
70 str->length = 0; 75 str->length = 0;
71 } 76 }
72 77
73 void cx_strfree_a( 78 void cx_strfree_a(
76 ) { 81 ) {
77 if (str == NULL) return; 82 if (str == NULL) return;
78 cxFree(alloc, str->ptr); 83 cxFree(alloc, str->ptr);
79 str->ptr = NULL; 84 str->ptr = NULL;
80 str->length = 0; 85 str->length = 0;
86 }
87
88 int cx_strcpy_a(
89 const CxAllocator *alloc,
90 cxmutstr *dest,
91 cxstring src
92 ) {
93 if (cxReallocate(alloc, &dest->ptr, src.length + 1)) {
94 return 1;
95 }
96
97 memcpy(dest->ptr, src.ptr, src.length);
98 dest->length = src.length;
99 dest->ptr[dest->length] = '\0';
100
101 return 0;
81 } 102 }
82 103
83 size_t cx_strlen( 104 size_t cx_strlen(
84 size_t count, 105 size_t count,
85 ... 106 ...
104 cxmutstr str, 125 cxmutstr str,
105 size_t count, 126 size_t count,
106 ... 127 ...
107 ) { 128 ) {
108 if (count == 0) return str; 129 if (count == 0) return str;
109
110 cxstring strings_stack[8];
111 cxstring *strings;
112 if (count > 8) {
113 strings = calloc(count, sizeof(cxstring));
114 if (strings == NULL) {
115 return (cxmutstr) {NULL, 0};
116 }
117 } else {
118 strings = strings_stack;
119 }
120
121 va_list ap; 130 va_list ap;
122 va_start(ap, count); 131 va_start(ap, count);
123 132 va_list ap2;
124 // get all args and overall length 133 va_copy(ap2, ap);
134
135 // compute overall length
125 bool overflow = false; 136 bool overflow = false;
126 size_t slen = str.length; 137 size_t slen = str.length;
127 for (size_t i = 0; i < count; i++) { 138 for (size_t i = 0; i < count; i++) {
128 cxstring s = va_arg (ap, cxstring); 139 cxstring s = va_arg(ap, cxstring);
129 strings[i] = s;
130 if (slen > SIZE_MAX - str.length) overflow = true; 140 if (slen > SIZE_MAX - str.length) overflow = true;
131 slen += s.length; 141 slen += s.length;
132 } 142 }
133 va_end(ap); 143 va_end(ap);
134 144
135 // abort in case of overflow 145 // abort in case of overflow
136 if (overflow) { 146 if (overflow) {
147 va_end(ap2);
137 errno = EOVERFLOW; 148 errno = EOVERFLOW;
138 if (strings != strings_stack) {
139 free(strings);
140 }
141 return (cxmutstr) { NULL, 0 }; 149 return (cxmutstr) { NULL, 0 };
142 } 150 }
143 151
144 // reallocate or create new string 152 // reallocate or create new string
145 char *newstr; 153 char *newstr;
147 newstr = cxMalloc(alloc, slen + 1); 155 newstr = cxMalloc(alloc, slen + 1);
148 } else { 156 } else {
149 newstr = cxRealloc(alloc, str.ptr, slen + 1); 157 newstr = cxRealloc(alloc, str.ptr, slen + 1);
150 } 158 }
151 if (newstr == NULL) { 159 if (newstr == NULL) {
152 if (strings != strings_stack) { 160 va_end(ap2);
153 free(strings);
154 }
155 return (cxmutstr) {NULL, 0}; 161 return (cxmutstr) {NULL, 0};
156 } 162 }
157 str.ptr = newstr; 163 str.ptr = newstr;
158 164
159 // concatenate strings 165 // concatenate strings
160 size_t pos = str.length; 166 size_t pos = str.length;
161 str.length = slen; 167 str.length = slen;
162 for (size_t i = 0; i < count; i++) { 168 for (size_t i = 0; i < count; i++) {
163 cxstring s = strings[i]; 169 cxstring s = va_arg(ap2, cxstring);
164 memcpy(str.ptr + pos, s.ptr, s.length); 170 memcpy(str.ptr + pos, s.ptr, s.length);
165 pos += s.length; 171 pos += s.length;
166 } 172 }
173 va_end(ap2);
167 174
168 // terminate string 175 // terminate string
169 str.ptr[str.length] = '\0'; 176 str.ptr[str.length] = '\0';
170
171 // free temporary array
172 if (strings != strings_stack) {
173 free(strings);
174 }
175 177
176 return str; 178 return str;
177 } 179 }
178 180
179 cxstring cx_strsubs( 181 cxstring cx_strsubs(
232 cxstring result = cx_strchr(cx_strcast(string), chr); 234 cxstring result = cx_strchr(cx_strcast(string), chr);
233 return (cxmutstr) {(char *) result.ptr, result.length}; 235 return (cxmutstr) {(char *) result.ptr, result.length};
234 } 236 }
235 237
236 cxstring cx_strrchr( 238 cxstring cx_strrchr(
237 cxstring string, 239 cxstring string,
238 int chr 240 int chr
239 ) { 241 ) {
242 #ifdef WITH_MEMRCHR
243 char *ret = memrchr(string.ptr, 0xFF & chr, string.length);
244 if (ret == NULL) return (cxstring) {NULL, 0};
245 return (cxstring) {ret, string.length - (ret - string.ptr)};
246 #else
240 chr = 0xFF & chr; 247 chr = 0xFF & chr;
241 size_t i = string.length; 248 size_t i = string.length;
242 while (i > 0) { 249 while (i > 0) {
243 i--; 250 i--;
244 // TODO: improve by comparing multiple bytes at once
245 if (string.ptr[i] == chr) { 251 if (string.ptr[i] == chr) {
246 return cx_strsubs(string, i); 252 return cx_strsubs(string, i);
247 } 253 }
248 } 254 }
249 return (cxstring) {NULL, 0}; 255 return (cxstring) {NULL, 0};
256 #endif
250 } 257 }
251 258
252 cxmutstr cx_strrchr_m( 259 cxmutstr cx_strrchr_m(
253 cxmutstr string, 260 cxmutstr string,
254 int chr 261 int chr
287 size_t s_prefix_table[CX_STRSTR_SBO_SIZE]; 294 size_t s_prefix_table[CX_STRSTR_SBO_SIZE];
288 295
289 // check needle length and use appropriate prefix table 296 // check needle length and use appropriate prefix table
290 // if the pattern exceeds static prefix table, allocate on the heap 297 // if the pattern exceeds static prefix table, allocate on the heap
291 const bool useheap = needle.length >= CX_STRSTR_SBO_SIZE; 298 const bool useheap = needle.length >= CX_STRSTR_SBO_SIZE;
292 register size_t *ptable = useheap ? calloc(needle.length + 1, 299 register size_t *ptable = useheap
293 sizeof(size_t)) : s_prefix_table; 300 ? cxCallocDefault(needle.length + 1, sizeof(size_t))
301 : s_prefix_table;
294 302
295 // keep counter in registers 303 // keep counter in registers
296 register size_t i, j; 304 register size_t i, j;
297 305
298 // fill prefix table 306 // fill prefix table
326 } 334 }
327 } 335 }
328 336
329 // if prefix table was allocated on the heap, free it 337 // if prefix table was allocated on the heap, free it
330 if (useheap) { 338 if (useheap) {
331 free(ptable); 339 cxFreeDefault(ptable);
332 } 340 }
333 341
334 return result; 342 return result;
335 } 343 }
336 344
451 ) { 459 ) {
452 return cx_strsplit_a(allocator, cx_strcast(string), 460 return cx_strsplit_a(allocator, cx_strcast(string),
453 delim, limit, (cxstring **) output); 461 delim, limit, (cxstring **) output);
454 } 462 }
455 463
456 int cx_strcmp( 464 int cx_strcmp_(
457 cxstring s1, 465 cxstring s1,
458 cxstring s2 466 cxstring s2
459 ) { 467 ) {
460 if (s1.length == s2.length) { 468 if (s1.length == s2.length) {
461 return strncmp(s1.ptr, s2.ptr, s1.length); 469 return strncmp(s1.ptr, s2.ptr, s1.length);
468 if (r != 0) return r; 476 if (r != 0) return r;
469 return -1; 477 return -1;
470 } 478 }
471 } 479 }
472 480
473 int cx_strcasecmp( 481 int cx_strcasecmp_(
474 cxstring s1, 482 cxstring s1,
475 cxstring s2 483 cxstring s2
476 ) { 484 ) {
477 if (s1.length == s2.length) { 485 if (s1.length == s2.length) {
478 return cx_strcasecmp_impl(s1.ptr, s2.ptr, s1.length); 486 return cx_strcasecmp_impl(s1.ptr, s2.ptr, s1.length);
520 memcpy(result.ptr, string.ptr, string.length); 528 memcpy(result.ptr, string.ptr, string.length);
521 result.ptr[string.length] = '\0'; 529 result.ptr[string.length] = '\0';
522 return result; 530 return result;
523 } 531 }
524 532
525 static bool str_isspace(char c) {
526 // TODO: remove once UCX has public API for this
527 return c == ' ' || c == '\t' || c == '\r' || c == '\n' || c == '\v' || c == '\f';
528 }
529
530 cxstring cx_strtrim(cxstring string) { 533 cxstring cx_strtrim(cxstring string) {
531 cxstring result = string; 534 cxstring result = string;
532 // TODO: optimize by comparing multiple bytes at once 535 while (result.length > 0 && isspace((unsigned char)(result.ptr[0]))) {
533 while (result.length > 0 && str_isspace(*result.ptr)) {
534 result.ptr++; 536 result.ptr++;
535 result.length--; 537 result.length--;
536 } 538 }
537 while (result.length > 0 && str_isspace(result.ptr[result.length - 1])) { 539 while (result.length > 0 && isspace((unsigned char)result.ptr[result.length - 1])) {
538 result.length--; 540 result.length--;
539 } 541 }
540 return result; 542 return result;
541 } 543 }
542 544
543 cxmutstr cx_strtrim_m(cxmutstr string) { 545 cxmutstr cx_strtrim_m(cxmutstr string) {
544 cxstring result = cx_strtrim(cx_strcast(string)); 546 cxstring result = cx_strtrim(cx_strcast(string));
545 return (cxmutstr) {(char *) result.ptr, result.length}; 547 return (cxmutstr) {(char *) result.ptr, result.length};
546 } 548 }
547 549
548 bool cx_strprefix( 550 bool cx_strprefix_(
549 cxstring string, 551 cxstring string,
550 cxstring prefix 552 cxstring prefix
551 ) { 553 ) {
552 if (string.length < prefix.length) return false; 554 if (string.length < prefix.length) return false;
553 return memcmp(string.ptr, prefix.ptr, prefix.length) == 0; 555 return memcmp(string.ptr, prefix.ptr, prefix.length) == 0;
554 } 556 }
555 557
556 bool cx_strsuffix( 558 bool cx_strsuffix_(
557 cxstring string, 559 cxstring string,
558 cxstring suffix 560 cxstring suffix
559 ) { 561 ) {
560 if (string.length < suffix.length) return false; 562 if (string.length < suffix.length) return false;
561 return memcmp(string.ptr + string.length - suffix.length, 563 return memcmp(string.ptr + string.length - suffix.length,
562 suffix.ptr, suffix.length) == 0; 564 suffix.ptr, suffix.length) == 0;
563 } 565 }
564 566
565 bool cx_strcaseprefix( 567 bool cx_strcaseprefix_(
566 cxstring string, 568 cxstring string,
567 cxstring prefix 569 cxstring prefix
568 ) { 570 ) {
569 if (string.length < prefix.length) return false; 571 if (string.length < prefix.length) return false;
570 #ifdef _WIN32 572 #ifdef _WIN32
572 #else 574 #else
573 return strncasecmp(string.ptr, prefix.ptr, prefix.length) == 0; 575 return strncasecmp(string.ptr, prefix.ptr, prefix.length) == 0;
574 #endif 576 #endif
575 } 577 }
576 578
577 bool cx_strcasesuffix( 579 bool cx_strcasesuffix_(
578 cxstring string, 580 cxstring string,
579 cxstring suffix 581 cxstring suffix
580 ) { 582 ) {
581 if (string.length < suffix.length) return false; 583 if (string.length < suffix.length) return false;
582 #ifdef _WIN32 584 #ifdef _WIN32
586 return strncasecmp(string.ptr + string.length - suffix.length, 588 return strncasecmp(string.ptr + string.length - suffix.length,
587 suffix.ptr, suffix.length) == 0; 589 suffix.ptr, suffix.length) == 0;
588 #endif 590 #endif
589 } 591 }
590 592
591 #ifndef CX_STRREPLACE_INDEX_BUFFER_SIZE
592 #define CX_STRREPLACE_INDEX_BUFFER_SIZE 64
593 #endif
594
595 struct cx_strreplace_ibuf {
596 size_t *buf;
597 struct cx_strreplace_ibuf *next;
598 unsigned int len;
599 };
600
601 static void cx_strrepl_free_ibuf(struct cx_strreplace_ibuf *buf) {
602 // remember, the first data is on the stack!
603 buf = buf->next;
604 while (buf) {
605 struct cx_strreplace_ibuf *next = buf->next;
606 free(buf->buf);
607 free(buf);
608 buf = next;
609 }
610 }
611
612 cxmutstr cx_strreplacen_a( 593 cxmutstr cx_strreplacen_a(
613 const CxAllocator *allocator, 594 const CxAllocator *allocator,
614 cxstring str, 595 cxstring str,
615 cxstring search, 596 cxstring search,
616 cxstring replacement, 597 cxstring replacement,
617 size_t replmax 598 size_t replmax
618 ) { 599 ) {
619 600 // special cases
620 if (search.length == 0 || search.length > str.length || replmax == 0) 601 if (search.length == 0 || search.length > str.length || replmax == 0) {
621 return cx_strdup_a(allocator, str); 602 return cx_strdup_a(allocator, str);
622 603 }
623 // Compute expected buffer length 604
624 size_t ibufmax = str.length / search.length; 605 size_t in_len = str.length;
625 size_t ibuflen = replmax < ibufmax ? replmax : ibufmax; 606 size_t search_len = search.length;
626 if (ibuflen > CX_STRREPLACE_INDEX_BUFFER_SIZE) { 607 size_t repl_len = replacement.length;
627 ibuflen = CX_STRREPLACE_INDEX_BUFFER_SIZE; 608
628 } 609 // first run, count the occurrences
629 610 // and remember where the first is
630 // First index buffer can be on the stack 611 size_t occurrences = 1;
631 struct cx_strreplace_ibuf ibuf, *curbuf = &ibuf; 612 cxstring first = cx_strstr(str, search);
632 size_t ibuf_sbo[CX_STRREPLACE_INDEX_BUFFER_SIZE]; 613 if (first.length == 0) {
633 ibuf.buf = ibuf_sbo; 614 // special case, no replacements
634 ibuf.next = NULL; 615 return cx_strdup_a(allocator, str);
635 ibuf.len = 0; 616 }
636 617 cxstring tmp = cx_strsubs(first, search_len);
637 // Search occurrences 618 while (occurrences < replmax &&
638 cxstring searchstr = str; 619 (tmp = cx_strstr(tmp, search)).length > 0) {
639 size_t found = 0; 620 occurrences++;
640 do { 621 tmp = cx_strsubs(tmp, search_len);
641 cxstring match = cx_strstr(searchstr, search); 622 }
642 if (match.length > 0) { 623
643 // Allocate next buffer in chain, if required 624 // calculate necessary memory
644 if (curbuf->len == ibuflen) { 625 signed long long diff_len = (signed long long) repl_len - search_len;
645 struct cx_strreplace_ibuf *nextbuf = 626 size_t out_len = in_len + diff_len * occurrences;
646 calloc(1, sizeof(struct cx_strreplace_ibuf)); 627 cxmutstr out = {
647 if (!nextbuf) { 628 cxMalloc(allocator, out_len + 1),
648 cx_strrepl_free_ibuf(&ibuf); 629 out_len
649 return cx_mutstrn(NULL, 0); 630 };
650 } 631 if (out.ptr == NULL) return out;
651 nextbuf->buf = calloc(ibuflen, sizeof(size_t)); 632
652 if (!nextbuf->buf) { 633 // second run: perform the replacements
653 free(nextbuf); 634 // but start where we found the first occurrence
654 cx_strrepl_free_ibuf(&ibuf); 635 const char *inp = str.ptr;
655 return cx_mutstrn(NULL, 0); 636 tmp = first;
656 } 637 char *outp = out.ptr;
657 curbuf->next = nextbuf; 638 while (occurrences-- > 0 && (tmp = cx_strstr(tmp, search)).length > 0) {
658 curbuf = nextbuf; 639 size_t copylen = tmp.ptr - inp;
659 } 640 memcpy(outp, inp, copylen);
660 641 outp += copylen;
661 // Record match index 642 memcpy(outp, replacement.ptr, repl_len);
662 found++; 643 outp += repl_len;
663 size_t idx = match.ptr - str.ptr; 644 inp += copylen + search_len;
664 curbuf->buf[curbuf->len++] = idx; 645 tmp = cx_strsubs(tmp, search_len);
665 searchstr.ptr = match.ptr + search.length; 646 }
666 searchstr.length = str.length - idx - search.length; 647
667 } else { 648 // add the remaining string
668 break; 649 size_t copylen = in_len - (inp - str.ptr);
669 } 650 memcpy(outp, inp, copylen);
670 } while (searchstr.length > 0 && found < replmax); 651 out.ptr[out_len] = '\0';
671 652
672 // Allocate result string 653 return out;
673 cxmutstr result;
674 {
675 long long adjlen = (long long) replacement.length - (long long) search.length;
676 size_t rcount = 0;
677 curbuf = &ibuf;
678 do {
679 rcount += curbuf->len;
680 curbuf = curbuf->next;
681 } while (curbuf);
682 result.length = str.length + rcount * adjlen;
683 result.ptr = cxMalloc(allocator, result.length + 1);
684 if (!result.ptr) {
685 cx_strrepl_free_ibuf(&ibuf);
686 return cx_mutstrn(NULL, 0);
687 }
688 }
689
690 // Build result string
691 curbuf = &ibuf;
692 size_t srcidx = 0;
693 char *destptr = result.ptr;
694 do {
695 for (size_t i = 0; i < curbuf->len; i++) {
696 // Copy source part up to next match
697 size_t idx = curbuf->buf[i];
698 size_t srclen = idx - srcidx;
699 if (srclen > 0) {
700 memcpy(destptr, str.ptr + srcidx, srclen);
701 destptr += srclen;
702 srcidx += srclen;
703 }
704
705 // Copy the replacement and skip the source pattern
706 srcidx += search.length;
707 memcpy(destptr, replacement.ptr, replacement.length);
708 destptr += replacement.length;
709 }
710 curbuf = curbuf->next;
711 } while (curbuf);
712 memcpy(destptr, str.ptr + srcidx, str.length - srcidx);
713
714 // Result is guaranteed to be zero-terminated
715 result.ptr[result.length] = '\0';
716
717 // Free index buffer
718 cx_strrepl_free_ibuf(&ibuf);
719
720 return result;
721 } 654 }
722 655
723 CxStrtokCtx cx_strtok_( 656 CxStrtokCtx cx_strtok_(
724 cxstring str, 657 cxstring str,
725 cxstring delim, 658 cxstring delim,
1026 } 959 }
1027 *output = (float) d; 960 *output = (float) d;
1028 return 0; 961 return 0;
1029 } 962 }
1030 963
1031 static bool str_isdigit(char c) {
1032 // TODO: remove once UCX has public API for this
1033 return c >= '0' && c <= '9';
1034 }
1035
1036 int cx_strtod_lc_(cxstring str, double *output, char decsep, const char *groupsep) { 964 int cx_strtod_lc_(cxstring str, double *output, char decsep, const char *groupsep) {
1037 // TODO: overflow check 965 // TODO: overflow check
1038 // TODO: increase precision 966 // TODO: increase precision
1039 967
1040 // emptiness check 968 // emptiness check
1063 } 991 }
1064 992
1065 // parse all digits until we find the decsep 993 // parse all digits until we find the decsep
1066 size_t pos = 0; 994 size_t pos = 0;
1067 do { 995 do {
1068 if (str_isdigit(str.ptr[pos])) { 996 if (isdigit((unsigned char)str.ptr[pos])) {
1069 result = result * 10 + (str.ptr[pos] - '0'); 997 result = result * 10 + (str.ptr[pos] - '0');
1070 } else if (strchr(groupsep, str.ptr[pos]) == NULL) { 998 } else if (strchr(groupsep, str.ptr[pos]) == NULL) {
1071 break; 999 break;
1072 } 1000 }
1073 } while (++pos < str.length); 1001 } while (++pos < str.length);
1092 } 1020 }
1093 } 1021 }
1094 // parse everything until exponent or end 1022 // parse everything until exponent or end
1095 double factor = 1.; 1023 double factor = 1.;
1096 do { 1024 do {
1097 if (str_isdigit(str.ptr[pos])) { 1025 if (isdigit((unsigned char)str.ptr[pos])) {
1098 factor *= 0.1; 1026 factor *= 0.1;
1099 result = result + factor * (str.ptr[pos] - '0'); 1027 result = result + factor * (str.ptr[pos] - '0');
1100 } else if (strchr(groupsep, str.ptr[pos]) == NULL) { 1028 } else if (strchr(groupsep, str.ptr[pos]) == NULL) {
1101 break; 1029 break;
1102 } 1030 }
1133 } 1061 }
1134 1062
1135 // parse the exponent 1063 // parse the exponent
1136 unsigned int exp = 0; 1064 unsigned int exp = 0;
1137 do { 1065 do {
1138 if (str_isdigit(str.ptr[pos])) { 1066 if (isdigit((unsigned char)str.ptr[pos])) {
1139 exp = 10 * exp + (str.ptr[pos] - '0'); 1067 exp = 10 * exp + (str.ptr[pos] - '0');
1140 } else if (strchr(groupsep, str.ptr[pos]) == NULL) { 1068 } else if (strchr(groupsep, str.ptr[pos]) == NULL) {
1141 errno = EINVAL; 1069 errno = EINVAL;
1142 return -1; 1070 return -1;
1143 } 1071 }

mercurial