1 /*
2  * kmp_str.cpp -- String manipulation routines.
3  */
4 
5 //===----------------------------------------------------------------------===//
6 //
7 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
8 // See https://llvm.org/LICENSE.txt for license information.
9 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
10 //
11 //===----------------------------------------------------------------------===//
12 
13 #include "kmp_str.h"
14 
15 #include <stdarg.h> // va_*
16 #include <stdio.h> // vsnprintf()
17 #include <stdlib.h> // malloc(), realloc()
18 
19 #include "kmp.h"
20 #include "kmp_i18n.h"
21 
22 /* String buffer.
23 
24    Usage:
25 
26    // Declare buffer and initialize it.
27    kmp_str_buf_t  buffer;
28    __kmp_str_buf_init( & buffer );
29 
30    // Print to buffer.
31    __kmp_str_buf_print(& buffer, "Error in file \"%s\" line %d\n", "foo.c", 12);
32    __kmp_str_buf_print(& buffer, "    <%s>\n", line);
33 
34    // Use buffer contents. buffer.str is a pointer to data, buffer.used is a
35    // number of printed characters (not including terminating zero).
36    write( fd, buffer.str, buffer.used );
37 
38    // Free buffer.
39    __kmp_str_buf_free( & buffer );
40 
41    // Alternatively, you can detach allocated memory from buffer:
42    __kmp_str_buf_detach( & buffer );
43    return buffer.str;    // That memory should be freed eventually.
44 
45    Notes:
46 
47    * Buffer users may use buffer.str and buffer.used. Users should not change
48      any fields of buffer directly.
49    * buffer.str is never NULL. If buffer is empty, buffer.str points to empty
50      string ("").
51    * For performance reasons, buffer uses stack memory (buffer.bulk) first. If
52      stack memory is exhausted, buffer allocates memory on heap by malloc(), and
53      reallocates it by realloc() as amount of used memory grows.
54    * Buffer doubles amount of allocated memory each time it is exhausted.
55 */
56 
57 // TODO: __kmp_str_buf_print() can use thread local memory allocator.
58 
59 #define KMP_STR_BUF_INVARIANT(b)                                               \
60   {                                                                            \
61     KMP_DEBUG_ASSERT((b)->str != NULL);                                        \
62     KMP_DEBUG_ASSERT((b)->size >= sizeof((b)->bulk));                          \
63     KMP_DEBUG_ASSERT((b)->size % sizeof((b)->bulk) == 0);                      \
64     KMP_DEBUG_ASSERT((unsigned)(b)->used < (b)->size);                         \
65     KMP_DEBUG_ASSERT(                                                          \
66         (b)->size == sizeof((b)->bulk) ? (b)->str == &(b)->bulk[0] : 1);       \
67     KMP_DEBUG_ASSERT((b)->size > sizeof((b)->bulk) ? (b)->str != &(b)->bulk[0] \
68                                                    : 1);                       \
69   }
70 
71 void __kmp_str_buf_clear(kmp_str_buf_t *buffer) {
72   KMP_STR_BUF_INVARIANT(buffer);
73   if (buffer->used > 0) {
74     buffer->used = 0;
75     buffer->str[0] = 0;
76   }
77   KMP_STR_BUF_INVARIANT(buffer);
78 } // __kmp_str_buf_clear
79 
80 void __kmp_str_buf_reserve(kmp_str_buf_t *buffer, int size) {
81   KMP_STR_BUF_INVARIANT(buffer);
82   KMP_DEBUG_ASSERT(size >= 0);
83 
84   if (buffer->size < (unsigned int)size) {
85     // Calculate buffer size.
86     do {
87       buffer->size *= 2;
88     } while (buffer->size < (unsigned int)size);
89 
90     // Enlarge buffer.
91     if (buffer->str == &buffer->bulk[0]) {
92       buffer->str = (char *)KMP_INTERNAL_MALLOC(buffer->size);
93       if (buffer->str == NULL) {
94         KMP_FATAL(MemoryAllocFailed);
95       }
96       KMP_MEMCPY_S(buffer->str, buffer->size, buffer->bulk, buffer->used + 1);
97     } else {
98       buffer->str = (char *)KMP_INTERNAL_REALLOC(buffer->str, buffer->size);
99       if (buffer->str == NULL) {
100         KMP_FATAL(MemoryAllocFailed);
101       }
102     }
103   }
104 
105   KMP_DEBUG_ASSERT(buffer->size > 0);
106   KMP_DEBUG_ASSERT(buffer->size >= (unsigned)size);
107   KMP_STR_BUF_INVARIANT(buffer);
108 } // __kmp_str_buf_reserve
109 
110 void __kmp_str_buf_detach(kmp_str_buf_t *buffer) {
111   KMP_STR_BUF_INVARIANT(buffer);
112 
113   // If internal bulk is used, allocate memory and copy it.
114   if (buffer->size <= sizeof(buffer->bulk)) {
115     buffer->str = (char *)KMP_INTERNAL_MALLOC(buffer->size);
116     if (buffer->str == NULL) {
117       KMP_FATAL(MemoryAllocFailed);
118     }
119     KMP_MEMCPY_S(buffer->str, buffer->size, buffer->bulk, buffer->used + 1);
120   }
121 } // __kmp_str_buf_detach
122 
123 void __kmp_str_buf_free(kmp_str_buf_t *buffer) {
124   KMP_STR_BUF_INVARIANT(buffer);
125   if (buffer->size > sizeof(buffer->bulk)) {
126     KMP_INTERNAL_FREE(buffer->str);
127   }
128   buffer->str = buffer->bulk;
129   buffer->size = sizeof(buffer->bulk);
130   buffer->used = 0;
131   KMP_STR_BUF_INVARIANT(buffer);
132 } // __kmp_str_buf_free
133 
134 void __kmp_str_buf_cat(kmp_str_buf_t *buffer, char const *str, int len) {
135   KMP_STR_BUF_INVARIANT(buffer);
136   KMP_DEBUG_ASSERT(str != NULL);
137   KMP_DEBUG_ASSERT(len >= 0);
138   __kmp_str_buf_reserve(buffer, buffer->used + len + 1);
139   KMP_MEMCPY(buffer->str + buffer->used, str, len);
140   buffer->str[buffer->used + len] = 0;
141   buffer->used += len;
142   KMP_STR_BUF_INVARIANT(buffer);
143 } // __kmp_str_buf_cat
144 
145 void __kmp_str_buf_catbuf(kmp_str_buf_t *dest, const kmp_str_buf_t *src) {
146   KMP_DEBUG_ASSERT(dest);
147   KMP_DEBUG_ASSERT(src);
148   KMP_STR_BUF_INVARIANT(dest);
149   KMP_STR_BUF_INVARIANT(src);
150   if (!src->str || !src->used)
151     return;
152   __kmp_str_buf_reserve(dest, dest->used + src->used + 1);
153   KMP_MEMCPY(dest->str + dest->used, src->str, src->used);
154   dest->str[dest->used + src->used] = 0;
155   dest->used += src->used;
156   KMP_STR_BUF_INVARIANT(dest);
157 } // __kmp_str_buf_catbuf
158 
159 // Return the number of characters written
160 int __kmp_str_buf_vprint(kmp_str_buf_t *buffer, char const *format,
161                          va_list args) {
162   int rc;
163   KMP_STR_BUF_INVARIANT(buffer);
164 
165   for (;;) {
166     int const free = buffer->size - buffer->used;
167     int size;
168 
169     // Try to format string.
170     {
171 /* On Linux* OS Intel(R) 64, vsnprintf() modifies args argument, so vsnprintf()
172    crashes if it is called for the second time with the same args. To prevent
173    the crash, we have to pass a fresh intact copy of args to vsnprintf() on each
174    iteration.
175 
176    Unfortunately, standard va_copy() macro is not available on Windows* OS.
177    However, it seems vsnprintf() does not modify args argument on Windows* OS.
178 */
179 
180 #if !KMP_OS_WINDOWS
181       va_list _args;
182       va_copy(_args, args); // Make copy of args.
183 #define args _args // Substitute args with its copy, _args.
184 #endif // KMP_OS_WINDOWS
185       rc = KMP_VSNPRINTF(buffer->str + buffer->used, free, format, args);
186 #if !KMP_OS_WINDOWS
187 #undef args // Remove substitution.
188       va_end(_args);
189 #endif // KMP_OS_WINDOWS
190     }
191 
192     // No errors, string has been formatted.
193     if (rc >= 0 && rc < free) {
194       buffer->used += rc;
195       break;
196     }
197 
198     // Error occurred, buffer is too small.
199     if (rc >= 0) {
200       // C99-conforming implementation of vsnprintf returns required buffer size
201       size = buffer->used + rc + 1;
202     } else {
203       // Older implementations just return -1. Double buffer size.
204       size = buffer->size * 2;
205     }
206 
207     // Enlarge buffer.
208     __kmp_str_buf_reserve(buffer, size);
209 
210     // And try again.
211   }
212 
213   KMP_DEBUG_ASSERT(buffer->size > 0);
214   KMP_STR_BUF_INVARIANT(buffer);
215   return rc;
216 } // __kmp_str_buf_vprint
217 
218 // Return the number of characters written
219 int __kmp_str_buf_print(kmp_str_buf_t *buffer, char const *format, ...) {
220   int rc;
221   va_list args;
222   va_start(args, format);
223   rc = __kmp_str_buf_vprint(buffer, format, args);
224   va_end(args);
225   return rc;
226 } // __kmp_str_buf_print
227 
228 /* The function prints specified size to buffer. Size is expressed using biggest
229    possible unit, for example 1024 is printed as "1k". */
230 void __kmp_str_buf_print_size(kmp_str_buf_t *buf, size_t size) {
231   char const *names[] = {"", "k", "M", "G", "T", "P", "E", "Z", "Y"};
232   int const units = sizeof(names) / sizeof(char const *);
233   int u = 0;
234   if (size > 0) {
235     while ((size % 1024 == 0) && (u + 1 < units)) {
236       size = size / 1024;
237       ++u;
238     }
239   }
240 
241   __kmp_str_buf_print(buf, "%" KMP_SIZE_T_SPEC "%s", size, names[u]);
242 } // __kmp_str_buf_print_size
243 
244 void __kmp_str_fname_init(kmp_str_fname_t *fname, char const *path) {
245   fname->path = NULL;
246   fname->dir = NULL;
247   fname->base = NULL;
248 
249   if (path != NULL) {
250     char *slash = NULL; // Pointer to the last character of dir.
251     char *base = NULL; // Pointer to the beginning of basename.
252     fname->path = __kmp_str_format("%s", path);
253     // Original code used strdup() function to copy a string, but on Windows* OS
254     // Intel(R) 64 it causes assertion id debug heap, so I had to replace
255     // strdup with __kmp_str_format().
256     if (KMP_OS_WINDOWS) {
257       __kmp_str_replace(fname->path, '\\', '/');
258     }
259     fname->dir = __kmp_str_format("%s", fname->path);
260     slash = strrchr(fname->dir, '/');
261     if (KMP_OS_WINDOWS &&
262         slash == NULL) { // On Windows* OS, if slash not found,
263       char first = TOLOWER(fname->dir[0]); // look for drive.
264       if ('a' <= first && first <= 'z' && fname->dir[1] == ':') {
265         slash = &fname->dir[1];
266       }
267     }
268     base = (slash == NULL ? fname->dir : slash + 1);
269     fname->base = __kmp_str_format("%s", base); // Copy basename
270     *base = 0; // and truncate dir.
271   }
272 
273 } // kmp_str_fname_init
274 
275 void __kmp_str_fname_free(kmp_str_fname_t *fname) {
276   __kmp_str_free(&fname->path);
277   __kmp_str_free(&fname->dir);
278   __kmp_str_free(&fname->base);
279 } // kmp_str_fname_free
280 
281 int __kmp_str_fname_match(kmp_str_fname_t const *fname, char const *pattern) {
282   int dir_match = 1;
283   int base_match = 1;
284 
285   if (pattern != NULL) {
286     kmp_str_fname_t ptrn;
287     __kmp_str_fname_init(&ptrn, pattern);
288     dir_match = strcmp(ptrn.dir, "*/") == 0 ||
289                 (fname->dir != NULL && __kmp_str_eqf(fname->dir, ptrn.dir));
290     base_match = strcmp(ptrn.base, "*") == 0 ||
291                  (fname->base != NULL && __kmp_str_eqf(fname->base, ptrn.base));
292     __kmp_str_fname_free(&ptrn);
293   }
294 
295   return dir_match && base_match;
296 } // __kmp_str_fname_match
297 
298 kmp_str_loc_t __kmp_str_loc_init(char const *psource, int init_fname) {
299   kmp_str_loc_t loc;
300 
301   loc._bulk = NULL;
302   loc.file = NULL;
303   loc.func = NULL;
304   loc.line = 0;
305   loc.col = 0;
306 
307   if (psource != NULL) {
308     char *str = NULL;
309     char *dummy = NULL;
310     char *line = NULL;
311     char *col = NULL;
312 
313     // Copy psource to keep it intact.
314     loc._bulk = __kmp_str_format("%s", psource);
315 
316     // Parse psource string: ";file;func;line;col;;"
317     str = loc._bulk;
318     __kmp_str_split(str, ';', &dummy, &str);
319     __kmp_str_split(str, ';', &loc.file, &str);
320     __kmp_str_split(str, ';', &loc.func, &str);
321     __kmp_str_split(str, ';', &line, &str);
322     __kmp_str_split(str, ';', &col, &str);
323 
324     // Convert line and col into numberic values.
325     if (line != NULL) {
326       loc.line = atoi(line);
327       if (loc.line < 0) {
328         loc.line = 0;
329       }
330     }
331     if (col != NULL) {
332       loc.col = atoi(col);
333       if (loc.col < 0) {
334         loc.col = 0;
335       }
336     }
337   }
338 
339   __kmp_str_fname_init(&loc.fname, init_fname ? loc.file : NULL);
340 
341   return loc;
342 } // kmp_str_loc_init
343 
344 void __kmp_str_loc_free(kmp_str_loc_t *loc) {
345   __kmp_str_fname_free(&loc->fname);
346   __kmp_str_free(&(loc->_bulk));
347   loc->file = NULL;
348   loc->func = NULL;
349 } // kmp_str_loc_free
350 
351 /* This function is intended to compare file names. On Windows* OS file names
352    are case-insensitive, so functions performs case-insensitive comparison. On
353    Linux* OS it performs case-sensitive comparison. Note: The function returns
354    *true* if strings are *equal*. */
355 int __kmp_str_eqf( // True, if strings are equal, false otherwise.
356     char const *lhs, // First string.
357     char const *rhs // Second string.
358     ) {
359   int result;
360 #if KMP_OS_WINDOWS
361   result = (_stricmp(lhs, rhs) == 0);
362 #else
363   result = (strcmp(lhs, rhs) == 0);
364 #endif
365   return result;
366 } // __kmp_str_eqf
367 
368 /* This function is like sprintf, but it *allocates* new buffer, which must be
369    freed eventually by __kmp_str_free(). The function is very convenient for
370    constructing strings, it successfully replaces strdup(), strcat(), it frees
371    programmer from buffer allocations and helps to avoid buffer overflows.
372    Examples:
373 
374    str = __kmp_str_format("%s", orig); //strdup() doesn't care about buffer size
375    __kmp_str_free( & str );
376    str = __kmp_str_format( "%s%s", orig1, orig2 ); // strcat(), doesn't care
377                                                    // about buffer size.
378    __kmp_str_free( & str );
379    str = __kmp_str_format( "%s/%s.txt", path, file ); // constructing string.
380    __kmp_str_free( & str );
381 
382    Performance note:
383    This function allocates memory with malloc() calls, so do not call it from
384    performance-critical code. In performance-critical code consider using
385    kmp_str_buf_t instead, since it uses stack-allocated buffer for short
386    strings.
387 
388    Why does this function use malloc()?
389    1. __kmp_allocate() returns cache-aligned memory allocated with malloc().
390       There are no reasons in using __kmp_allocate() for strings due to extra
391       overhead while cache-aligned memory is not necessary.
392    2. __kmp_thread_malloc() cannot be used because it requires pointer to thread
393       structure. We need to perform string operations during library startup
394       (for example, in __kmp_register_library_startup()) when no thread
395       structures are allocated yet.
396    So standard malloc() is the only available option.
397 */
398 
399 char *__kmp_str_format( // Allocated string.
400     char const *format, // Format string.
401     ... // Other parameters.
402     ) {
403   va_list args;
404   int size = 512;
405   char *buffer = NULL;
406   int rc;
407 
408   // Allocate buffer.
409   buffer = (char *)KMP_INTERNAL_MALLOC(size);
410   if (buffer == NULL) {
411     KMP_FATAL(MemoryAllocFailed);
412   }
413 
414   for (;;) {
415     // Try to format string.
416     va_start(args, format);
417     rc = KMP_VSNPRINTF(buffer, size, format, args);
418     va_end(args);
419 
420     // No errors, string has been formatted.
421     if (rc >= 0 && rc < size) {
422       break;
423     }
424 
425     // Error occurred, buffer is too small.
426     if (rc >= 0) {
427       // C99-conforming implementation of vsnprintf returns required buffer
428       // size.
429       size = rc + 1;
430     } else {
431       // Older implementations just return -1.
432       size = size * 2;
433     }
434 
435     // Enlarge buffer and try again.
436     buffer = (char *)KMP_INTERNAL_REALLOC(buffer, size);
437     if (buffer == NULL) {
438       KMP_FATAL(MemoryAllocFailed);
439     }
440   }
441 
442   return buffer;
443 } // func __kmp_str_format
444 
445 void __kmp_str_free(char **str) {
446   KMP_DEBUG_ASSERT(str != NULL);
447   KMP_INTERNAL_FREE(*str);
448   *str = NULL;
449 } // func __kmp_str_free
450 
451 /* If len is zero, returns true iff target and data have exact case-insensitive
452    match. If len is negative, returns true iff target is a case-insensitive
453    substring of data. If len is positive, returns true iff target is a
454    case-insensitive substring of data or vice versa, and neither is shorter than
455    len. */
456 int __kmp_str_match(char const *target, int len, char const *data) {
457   int i;
458   if (target == NULL || data == NULL) {
459     return FALSE;
460   }
461   for (i = 0; target[i] && data[i]; ++i) {
462     if (TOLOWER(target[i]) != TOLOWER(data[i])) {
463       return FALSE;
464     }
465   }
466   return ((len > 0) ? i >= len : (!target[i] && (len || !data[i])));
467 } // __kmp_str_match
468 
469 int __kmp_str_match_false(char const *data) {
470   int result =
471       __kmp_str_match("false", 1, data) || __kmp_str_match("off", 2, data) ||
472       __kmp_str_match("0", 1, data) || __kmp_str_match(".false.", 2, data) ||
473       __kmp_str_match(".f.", 2, data) || __kmp_str_match("no", 1, data) ||
474       __kmp_str_match("disabled", 0, data);
475   return result;
476 } // __kmp_str_match_false
477 
478 int __kmp_str_match_true(char const *data) {
479   int result =
480       __kmp_str_match("true", 1, data) || __kmp_str_match("on", 2, data) ||
481       __kmp_str_match("1", 1, data) || __kmp_str_match(".true.", 2, data) ||
482       __kmp_str_match(".t.", 2, data) || __kmp_str_match("yes", 1, data) ||
483       __kmp_str_match("enabled", 0, data);
484   return result;
485 } // __kmp_str_match_true
486 
487 void __kmp_str_replace(char *str, char search_for, char replace_with) {
488   char *found = NULL;
489 
490   found = strchr(str, search_for);
491   while (found) {
492     *found = replace_with;
493     found = strchr(found + 1, search_for);
494   }
495 } // __kmp_str_replace
496 
497 void __kmp_str_split(char *str, // I: String to split.
498                      char delim, // I: Character to split on.
499                      char **head, // O: Pointer to head (may be NULL).
500                      char **tail // O: Pointer to tail (may be NULL).
501                      ) {
502   char *h = str;
503   char *t = NULL;
504   if (str != NULL) {
505     char *ptr = strchr(str, delim);
506     if (ptr != NULL) {
507       *ptr = 0;
508       t = ptr + 1;
509     }
510   }
511   if (head != NULL) {
512     *head = h;
513   }
514   if (tail != NULL) {
515     *tail = t;
516   }
517 } // __kmp_str_split
518 
519 /* strtok_r() is not available on Windows* OS. This function reimplements
520    strtok_r(). */
521 char *__kmp_str_token(
522     char *str, // String to split into tokens. Note: String *is* modified!
523     char const *delim, // Delimiters.
524     char **buf // Internal buffer.
525     ) {
526   char *token = NULL;
527 #if KMP_OS_WINDOWS
528   // On Windows* OS there is no strtok_r() function. Let us implement it.
529   if (str != NULL) {
530     *buf = str; // First call, initialize buf.
531   }
532   *buf += strspn(*buf, delim); // Skip leading delimiters.
533   if (**buf != 0) { // Rest of the string is not yet empty.
534     token = *buf; // Use it as result.
535     *buf += strcspn(*buf, delim); // Skip non-delimiters.
536     if (**buf != 0) { // Rest of the string is not yet empty.
537       **buf = 0; // Terminate token here.
538       *buf += 1; // Advance buf to start with the next token next time.
539     }
540   }
541 #else
542   // On Linux* OS and OS X*, strtok_r() is available. Let us use it.
543   token = strtok_r(str, delim, buf);
544 #endif
545   return token;
546 } // __kmp_str_token
547 
548 int __kmp_str_to_int(char const *str, char sentinel) {
549   int result, factor;
550   char const *t;
551 
552   result = 0;
553 
554   for (t = str; *t != '\0'; ++t) {
555     if (*t < '0' || *t > '9')
556       break;
557     result = (result * 10) + (*t - '0');
558   }
559 
560   switch (*t) {
561   case '\0': /* the current default for no suffix is bytes */
562     factor = 1;
563     break;
564   case 'b':
565   case 'B': /* bytes */
566     ++t;
567     factor = 1;
568     break;
569   case 'k':
570   case 'K': /* kilo-bytes */
571     ++t;
572     factor = 1024;
573     break;
574   case 'm':
575   case 'M': /* mega-bytes */
576     ++t;
577     factor = (1024 * 1024);
578     break;
579   default:
580     if (*t != sentinel)
581       return (-1);
582     t = "";
583     factor = 1;
584   }
585 
586   if (result > (INT_MAX / factor))
587     result = INT_MAX;
588   else
589     result *= factor;
590 
591   return (*t != 0 ? 0 : result);
592 } // __kmp_str_to_int
593 
594 /* The routine parses input string. It is expected it is a unsigned integer with
595    optional unit. Units are: "b" for bytes, "kb" or just "k" for kilobytes, "mb"
596    or "m" for megabytes, ..., "yb" or "y" for yottabytes. :-) Unit name is
597    case-insensitive. The routine returns 0 if everything is ok, or error code:
598    -1 in case of overflow, -2 in case of unknown unit. *size is set to parsed
599    value. In case of overflow *size is set to KMP_SIZE_T_MAX, in case of unknown
600    unit *size is set to zero. */
601 void __kmp_str_to_size( // R: Error code.
602     char const *str, // I: String of characters, unsigned number and unit ("b",
603     // "kb", etc).
604     size_t *out, // O: Parsed number.
605     size_t dfactor, // I: The factor if none of the letters specified.
606     char const **error // O: Null if everything is ok, error message otherwise.
607     ) {
608 
609   size_t value = 0;
610   size_t factor = 0;
611   int overflow = 0;
612   int i = 0;
613   int digit;
614 
615   KMP_DEBUG_ASSERT(str != NULL);
616 
617   // Skip spaces.
618   while (str[i] == ' ' || str[i] == '\t') {
619     ++i;
620   }
621 
622   // Parse number.
623   if (str[i] < '0' || str[i] > '9') {
624     *error = KMP_I18N_STR(NotANumber);
625     return;
626   }
627   do {
628     digit = str[i] - '0';
629     overflow = overflow || (value > (KMP_SIZE_T_MAX - digit) / 10);
630     value = (value * 10) + digit;
631     ++i;
632   } while (str[i] >= '0' && str[i] <= '9');
633 
634   // Skip spaces.
635   while (str[i] == ' ' || str[i] == '\t') {
636     ++i;
637   }
638 
639 // Parse unit.
640 #define _case(ch, exp)                                                         \
641   case ch:                                                                     \
642   case ch - ('a' - 'A'): {                                                     \
643     size_t shift = (exp)*10;                                                   \
644     ++i;                                                                       \
645     if (shift < sizeof(size_t) * 8) {                                          \
646       factor = (size_t)(1) << shift;                                           \
647     } else {                                                                   \
648       overflow = 1;                                                            \
649     }                                                                          \
650   } break;
651   switch (str[i]) {
652     _case('k', 1); // Kilo
653     _case('m', 2); // Mega
654     _case('g', 3); // Giga
655     _case('t', 4); // Tera
656     _case('p', 5); // Peta
657     _case('e', 6); // Exa
658     _case('z', 7); // Zetta
659     _case('y', 8); // Yotta
660     // Oops. No more units...
661   }
662 #undef _case
663   if (str[i] == 'b' || str[i] == 'B') { // Skip optional "b".
664     if (factor == 0) {
665       factor = 1;
666     }
667     ++i;
668   }
669   if (!(str[i] == ' ' || str[i] == '\t' || str[i] == 0)) { // Bad unit
670     *error = KMP_I18N_STR(BadUnit);
671     return;
672   }
673 
674   if (factor == 0) {
675     factor = dfactor;
676   }
677 
678   // Apply factor.
679   overflow = overflow || (value > (KMP_SIZE_T_MAX / factor));
680   value *= factor;
681 
682   // Skip spaces.
683   while (str[i] == ' ' || str[i] == '\t') {
684     ++i;
685   }
686 
687   if (str[i] != 0) {
688     *error = KMP_I18N_STR(IllegalCharacters);
689     return;
690   }
691 
692   if (overflow) {
693     *error = KMP_I18N_STR(ValueTooLarge);
694     *out = KMP_SIZE_T_MAX;
695     return;
696   }
697 
698   *error = NULL;
699   *out = value;
700 } // __kmp_str_to_size
701 
702 void __kmp_str_to_uint( // R: Error code.
703     char const *str, // I: String of characters, unsigned number.
704     kmp_uint64 *out, // O: Parsed number.
705     char const **error // O: Null if everything is ok, error message otherwise.
706     ) {
707   size_t value = 0;
708   int overflow = 0;
709   int i = 0;
710   int digit;
711 
712   KMP_DEBUG_ASSERT(str != NULL);
713 
714   // Skip spaces.
715   while (str[i] == ' ' || str[i] == '\t') {
716     ++i;
717   }
718 
719   // Parse number.
720   if (str[i] < '0' || str[i] > '9') {
721     *error = KMP_I18N_STR(NotANumber);
722     return;
723   }
724   do {
725     digit = str[i] - '0';
726     overflow = overflow || (value > (KMP_SIZE_T_MAX - digit) / 10);
727     value = (value * 10) + digit;
728     ++i;
729   } while (str[i] >= '0' && str[i] <= '9');
730 
731   // Skip spaces.
732   while (str[i] == ' ' || str[i] == '\t') {
733     ++i;
734   }
735 
736   if (str[i] != 0) {
737     *error = KMP_I18N_STR(IllegalCharacters);
738     return;
739   }
740 
741   if (overflow) {
742     *error = KMP_I18N_STR(ValueTooLarge);
743     *out = (kmp_uint64)-1;
744     return;
745   }
746 
747   *error = NULL;
748   *out = value;
749 } // __kmp_str_to_unit
750 
751 // end of file //
752