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