1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public License
4  * as published by the Free Software Foundation; either version 2
5  * of the License, or (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software Foundation,
14  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15  *
16  * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
17  * All rights reserved.
18  */
19 
20 /** \file
21  * \ingroup bli
22  */
23 
24 #include <ctype.h>
25 #include <inttypes.h>
26 #include <math.h>
27 #include <stdarg.h>
28 #include <stdlib.h>
29 #include <string.h>
30 
31 #include "MEM_guardedalloc.h"
32 
33 #include "BLI_dynstr.h"
34 #include "BLI_string.h"
35 
36 #include "BLI_utildefines.h"
37 
38 #ifdef __GNUC__
39 #  pragma GCC diagnostic error "-Wsign-conversion"
40 #endif
41 
42 // #define DEBUG_STRSIZE
43 
44 /**
45  * Duplicates the first \a len bytes of cstring \a str
46  * into a newly mallocN'd string and returns it. \a str
47  * is assumed to be at least len bytes long.
48  *
49  * \param str: The string to be duplicated
50  * \param len: The number of bytes to duplicate
51  * \retval Returns the duplicated string
52  */
BLI_strdupn(const char * str,const size_t len)53 char *BLI_strdupn(const char *str, const size_t len)
54 {
55   char *n = MEM_mallocN(len + 1, "strdup");
56   memcpy(n, str, len);
57   n[len] = '\0';
58 
59   return n;
60 }
61 
62 /**
63  * Duplicates the cstring \a str into a newly mallocN'd
64  * string and returns it.
65  *
66  * \param str: The string to be duplicated
67  * \retval Returns the duplicated string
68  */
BLI_strdup(const char * str)69 char *BLI_strdup(const char *str)
70 {
71   return BLI_strdupn(str, strlen(str));
72 }
73 
74 /**
75  * Appends the two strings, and returns new mallocN'ed string
76  * \param str1: first string for copy
77  * \param str2: second string for append
78  * \retval Returns dst
79  */
BLI_strdupcat(const char * __restrict str1,const char * __restrict str2)80 char *BLI_strdupcat(const char *__restrict str1, const char *__restrict str2)
81 {
82   /* include the NULL terminator of str2 only */
83   const size_t str1_len = strlen(str1);
84   const size_t str2_len = strlen(str2) + 1;
85   char *str, *s;
86 
87   str = MEM_mallocN(str1_len + str2_len, "strdupcat");
88   s = str;
89 
90   memcpy(s, str1, str1_len); /* NOLINT: bugprone-not-null-terminated-result */
91   s += str1_len;
92   memcpy(s, str2, str2_len);
93 
94   return str;
95 }
96 
97 /**
98  * Like strncpy but ensures dst is always
99  * '\0' terminated.
100  *
101  * \param dst: Destination for copy
102  * \param src: Source string to copy
103  * \param maxncpy: Maximum number of characters to copy (generally
104  * the size of dst)
105  * \retval Returns dst
106  */
BLI_strncpy(char * __restrict dst,const char * __restrict src,const size_t maxncpy)107 char *BLI_strncpy(char *__restrict dst, const char *__restrict src, const size_t maxncpy)
108 {
109   size_t srclen = BLI_strnlen(src, maxncpy - 1);
110   BLI_assert(maxncpy != 0);
111 
112 #ifdef DEBUG_STRSIZE
113   memset(dst, 0xff, sizeof(*dst) * maxncpy);
114 #endif
115 
116   memcpy(dst, src, srclen);
117   dst[srclen] = '\0';
118   return dst;
119 }
120 
121 /**
122  * Like BLI_strncpy but ensures dst is always padded by given char,
123  * on both sides (unless src is empty).
124  *
125  * \param dst: Destination for copy
126  * \param src: Source string to copy
127  * \param pad: the char to use for padding
128  * \param maxncpy: Maximum number of characters to copy (generally the size of dst)
129  * \retval Returns dst
130  */
BLI_strncpy_ensure_pad(char * __restrict dst,const char * __restrict src,const char pad,size_t maxncpy)131 char *BLI_strncpy_ensure_pad(char *__restrict dst,
132                              const char *__restrict src,
133                              const char pad,
134                              size_t maxncpy)
135 {
136   BLI_assert(maxncpy != 0);
137 
138 #ifdef DEBUG_STRSIZE
139   memset(dst, 0xff, sizeof(*dst) * maxncpy);
140 #endif
141 
142   if (src[0] == '\0') {
143     dst[0] = '\0';
144   }
145   else {
146     /* Add heading/trailing wildcards if needed. */
147     size_t idx = 0;
148     size_t srclen;
149 
150     if (src[idx] != pad) {
151       dst[idx++] = pad;
152       maxncpy--;
153     }
154     maxncpy--; /* trailing '\0' */
155 
156     srclen = BLI_strnlen(src, maxncpy);
157     if ((src[srclen - 1] != pad) && (srclen == maxncpy)) {
158       srclen--;
159     }
160 
161     memcpy(&dst[idx], src, srclen);
162     idx += srclen;
163 
164     if (dst[idx - 1] != pad) {
165       dst[idx++] = pad;
166     }
167     dst[idx] = '\0';
168   }
169 
170   return dst;
171 }
172 
173 /**
174  * Like strncpy but ensures dst is always
175  * '\0' terminated.
176  *
177  * \note This is a duplicate of #BLI_strncpy that returns bytes copied.
178  * And is a drop in replacement for 'snprintf(str, sizeof(str), "%s", arg);'
179  *
180  * \param dst: Destination for copy
181  * \param src: Source string to copy
182  * \param maxncpy: Maximum number of characters to copy (generally
183  * the size of dst)
184  * \retval The number of bytes copied (The only difference from BLI_strncpy).
185  */
BLI_strncpy_rlen(char * __restrict dst,const char * __restrict src,const size_t maxncpy)186 size_t BLI_strncpy_rlen(char *__restrict dst, const char *__restrict src, const size_t maxncpy)
187 {
188   size_t srclen = BLI_strnlen(src, maxncpy - 1);
189   BLI_assert(maxncpy != 0);
190 
191 #ifdef DEBUG_STRSIZE
192   memset(dst, 0xff, sizeof(*dst) * maxncpy);
193 #endif
194 
195   memcpy(dst, src, srclen);
196   dst[srclen] = '\0';
197   return srclen;
198 }
199 
BLI_strcpy_rlen(char * __restrict dst,const char * __restrict src)200 size_t BLI_strcpy_rlen(char *__restrict dst, const char *__restrict src)
201 {
202   size_t srclen = strlen(src);
203   memcpy(dst, src, srclen + 1);
204   return srclen;
205 }
206 
207 /**
208  * Portable replacement for `vsnprintf`.
209  */
BLI_vsnprintf(char * __restrict buffer,size_t maxncpy,const char * __restrict format,va_list arg)210 size_t BLI_vsnprintf(char *__restrict buffer,
211                      size_t maxncpy,
212                      const char *__restrict format,
213                      va_list arg)
214 {
215   size_t n;
216 
217   BLI_assert(buffer != NULL);
218   BLI_assert(maxncpy > 0);
219   BLI_assert(format != NULL);
220 
221   n = (size_t)vsnprintf(buffer, maxncpy, format, arg);
222 
223   if (n != -1 && n < maxncpy) {
224     buffer[n] = '\0';
225   }
226   else {
227     buffer[maxncpy - 1] = '\0';
228   }
229 
230   return n;
231 }
232 
233 /**
234  * A version of #BLI_vsnprintf that returns ``strlen(buffer)``
235  */
BLI_vsnprintf_rlen(char * __restrict buffer,size_t maxncpy,const char * __restrict format,va_list arg)236 size_t BLI_vsnprintf_rlen(char *__restrict buffer,
237                           size_t maxncpy,
238                           const char *__restrict format,
239                           va_list arg)
240 {
241   size_t n;
242 
243   BLI_assert(buffer != NULL);
244   BLI_assert(maxncpy > 0);
245   BLI_assert(format != NULL);
246 
247   n = (size_t)vsnprintf(buffer, maxncpy, format, arg);
248 
249   if (n != -1 && n < maxncpy) {
250     /* pass */
251   }
252   else {
253     n = maxncpy - 1;
254   }
255   buffer[n] = '\0';
256 
257   return n;
258 }
259 
260 /**
261  * Portable replacement for #snprintf
262  */
BLI_snprintf(char * __restrict dst,size_t maxncpy,const char * __restrict format,...)263 size_t BLI_snprintf(char *__restrict dst, size_t maxncpy, const char *__restrict format, ...)
264 {
265   size_t n;
266   va_list arg;
267 
268 #ifdef DEBUG_STRSIZE
269   memset(dst, 0xff, sizeof(*dst) * maxncpy);
270 #endif
271 
272   va_start(arg, format);
273   n = BLI_vsnprintf(dst, maxncpy, format, arg);
274   va_end(arg);
275 
276   return n;
277 }
278 
279 /**
280  * A version of #BLI_snprintf that returns ``strlen(dst)``
281  */
BLI_snprintf_rlen(char * __restrict dst,size_t maxncpy,const char * __restrict format,...)282 size_t BLI_snprintf_rlen(char *__restrict dst, size_t maxncpy, const char *__restrict format, ...)
283 {
284   size_t n;
285   va_list arg;
286 
287 #ifdef DEBUG_STRSIZE
288   memset(dst, 0xff, sizeof(*dst) * maxncpy);
289 #endif
290 
291   va_start(arg, format);
292   n = BLI_vsnprintf_rlen(dst, maxncpy, format, arg);
293   va_end(arg);
294 
295   return n;
296 }
297 
298 /**
299  * Print formatted string into a newly #MEM_mallocN'd string
300  * and return it.
301  */
BLI_sprintfN(const char * __restrict format,...)302 char *BLI_sprintfN(const char *__restrict format, ...)
303 {
304   DynStr *ds;
305   va_list arg;
306   char *n;
307 
308   va_start(arg, format);
309 
310   ds = BLI_dynstr_new();
311   BLI_dynstr_vappendf(ds, format, arg);
312   n = BLI_dynstr_get_cstring(ds);
313   BLI_dynstr_free(ds);
314 
315   va_end(arg);
316 
317   return n;
318 }
319 
320 /* match pythons string escaping, assume double quotes - (")
321  * TODO: should be used to create RNA animation paths.
322  * TODO: support more fancy string escaping. current code is primitive
323  *    this basically is an ascii version of PyUnicode_EncodeUnicodeEscape()
324  *    which is a useful reference. */
BLI_strescape(char * __restrict dst,const char * __restrict src,const size_t maxncpy)325 size_t BLI_strescape(char *__restrict dst, const char *__restrict src, const size_t maxncpy)
326 {
327   size_t len = 0;
328 
329   BLI_assert(maxncpy != 0);
330 
331   while (len < maxncpy) {
332     switch (*src) {
333       case '\0':
334         goto escape_finish;
335       case '\\':
336       case '"':
337         ATTR_FALLTHROUGH;
338 
339       /* less common but should also be support */
340       case '\t':
341       case '\n':
342       case '\r':
343         if (len + 1 < maxncpy) {
344           *dst++ = '\\';
345           len++;
346         }
347         else {
348           /* not enough space to escape */
349           break;
350         }
351         ATTR_FALLTHROUGH;
352       default:
353         *dst = *src;
354         break;
355     }
356     dst++;
357     src++;
358     len++;
359   }
360 
361 escape_finish:
362 
363   *dst = '\0';
364 
365   return len;
366 }
367 
368 /**
369  * Makes a copy of the text within the "" that appear after some text 'blahblah'
370  * i.e. for string 'pose["apples"]' with prefix 'pose[', it should grab "apples"
371  *
372  * - str: is the entire string to chop
373  * - prefix: is the part of the string to leave out
374  *
375  * Assume that the strings returned must be freed afterwards, and that the inputs will contain
376  * data we want...
377  *
378  * \return the offset and a length so as to avoid doing an allocation.
379  */
BLI_str_quoted_substrN(const char * __restrict str,const char * __restrict prefix)380 char *BLI_str_quoted_substrN(const char *__restrict str, const char *__restrict prefix)
381 {
382   const char *startMatch, *endMatch;
383 
384   /* get the starting point (i.e. where prefix starts, and add prefixLen+1
385    * to it to get be after the first " */
386   startMatch = strstr(str, prefix);
387   if (startMatch) {
388     const size_t prefixLen = strlen(prefix);
389     startMatch += prefixLen + 1;
390     /* get the end point (i.e. where the next occurrence of " is after the starting point) */
391 
392     endMatch = startMatch;
393     while ((endMatch = strchr(endMatch, '"'))) {
394       if (LIKELY(*(endMatch - 1) != '\\')) {
395         break;
396       }
397       endMatch++;
398     }
399 
400     if (endMatch) {
401       /* return the slice indicated */
402       return BLI_strdupn(startMatch, (size_t)(endMatch - startMatch));
403     }
404   }
405   return BLI_strdupn("", 0);
406 }
407 
408 /**
409  * string with all instances of substr_old replaced with substr_new,
410  * Returns a copy of the c-string \a str into a newly #MEM_mallocN'd
411  * and returns it.
412  *
413  * \note A rather wasteful string-replacement utility, though this shall do for now...
414  * Feel free to replace this with an even safe + nicer alternative
415  *
416  * \param str: The string to replace occurrences of substr_old in
417  * \param substr_old: The text in the string to find and replace
418  * \param substr_new: The text in the string to find and replace
419  * \retval Returns the duplicated string
420  */
BLI_str_replaceN(const char * __restrict str,const char * __restrict substr_old,const char * __restrict substr_new)421 char *BLI_str_replaceN(const char *__restrict str,
422                        const char *__restrict substr_old,
423                        const char *__restrict substr_new)
424 {
425   DynStr *ds = NULL;
426   size_t len_old = strlen(substr_old);
427   const char *match;
428 
429   BLI_assert(substr_old[0] != '\0');
430 
431   /* While we can still find a match for the old sub-string that we're searching for,
432    * keep dicing and replacing. */
433   while ((match = strstr(str, substr_old))) {
434     /* the assembly buffer only gets created when we actually need to rebuild the string */
435     if (ds == NULL) {
436       ds = BLI_dynstr_new();
437     }
438 
439     /* If the match position does not match the current position in the string,
440      * copy the text up to this position and advance the current position in the string. */
441     if (str != match) {
442       /* Add the segment of the string from `str` to match to the buffer,
443        * then restore the value at match. */
444       BLI_dynstr_nappend(ds, str, (match - str));
445 
446       /* now our current position should be set on the start of the match */
447       str = match;
448     }
449 
450     /* Add the replacement text to the accumulation buffer. */
451     BLI_dynstr_append(ds, substr_new);
452 
453     /* Advance the current position of the string up to the end of the replaced segment. */
454     str += len_old;
455   }
456 
457   /* Finish off and return a new string that has had all occurrences of. */
458   if (ds) {
459     char *str_new;
460 
461     /* Add what's left of the string to the assembly buffer
462      * - we've been adjusting `str` to point at the end of the replaced segments. */
463     BLI_dynstr_append(ds, str);
464 
465     /* Convert to new c-string (MEM_malloc'd), and free the buffer. */
466     str_new = BLI_dynstr_get_cstring(ds);
467     BLI_dynstr_free(ds);
468 
469     return str_new;
470   }
471   /* Just create a new copy of the entire string - we avoid going through the assembly buffer
472    * for what should be a bit more efficiency. */
473   return BLI_strdup(str);
474 }
475 
476 /**
477  * In-place replace every \a src to \a dst in \a str.
478  *
479  * \param str: The string to operate on.
480  * \param src: The character to replace.
481  * \param dst: The character to replace with.
482  */
BLI_str_replace_char(char * str,char src,char dst)483 void BLI_str_replace_char(char *str, char src, char dst)
484 {
485   while (*str) {
486     if (*str == src) {
487       *str = dst;
488     }
489     str++;
490   }
491 }
492 
493 /**
494  * Compare two strings without regard to case.
495  *
496  * \retval True if the strings are equal, false otherwise.
497  */
BLI_strcaseeq(const char * a,const char * b)498 int BLI_strcaseeq(const char *a, const char *b)
499 {
500   return (BLI_strcasecmp(a, b) == 0);
501 }
502 
503 /**
504  * Portable replacement for `strcasestr` (not available in MSVC)
505  */
BLI_strcasestr(const char * s,const char * find)506 char *BLI_strcasestr(const char *s, const char *find)
507 {
508   char c, sc;
509   size_t len;
510 
511   if ((c = *find++) != 0) {
512     c = tolower(c);
513     len = strlen(find);
514     do {
515       do {
516         if ((sc = *s++) == 0) {
517           return NULL;
518         }
519         sc = tolower(sc);
520       } while (sc != c);
521     } while (BLI_strncasecmp(s, find, len) != 0);
522     s--;
523   }
524   return ((char *)s);
525 }
526 
BLI_string_max_possible_word_count(const int str_len)527 int BLI_string_max_possible_word_count(const int str_len)
528 {
529   return (str_len / 2) + 1;
530 }
531 
BLI_string_has_word_prefix(const char * haystack,const char * needle,size_t needle_len)532 bool BLI_string_has_word_prefix(const char *haystack, const char *needle, size_t needle_len)
533 {
534   const char *match = BLI_strncasestr(haystack, needle, needle_len);
535   if (match) {
536     if ((match == haystack) || (*(match - 1) == ' ') || ispunct(*(match - 1))) {
537       return true;
538     }
539     return BLI_string_has_word_prefix(match + 1, needle, needle_len);
540   }
541   return false;
542 }
543 
BLI_string_all_words_matched(const char * name,const char * str,int (* words)[2],const int words_len)544 bool BLI_string_all_words_matched(const char *name,
545                                   const char *str,
546                                   int (*words)[2],
547                                   const int words_len)
548 {
549   int index;
550   for (index = 0; index < words_len; index++) {
551     if (!BLI_string_has_word_prefix(name, str + words[index][0], (size_t)words[index][1])) {
552       break;
553     }
554   }
555   const bool all_words_matched = (index == words_len);
556 
557   return all_words_matched;
558 }
559 
560 /**
561  * Variation of #BLI_strcasestr with string length limited to \a len
562  */
BLI_strncasestr(const char * s,const char * find,size_t len)563 char *BLI_strncasestr(const char *s, const char *find, size_t len)
564 {
565   char c, sc;
566 
567   if ((c = *find++) != 0) {
568     c = tolower(c);
569     if (len > 1) {
570       do {
571         do {
572           if ((sc = *s++) == 0) {
573             return NULL;
574           }
575           sc = tolower(sc);
576         } while (sc != c);
577       } while (BLI_strncasecmp(s, find, len - 1) != 0);
578     }
579     else {
580       {
581         do {
582           if ((sc = *s++) == 0) {
583             return NULL;
584           }
585           sc = tolower(sc);
586         } while (sc != c);
587       }
588     }
589     s--;
590   }
591   return ((char *)s);
592 }
593 
BLI_strcasecmp(const char * s1,const char * s2)594 int BLI_strcasecmp(const char *s1, const char *s2)
595 {
596   int i;
597   char c1, c2;
598 
599   for (i = 0;; i++) {
600     c1 = tolower(s1[i]);
601     c2 = tolower(s2[i]);
602 
603     if (c1 < c2) {
604       return -1;
605     }
606     if (c1 > c2) {
607       return 1;
608     }
609     if (c1 == 0) {
610       break;
611     }
612   }
613 
614   return 0;
615 }
616 
BLI_strncasecmp(const char * s1,const char * s2,size_t len)617 int BLI_strncasecmp(const char *s1, const char *s2, size_t len)
618 {
619   size_t i;
620   char c1, c2;
621 
622   for (i = 0; i < len; i++) {
623     c1 = tolower(s1[i]);
624     c2 = tolower(s2[i]);
625 
626     if (c1 < c2) {
627       return -1;
628     }
629     if (c1 > c2) {
630       return 1;
631     }
632     if (c1 == 0) {
633       break;
634     }
635   }
636 
637   return 0;
638 }
639 
640 /* compare number on the left size of the string */
left_number_strcmp(const char * s1,const char * s2,int * tiebreaker)641 static int left_number_strcmp(const char *s1, const char *s2, int *tiebreaker)
642 {
643   const char *p1 = s1, *p2 = s2;
644   int numdigit, numzero1, numzero2;
645 
646   /* count and skip leading zeros */
647   for (numzero1 = 0; *p1 == '0'; numzero1++) {
648     p1++;
649   }
650   for (numzero2 = 0; *p2 == '0'; numzero2++) {
651     p2++;
652   }
653 
654   /* find number of consecutive digits */
655   for (numdigit = 0;; numdigit++) {
656     if (isdigit(*(p1 + numdigit)) && isdigit(*(p2 + numdigit))) {
657       continue;
658     }
659     if (isdigit(*(p1 + numdigit))) {
660       return 1; /* s2 is bigger */
661     }
662     if (isdigit(*(p2 + numdigit))) {
663       return -1; /* s1 is bigger */
664     }
665     break;
666   }
667 
668   /* same number of digits, compare size of number */
669   if (numdigit > 0) {
670     int compare = (int)strncmp(p1, p2, (size_t)numdigit);
671 
672     if (compare != 0) {
673       return compare;
674     }
675   }
676 
677   /* use number of leading zeros as tie breaker if still equal */
678   if (*tiebreaker == 0) {
679     if (numzero1 > numzero2) {
680       *tiebreaker = 1;
681     }
682     else if (numzero1 < numzero2) {
683       *tiebreaker = -1;
684     }
685   }
686 
687   return 0;
688 }
689 
690 /**
691  * Case insensitive, *natural* string comparison,
692  * keeping numbers in order.
693  */
BLI_strcasecmp_natural(const char * s1,const char * s2)694 int BLI_strcasecmp_natural(const char *s1, const char *s2)
695 {
696   int d1 = 0, d2 = 0;
697   char c1, c2;
698   int tiebreaker = 0;
699 
700   /* if both chars are numeric, to a left_number_strcmp().
701    * then increase string deltas as long they are
702    * numeric, else do a tolower and char compare */
703 
704   while (1) {
705     if (isdigit(s1[d1]) && isdigit(s2[d2])) {
706       int numcompare = left_number_strcmp(s1 + d1, s2 + d2, &tiebreaker);
707 
708       if (numcompare != 0) {
709         return numcompare;
710       }
711 
712       /* Some wasted work here, left_number_strcmp already consumes at least some digits. */
713       d1++;
714       while (isdigit(s1[d1])) {
715         d1++;
716       }
717       d2++;
718       while (isdigit(s2[d2])) {
719         d2++;
720       }
721     }
722 
723     /* Test for end of strings first so that shorter strings are ordered in front. */
724     if (ELEM(0, s1[d1], s2[d2])) {
725       break;
726     }
727 
728     c1 = tolower(s1[d1]);
729     c2 = tolower(s2[d2]);
730 
731     if (c1 == c2) {
732       /* Continue iteration */
733     }
734     /* Check for '.' so "foo.bar" comes before "foo 1.bar". */
735     else if (c1 == '.') {
736       return -1;
737     }
738     else if (c2 == '.') {
739       return 1;
740     }
741     else if (c1 < c2) {
742       return -1;
743     }
744     else if (c1 > c2) {
745       return 1;
746     }
747 
748     d1++;
749     d2++;
750   }
751 
752   if (tiebreaker) {
753     return tiebreaker;
754   }
755 
756   /* we might still have a different string because of lower/upper case, in
757    * that case fall back to regular string comparison */
758   return strcmp(s1, s2);
759 }
760 
761 /**
762  * Like strcmp, but will ignore any heading/trailing pad char for comparison.
763  * So e.g. if pad is '*', '*world' and 'world*' will compare equal.
764  */
BLI_strcmp_ignore_pad(const char * str1,const char * str2,const char pad)765 int BLI_strcmp_ignore_pad(const char *str1, const char *str2, const char pad)
766 {
767   size_t str1_len, str2_len;
768 
769   while (*str1 == pad) {
770     str1++;
771   }
772   while (*str2 == pad) {
773     str2++;
774   }
775 
776   str1_len = strlen(str1);
777   str2_len = strlen(str2);
778 
779   while (str1_len && (str1[str1_len - 1] == pad)) {
780     str1_len--;
781   }
782   while (str2_len && (str2[str2_len - 1] == pad)) {
783     str2_len--;
784   }
785 
786   if (str1_len == str2_len) {
787     return strncmp(str1, str2, str2_len);
788   }
789   if (str1_len > str2_len) {
790     int ret = strncmp(str1, str2, str2_len);
791     if (ret == 0) {
792       ret = 1;
793     }
794     return ret;
795   }
796   {
797     int ret = strncmp(str1, str2, str1_len);
798     if (ret == 0) {
799       ret = -1;
800     }
801     return ret;
802   }
803 }
804 
805 /* determine the length of a fixed-size string */
BLI_strnlen(const char * s,const size_t maxlen)806 size_t BLI_strnlen(const char *s, const size_t maxlen)
807 {
808   size_t len;
809 
810   for (len = 0; len < maxlen; len++, s++) {
811     if (!*s) {
812       break;
813     }
814   }
815   return len;
816 }
817 
BLI_str_tolower_ascii(char * str,const size_t len)818 void BLI_str_tolower_ascii(char *str, const size_t len)
819 {
820   size_t i;
821 
822   for (i = 0; (i < len) && str[i]; i++) {
823     if (str[i] >= 'A' && str[i] <= 'Z') {
824       str[i] += 'a' - 'A';
825     }
826   }
827 }
828 
BLI_str_toupper_ascii(char * str,const size_t len)829 void BLI_str_toupper_ascii(char *str, const size_t len)
830 {
831   size_t i;
832 
833   for (i = 0; (i < len) && str[i]; i++) {
834     if (str[i] >= 'a' && str[i] <= 'z') {
835       str[i] -= 'a' - 'A';
836     }
837   }
838 }
839 
840 /**
841  * Strip whitespace from end of the string.
842  */
BLI_str_rstrip(char * str)843 void BLI_str_rstrip(char *str)
844 {
845   for (int i = (int)strlen(str) - 1; i >= 0; i--) {
846     if (isspace(str[i])) {
847       str[i] = '\0';
848     }
849     else {
850       break;
851     }
852   }
853 }
854 
855 /**
856  * Strip trailing zeros from a float, eg:
857  *   0.0000 -> 0.0
858  *   2.0010 -> 2.001
859  *
860  * \param str:
861  * \param pad:
862  * \return The number of zeros stripped.
863  */
BLI_str_rstrip_float_zero(char * str,const char pad)864 int BLI_str_rstrip_float_zero(char *str, const char pad)
865 {
866   char *p = strchr(str, '.');
867   int totstrip = 0;
868   if (p) {
869     char *end_p;
870     p++;                         /* position at first decimal place */
871     end_p = p + (strlen(p) - 1); /* position at last character */
872     if (end_p > p) {
873       while (end_p != p && *end_p == '0') {
874         *end_p = pad;
875         end_p--;
876         totstrip++;
877       }
878     }
879   }
880 
881   return totstrip;
882 }
883 
884 /**
885  * Return index of a string in a string array.
886  *
887  * \param str: The string to find.
888  * \param str_array: Array of strings.
889  * \param str_array_len: The length of the array, or -1 for a NULL-terminated array.
890  * \return The index of str in str_array or -1.
891  */
BLI_str_index_in_array_n(const char * __restrict str,const char ** __restrict str_array,const int str_array_len)892 int BLI_str_index_in_array_n(const char *__restrict str,
893                              const char **__restrict str_array,
894                              const int str_array_len)
895 {
896   int index;
897   const char **str_iter = str_array;
898 
899   for (index = 0; index < str_array_len; str_iter++, index++) {
900     if (STREQ(str, *str_iter)) {
901       return index;
902     }
903   }
904   return -1;
905 }
906 
907 /**
908  * Return index of a string in a string array.
909  *
910  * \param str: The string to find.
911  * \param str_array: Array of strings, (must be NULL-terminated).
912  * \return The index of str in str_array or -1.
913  */
BLI_str_index_in_array(const char * __restrict str,const char ** __restrict str_array)914 int BLI_str_index_in_array(const char *__restrict str, const char **__restrict str_array)
915 {
916   int index;
917   const char **str_iter = str_array;
918 
919   for (index = 0; *str_iter; str_iter++, index++) {
920     if (STREQ(str, *str_iter)) {
921       return index;
922     }
923   }
924   return -1;
925 }
926 
927 /**
928  * Find if a string starts with another string.
929  *
930  * \param str: The string to search within.
931  * \param start: The string we look for at the start.
932  * \return If str starts with start.
933  */
BLI_str_startswith(const char * __restrict str,const char * __restrict start)934 bool BLI_str_startswith(const char *__restrict str, const char *__restrict start)
935 {
936   for (; *str && *start; str++, start++) {
937     if (*str != *start) {
938       return false;
939     }
940   }
941 
942   return (*start == '\0');
943 }
944 
BLI_strn_endswith(const char * __restrict str,const char * __restrict end,size_t slength)945 bool BLI_strn_endswith(const char *__restrict str, const char *__restrict end, size_t slength)
946 {
947   size_t elength = strlen(end);
948 
949   if (elength < slength) {
950     const char *iter = &str[slength - elength];
951     while (*iter) {
952       if (*iter++ != *end++) {
953         return false;
954       }
955     }
956     return true;
957   }
958   return false;
959 }
960 
961 /**
962  * Find if a string ends with another string.
963  *
964  * \param str: The string to search within.
965  * \param end: The string we look for at the end.
966  * \return If str ends with end.
967  */
BLI_str_endswith(const char * __restrict str,const char * __restrict end)968 bool BLI_str_endswith(const char *__restrict str, const char *__restrict end)
969 {
970   const size_t slength = strlen(str);
971   return BLI_strn_endswith(str, end, slength);
972 }
973 
974 /**
975  * Find the first char matching one of the chars in \a delim, from left.
976  *
977  * \param str: The string to search within.
978  * \param delim: The set of delimiters to search for, as unicode values.
979  * \param sep: Return value, set to the first delimiter found (or NULL if none found).
980  * \param suf: Return value, set to next char after the first delimiter found
981  * (or NULL if none found).
982  * \return The length of the prefix (i.e. *sep - str).
983  */
BLI_str_partition(const char * str,const char delim[],const char ** sep,const char ** suf)984 size_t BLI_str_partition(const char *str, const char delim[], const char **sep, const char **suf)
985 {
986   return BLI_str_partition_ex(str, NULL, delim, sep, suf, false);
987 }
988 
989 /**
990  * Find the first char matching one of the chars in \a delim, from right.
991  *
992  * \param str: The string to search within.
993  * \param delim: The set of delimiters to search for, as unicode values.
994  * \param sep: Return value, set to the first delimiter found (or NULL if none found).
995  * \param suf: Return value, set to next char after the first delimiter found
996  * (or NULL if none found).
997  * \return The length of the prefix (i.e. *sep - str).
998  */
BLI_str_rpartition(const char * str,const char delim[],const char ** sep,const char ** suf)999 size_t BLI_str_rpartition(const char *str, const char delim[], const char **sep, const char **suf)
1000 {
1001   return BLI_str_partition_ex(str, NULL, delim, sep, suf, true);
1002 }
1003 
1004 /**
1005  * Find the first char matching one of the chars in \a delim, either from left or right.
1006  *
1007  * \param str: The string to search within.
1008  * \param end: If non-NULL, the right delimiter of the string.
1009  * \param delim: The set of delimiters to search for, as unicode values.
1010  * \param sep: Return value, set to the first delimiter found (or NULL if none found).
1011  * \param suf: Return value, set to next char after the first delimiter found
1012  * (or NULL if none found).
1013  * \param from_right: If %true, search from the right of \a str, else, search from its left.
1014  * \return The length of the prefix (i.e. *sep - str).
1015  */
BLI_str_partition_ex(const char * str,const char * end,const char delim[],const char ** sep,const char ** suf,const bool from_right)1016 size_t BLI_str_partition_ex(const char *str,
1017                             const char *end,
1018                             const char delim[],
1019                             const char **sep,
1020                             const char **suf,
1021                             const bool from_right)
1022 {
1023   const char *d;
1024   char *(*func)(const char *str, int c) = from_right ? strrchr : strchr;
1025 
1026   BLI_assert(end == NULL || end > str);
1027 
1028   *sep = *suf = NULL;
1029 
1030   for (d = delim; *d != '\0'; d++) {
1031     const char *tmp;
1032 
1033     if (end) {
1034       if (from_right) {
1035         for (tmp = end - 1; (tmp >= str) && (*tmp != *d); tmp--) {
1036           /* pass */
1037         }
1038         if (tmp < str) {
1039           tmp = NULL;
1040         }
1041       }
1042       else {
1043         tmp = func(str, *d);
1044         if (tmp >= end) {
1045           tmp = NULL;
1046         }
1047       }
1048     }
1049     else {
1050       tmp = func(str, *d);
1051     }
1052 
1053     if (tmp && (from_right ? (*sep < tmp) : (!*sep || *sep > tmp))) {
1054       *sep = tmp;
1055     }
1056   }
1057 
1058   if (*sep) {
1059     *suf = *sep + 1;
1060     return (size_t)(*sep - str);
1061   }
1062 
1063   return end ? (size_t)(end - str) : strlen(str);
1064 }
1065 
BLI_str_format_int_grouped_ex(char src[16],char dst[16],int num_len)1066 static size_t BLI_str_format_int_grouped_ex(char src[16], char dst[16], int num_len)
1067 {
1068   char *p_src = src;
1069   char *p_dst = dst;
1070 
1071   const char separator = ',';
1072   int commas;
1073 
1074   if (*p_src == '-') {
1075     *p_dst++ = *p_src++;
1076     num_len--;
1077   }
1078 
1079   for (commas = 2 - num_len % 3; *p_src; commas = (commas + 1) % 3) {
1080     *p_dst++ = *p_src++;
1081     if (commas == 1) {
1082       *p_dst++ = separator;
1083     }
1084   }
1085   *--p_dst = '\0';
1086 
1087   return (size_t)(p_dst - dst);
1088 }
1089 
1090 /**
1091  * Format ints with decimal grouping.
1092  * 1000 -> 1,000
1093  *
1094  * \param dst: The resulting string
1095  * \param num: Number to format
1096  * \return The length of \a dst
1097  */
BLI_str_format_int_grouped(char dst[16],int num)1098 size_t BLI_str_format_int_grouped(char dst[16], int num)
1099 {
1100   char src[16];
1101   int num_len = sprintf(src, "%d", num);
1102 
1103   return BLI_str_format_int_grouped_ex(src, dst, num_len);
1104 }
1105 
1106 /**
1107  * Format uint64_t with decimal grouping.
1108  * 1000 -> 1,000
1109  *
1110  * \param dst: The resulting string
1111  * \param num: Number to format
1112  * \return The length of \a dst
1113  */
BLI_str_format_uint64_grouped(char dst[16],uint64_t num)1114 size_t BLI_str_format_uint64_grouped(char dst[16], uint64_t num)
1115 {
1116   /* NOTE: Buffer to hold maximum unsigned int64, which is 1.8e+19. but
1117    * we also need space for commas and null-terminator. */
1118   char src[27];
1119   int num_len = sprintf(src, "%" PRIu64 "", num);
1120 
1121   return BLI_str_format_int_grouped_ex(src, dst, num_len);
1122 }
1123 
1124 /**
1125  * Format a size in bytes using binary units.
1126  * 1000 -> 1 KB
1127  * Number of decimal places grows with the used unit (e.g. 1.5 MB, 1.55 GB, 1.545 TB).
1128  *
1129  * \param dst: The resulting string.
1130  * Dimension of 14 to support largest possible value for \a bytes (#LLONG_MAX).
1131  * \param bytes: Number to format.
1132  * \param base_10: Calculate using base 10 (GB, MB, ...) or 2 (GiB, MiB, ...).
1133  */
BLI_str_format_byte_unit(char dst[15],long long int bytes,const bool base_10)1134 void BLI_str_format_byte_unit(char dst[15], long long int bytes, const bool base_10)
1135 {
1136   double bytes_converted = bytes;
1137   int order = 0;
1138   int decimals;
1139   const int base = base_10 ? 1000 : 1024;
1140   const char *units_base_10[] = {"B", "KB", "MB", "GB", "TB", "PB"};
1141   const char *units_base_2[] = {"B", "KiB", "MiB", "GiB", "TiB", "PiB"};
1142   const int tot_units = ARRAY_SIZE(units_base_2);
1143 
1144   BLI_STATIC_ASSERT(ARRAY_SIZE(units_base_2) == ARRAY_SIZE(units_base_10), "array size mismatch");
1145 
1146   while ((fabs(bytes_converted) >= base) && ((order + 1) < tot_units)) {
1147     bytes_converted /= base;
1148     order++;
1149   }
1150   decimals = MAX2(order - 1, 0);
1151 
1152   /* Format value first, stripping away floating zeroes. */
1153   const size_t dst_len = 15;
1154   size_t len = BLI_snprintf_rlen(dst, dst_len, "%.*f", decimals, bytes_converted);
1155   len -= (size_t)BLI_str_rstrip_float_zero(dst, '\0');
1156   dst[len++] = ' ';
1157   BLI_strncpy(dst + len, base_10 ? units_base_10[order] : units_base_2[order], dst_len - len);
1158 }
1159 
1160 /**
1161  * Find the ranges needed to split \a str into its individual words.
1162  *
1163  * \param str: The string to search for words.
1164  * \param len: Size of the string to search.
1165  * \param delim: Character to use as a delimiter.
1166  * \param r_words: Info about the words found. Set to [index, len] pairs.
1167  * \param words_max: Max number of words to find
1168  * \return The number of words found in \a str
1169  */
BLI_string_find_split_words(const char * str,const size_t len,const char delim,int r_words[][2],int words_max)1170 int BLI_string_find_split_words(
1171     const char *str, const size_t len, const char delim, int r_words[][2], int words_max)
1172 {
1173   int n = 0, i;
1174   bool charsearch = true;
1175 
1176   /* Skip leading spaces */
1177   for (i = 0; (i < len) && (str[i] != '\0'); i++) {
1178     if (str[i] != delim) {
1179       break;
1180     }
1181   }
1182 
1183   for (; (i < len) && (str[i] != '\0') && (n < words_max); i++) {
1184     if ((str[i] != delim) && (charsearch == true)) {
1185       r_words[n][0] = i;
1186       charsearch = false;
1187     }
1188     else {
1189       if ((str[i] == delim) && (charsearch == false)) {
1190         r_words[n][1] = i - r_words[n][0];
1191         n++;
1192         charsearch = true;
1193       }
1194     }
1195   }
1196 
1197   if (charsearch == false) {
1198     r_words[n][1] = i - r_words[n][0];
1199     n++;
1200   }
1201 
1202   return n;
1203 }
1204