cleanup: style
[blender-staging.git] / source / blender / blenlib / intern / string.c
1 /*
2  * ***** BEGIN GPL LICENSE BLOCK *****
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software Foundation,
16  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  *
18  * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
19  * All rights reserved.
20  *
21  * The Original Code is: all of this file.
22  *
23  * Contributor(s): none yet.
24  *
25  * ***** END GPL LICENSE BLOCK *****
26  * 
27  */
28
29 /** \file blender/blenlib/intern/string.c
30  *  \ingroup bli
31  */
32
33
34 #include <string.h>
35 #include <stdlib.h>
36 #include <stdarg.h>
37 #include <ctype.h>
38
39 #include "MEM_guardedalloc.h"
40
41 #include "BLI_dynstr.h"
42 #include "BLI_string.h"
43
44 #include "BLI_utildefines.h"
45
46 #ifdef __GNUC__
47 #  pragma GCC diagnostic error "-Wsign-conversion"
48 #endif
49
50 // #define DEBUG_STRSIZE
51
52 /**
53  * Duplicates the first \a len bytes of cstring \a str
54  * into a newly mallocN'd string and returns it. \a str
55  * is assumed to be at least len bytes long.
56  *
57  * \param str The string to be duplicated
58  * \param len The number of bytes to duplicate
59  * \retval Returns the duplicated string
60  */
61 char *BLI_strdupn(const char *str, const size_t len)
62 {
63         char *n = MEM_mallocN(len + 1, "strdup");
64         memcpy(n, str, len);
65         n[len] = '\0';
66         
67         return n;
68 }
69
70 /**
71  * Duplicates the cstring \a str into a newly mallocN'd
72  * string and returns it.
73  *
74  * \param str The string to be duplicated
75  * \retval Returns the duplicated string
76  */
77 char *BLI_strdup(const char *str)
78 {
79         return BLI_strdupn(str, strlen(str));
80 }
81
82 /**
83  * Appends the two strings, and returns new mallocN'ed string
84  * \param str1 first string for copy
85  * \param str2 second string for append
86  * \retval Returns dst
87  */
88 char *BLI_strdupcat(const char *__restrict str1, const char *__restrict str2)
89 {
90         /* include the NULL terminator of str2 only */
91         const size_t str1_len = strlen(str1);
92         const size_t str2_len = strlen(str2) + 1;
93         char *str, *s;
94         
95         str = MEM_mallocN(str1_len + str2_len, "strdupcat");
96         s = str;
97
98         memcpy(s, str1, str1_len); s += str1_len;
99         memcpy(s, str2, str2_len);
100
101         return str;
102 }
103
104 /**
105  * Like strncpy but ensures dst is always
106  * '\0' terminated.
107  *
108  * \param dst Destination for copy
109  * \param src Source string to copy
110  * \param maxncpy Maximum number of characters to copy (generally
111  * the size of dst)
112  * \retval Returns dst
113  */
114 char *BLI_strncpy(char *__restrict dst, const char *__restrict src, const size_t maxncpy)
115 {
116         size_t srclen = BLI_strnlen(src, maxncpy - 1);
117         BLI_assert(maxncpy != 0);
118
119 #ifdef DEBUG_STRSIZE
120         memset(dst, 0xff, sizeof(*dst) * maxncpy);
121 #endif
122
123         memcpy(dst, src, srclen);
124         dst[srclen] = '\0';
125         return dst;
126 }
127
128 /**
129  * Like BLI_strncpy but ensures dst is always padded by given char, on both sides (unless src is empty).
130  *
131  * \param dst Destination for copy
132  * \param src Source string to copy
133  * \param pad the char to use for padding
134  * \param maxncpy Maximum number of characters to copy (generally the size of dst)
135  * \retval Returns dst
136  */
137 char *BLI_strncpy_ensure_pad(char *__restrict dst, const char *__restrict src, const char pad, size_t maxncpy)
138 {
139         BLI_assert(maxncpy != 0);
140
141 #ifdef DEBUG_STRSIZE
142         memset(dst, 0xff, sizeof(*dst) * maxncpy);
143 #endif
144
145         if (src[0] == '\0') {
146                 dst[0] = '\0';
147         }
148         else {
149                 /* Add heading/trailing wildcards if needed. */
150                 size_t idx = 0;
151                 size_t srclen;
152
153                 if (src[idx] != pad) {
154                         dst[idx++] = pad;
155                         maxncpy--;
156                 }
157                 maxncpy--;  /* trailing '\0' */
158
159                 srclen = BLI_strnlen(src, maxncpy);
160                 if ((src[srclen - 1] != pad) && (srclen == maxncpy)) {
161                         srclen--;
162                 }
163
164                 memcpy(&dst[idx], src, srclen);
165                 idx += srclen;
166
167                 if (dst[idx - 1] != pad) {
168                         dst[idx++] = pad;
169                 }
170                 dst[idx] = '\0';
171         }
172
173         return dst;
174 }
175
176 /**
177  * Like strncpy but ensures dst is always
178  * '\0' terminated.
179  *
180  * \note This is a duplicate of #BLI_strncpy that returns bytes copied.
181  * And is a drop in replacement for 'snprintf(str, sizeof(str), "%s", arg);'
182  *
183  * \param dst Destination for copy
184  * \param src Source string to copy
185  * \param maxncpy Maximum number of characters to copy (generally
186  * the size of dst)
187  * \retval The number of bytes copied (The only difference from BLI_strncpy).
188  */
189 size_t BLI_strncpy_rlen(char *__restrict dst, const char *__restrict src, const size_t maxncpy)
190 {
191         size_t srclen = BLI_strnlen(src, maxncpy - 1);
192         BLI_assert(maxncpy != 0);
193
194 #ifdef DEBUG_STRSIZE
195         memset(dst, 0xff, sizeof(*dst) * maxncpy);
196 #endif
197
198         memcpy(dst, src, srclen);
199         dst[srclen] = '\0';
200         return srclen;
201 }
202
203 size_t BLI_strcpy_rlen(char *__restrict dst, const char *__restrict src)
204 {
205         size_t srclen = strlen(src);
206         memcpy(dst, src, srclen + 1);
207         return srclen;
208 }
209
210 /**
211  * Portable replacement for #vsnprintf
212  */
213 size_t BLI_vsnprintf(char *__restrict buffer, size_t maxncpy, const char *__restrict format, 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  * Portable replacement for #snprintf
235  */
236 size_t BLI_snprintf(char *__restrict dst, size_t maxncpy, const char *__restrict format, ...)
237 {
238         size_t n;
239         va_list arg;
240
241 #ifdef DEBUG_STRSIZE
242         memset(dst, 0xff, sizeof(*dst) * maxncpy);
243 #endif
244
245         va_start(arg, format);
246         n = BLI_vsnprintf(dst, maxncpy, format, arg);
247         va_end(arg);
248
249         return n;
250 }
251
252 /**
253  * Print formatted string into a newly #MEM_mallocN'd string
254  * and return it.
255  */
256 char *BLI_sprintfN(const char *__restrict format, ...)
257 {
258         DynStr *ds;
259         va_list arg;
260         char *n;
261
262         BLI_assert(format != NULL);
263
264         va_start(arg, format);
265
266         ds = BLI_dynstr_new();
267         BLI_dynstr_vappendf(ds, format, arg);
268         n = BLI_dynstr_get_cstring(ds);
269         BLI_dynstr_free(ds);
270
271         va_end(arg);
272
273         return n;
274 }
275
276
277 /* match pythons string escaping, assume double quotes - (")
278  * TODO: should be used to create RNA animation paths.
279  * TODO: support more fancy string escaping. current code is primitive
280  *    this basically is an ascii version of PyUnicode_EncodeUnicodeEscape()
281  *    which is a useful reference. */
282 size_t BLI_strescape(char *__restrict dst, const char *__restrict src, const size_t maxncpy)
283 {
284         size_t len = 0;
285
286         BLI_assert(maxncpy != 0);
287
288         while (len < maxncpy) {
289                 switch (*src) {
290                         case '\0':
291                                 goto escape_finish;
292                         case '\\':
293                         case '"':
294                                 /* fall-through */
295
296                         /* less common but should also be support */
297                         case '\t':
298                         case '\n':
299                         case '\r':
300                                 if (len + 1 < maxncpy) {
301                                         *dst++ = '\\';
302                                         len++;
303                                 }
304                                 else {
305                                         /* not enough space to escape */
306                                         break;
307                                 }
308                                 /* fall-through */
309                         default:
310                                 *dst = *src;
311                                 break;
312                 }
313                 dst++;
314                 src++;
315                 len++;
316         }
317
318 escape_finish:
319
320         *dst = '\0';
321
322         return len;
323 }
324
325 /**
326  * Makes a copy of the text within the "" that appear after some text 'blahblah'
327  * i.e. for string 'pose["apples"]' with prefix 'pose[', it should grab "apples"
328  *
329  * - str: is the entire string to chop
330  * - prefix: is the part of the string to leave out
331  *
332  * Assume that the strings returned must be freed afterwards, and that the inputs will contain
333  * data we want...
334  *
335  * \return the offset and a length so as to avoid doing an allocation.
336  */
337 char *BLI_str_quoted_substrN(const char *__restrict str, const char *__restrict prefix)
338 {
339         size_t prefixLen = strlen(prefix);
340         const char *startMatch, *endMatch;
341         
342         /* get the starting point (i.e. where prefix starts, and add prefixLen+1 to it to get be after the first " */
343         startMatch = strstr(str, prefix) + prefixLen + 1;
344         if (startMatch) {
345                 /* get the end point (i.e. where the next occurance of " is after the starting point) */
346
347                 endMatch = startMatch;
348                 while ((endMatch = strchr(endMatch, '"'))) {
349                         if (LIKELY(*(endMatch - 1) != '\\')) {
350                                 break;
351                         }
352                         else {
353                                 endMatch++;
354                         }
355                 }
356
357                 if (endMatch) {
358                         /* return the slice indicated */
359                         return BLI_strdupn(startMatch, (size_t)(endMatch - startMatch));
360                 }
361         }
362         return BLI_strdupn("", 0);
363 }
364
365 /**
366  * string with all instances of substr_old replaced with substr_new,
367  * Returns a copy of the cstring \a str into a newly mallocN'd
368  * and returns it.
369  *
370  * \note A rather wasteful string-replacement utility, though this shall do for now...
371  * Feel free to replace this with an even safe + nicer alternative
372  *
373  * \param str The string to replace occurrences of substr_old in
374  * \param substr_old The text in the string to find and replace
375  * \param substr_new The text in the string to find and replace
376  * \retval Returns the duplicated string
377  */
378 char *BLI_replacestrN(const char *__restrict str, const char *__restrict substr_old, const char *__restrict substr_new)
379 {
380         DynStr *ds = NULL;
381         size_t len_old = strlen(substr_old);
382         const char *match;
383
384         BLI_assert(substr_old[0] != '\0');
385
386         /* while we can still find a match for the old substring that we're searching for, 
387          * keep dicing and replacing
388          */
389         while ((match = strstr(str, substr_old))) {
390                 /* the assembly buffer only gets created when we actually need to rebuild the string */
391                 if (ds == NULL)
392                         ds = BLI_dynstr_new();
393                         
394                 /* if the match position does not match the current position in the string, 
395                  * copy the text up to this position and advance the current position in the string
396                  */
397                 if (str != match) {
398                         /* add the segment of the string from str to match to the buffer, then restore the value at match
399                          */
400                         BLI_dynstr_nappend(ds, str, (match - str));
401                         
402                         /* now our current position should be set on the start of the match */
403                         str = match;
404                 }
405                 
406                 /* add the replacement text to the accumulation buffer */
407                 BLI_dynstr_append(ds, substr_new);
408                 
409                 /* advance the current position of the string up to the end of the replaced segment */
410                 str += len_old;
411         }
412         
413         /* finish off and return a new string that has had all occurrences of */
414         if (ds) {
415                 char *str_new;
416                 
417                 /* add what's left of the string to the assembly buffer 
418                  * - we've been adjusting str to point at the end of the replaced segments
419                  */
420                 BLI_dynstr_append(ds, str);
421                 
422                 /* convert to new c-string (MEM_malloc'd), and free the buffer */
423                 str_new = BLI_dynstr_get_cstring(ds);
424                 BLI_dynstr_free(ds);
425                 
426                 return str_new;
427         }
428         else {
429                 /* just create a new copy of the entire string - we avoid going through the assembly buffer 
430                  * for what should be a bit more efficiency...
431                  */
432                 return BLI_strdup(str);
433         }
434
435
436 /**
437  * Compare two strings without regard to case.
438  *
439  * \retval True if the strings are equal, false otherwise.
440  */
441 int BLI_strcaseeq(const char *a, const char *b) 
442 {
443         return (BLI_strcasecmp(a, b) == 0);
444 }
445
446 /**
447  * Portable replacement for #strcasestr (not available in MSVC)
448  */
449 char *BLI_strcasestr(const char *s, const char *find)
450 {
451         register char c, sc;
452         register size_t len;
453         
454         if ((c = *find++) != 0) {
455                 c = tolower(c);
456                 len = strlen(find);
457                 do {
458                         do {
459                                 if ((sc = *s++) == 0)
460                                         return (NULL);
461                                 sc = tolower(sc);
462                         } while (sc != c);
463                 } while (BLI_strncasecmp(s, find, len) != 0);
464                 s--;
465         }
466         return ((char *) s);
467 }
468
469
470 int BLI_strcasecmp(const char *s1, const char *s2)
471 {
472         register int i;
473         register char c1, c2;
474
475         for (i = 0;; i++) {
476                 c1 = tolower(s1[i]);
477                 c2 = tolower(s2[i]);
478
479                 if (c1 < c2) {
480                         return -1;
481                 }
482                 else if (c1 > c2) {
483                         return 1;
484                 }
485                 else if (c1 == 0) {
486                         break;
487                 }
488         }
489
490         return 0;
491 }
492
493 int BLI_strncasecmp(const char *s1, const char *s2, size_t len)
494 {
495         register size_t i;
496         register char c1, c2;
497
498         for (i = 0; i < len; i++) {
499                 c1 = tolower(s1[i]);
500                 c2 = tolower(s2[i]);
501
502                 if (c1 < c2) {
503                         return -1;
504                 }
505                 else if (c1 > c2) {
506                         return 1;
507                 }
508                 else if (c1 == 0) {
509                         break;
510                 }
511         }
512
513         return 0;
514 }
515
516 /* compare number on the left size of the string */
517 static int left_number_strcmp(const char *s1, const char *s2, int *tiebreaker)
518 {
519         const char *p1 = s1, *p2 = s2;
520         int numdigit, numzero1, numzero2;
521
522         /* count and skip leading zeros */
523         for (numzero1 = 0; *p1 && (*p1 == '0'); numzero1++)
524                 p1++;
525         for (numzero2 = 0; *p2 && (*p2 == '0'); numzero2++)
526                 p2++;
527
528         /* find number of consecutive digits */
529         for (numdigit = 0; ; numdigit++) {
530                 if (isdigit(*(p1 + numdigit)) && isdigit(*(p2 + numdigit)))
531                         continue;
532                 else if (isdigit(*(p1 + numdigit)))
533                         return 1; /* s2 is bigger */
534                 else if (isdigit(*(p2 + numdigit)))
535                         return -1; /* s1 is bigger */
536                 else
537                         break;
538         }
539
540         /* same number of digits, compare size of number */
541         if (numdigit > 0) {
542                 int compare = (int)strncmp(p1, p2, (size_t)numdigit);
543
544                 if (compare != 0)
545                         return compare;
546         }
547
548         /* use number of leading zeros as tie breaker if still equal */
549         if (*tiebreaker == 0) {
550                 if (numzero1 > numzero2)
551                         *tiebreaker = 1;
552                 else if (numzero1 < numzero2)
553                         *tiebreaker = -1;
554         }
555
556         return 0;
557 }
558
559 /* natural string compare, keeping numbers in order */
560 int BLI_natstrcmp(const char *s1, const char *s2)
561 {
562         register int d1 = 0, d2 = 0;
563         register char c1, c2;
564         int tiebreaker = 0;
565
566         /* if both chars are numeric, to a left_number_strcmp().
567          * then increase string deltas as long they are 
568          * numeric, else do a tolower and char compare */
569
570         while (1) {
571                 c1 = tolower(s1[d1]);
572                 c2 = tolower(s2[d2]);
573                 
574                 if (isdigit(c1) && isdigit(c2)) {
575                         int numcompare = left_number_strcmp(s1 + d1, s2 + d2, &tiebreaker);
576                         
577                         if (numcompare != 0)
578                                 return numcompare;
579
580                         d1++;
581                         while (isdigit(s1[d1]))
582                                 d1++;
583                         d2++;
584                         while (isdigit(s2[d2]))
585                                 d2++;
586                         
587                         c1 = tolower(s1[d1]);
588                         c2 = tolower(s2[d2]);
589                 }
590         
591                 /* first check for '.' so "foo.bar" comes before "foo 1.bar" */
592                 if (c1 == '.' && c2 != '.')
593                         return -1;
594                 if (c1 != '.' && c2 == '.')
595                         return 1;
596                 else if (c1 < c2) {
597                         return -1;
598                 }
599                 else if (c1 > c2) {
600                         return 1;
601                 }
602                 else if (c1 == 0) {
603                         break;
604                 }
605                 d1++;
606                 d2++;
607         }
608
609         if (tiebreaker)
610                 return tiebreaker;
611         
612         /* we might still have a different string because of lower/upper case, in
613          * that case fall back to regular string comparison */
614         return strcmp(s1, s2);
615 }
616
617 /**
618  * Like strcmp, but will ignore any heading/trailing pad char for comparison.
619  * So e.g. if pad is '*', '*world' and 'world*' will compare equal.
620  */
621 int BLI_strcmp_ignore_pad(const char *str1, const char *str2, const char pad)
622 {
623         size_t str1_len, str2_len;
624
625         while (*str1 == pad) {
626                 str1++;
627         }
628         while (*str2 == pad) {
629                 str2++;
630         }
631
632         str1_len = strlen(str1);
633         str2_len = strlen(str2);
634
635         while (str1_len && (str1[str1_len - 1] == pad)) {
636                 str1_len--;
637         }
638         while (str2_len && (str2[str2_len - 1] == pad)) {
639                 str2_len--;
640         }
641
642         if (str1_len == str2_len) {
643                 return strncmp(str1, str2, str2_len);
644         }
645         else if (str1_len > str2_len) {
646                 int ret = strncmp(str1, str2, str2_len);
647                 if (ret == 0) {
648                         ret = 1;
649                 }
650                 return ret;
651         }
652         else {
653                 int ret = strncmp(str1, str2, str1_len);
654                 if (ret == 0) {
655                         ret = -1;
656                 }
657                 return ret;
658         }
659 }
660
661 void BLI_timestr(double _time, char *str, size_t maxlen)
662 {
663         /* format 00:00:00.00 (hr:min:sec) string has to be 12 long */
664         int  hr = ( (int)  _time) / (60 * 60);
665         int min = (((int)  _time) / 60 ) % 60;
666         int sec = ( (int)  _time) % 60;
667         int hun = ( (int) (_time   * 100.0)) % 100;
668
669         if (hr) {
670                 BLI_snprintf(str, maxlen, "%.2d:%.2d:%.2d.%.2d", hr, min, sec, hun);
671         }
672         else {
673                 BLI_snprintf(str, maxlen, "%.2d:%.2d.%.2d", min, sec, hun);
674         }
675 }
676
677 /* determine the length of a fixed-size string */
678 size_t BLI_strnlen(const char *s, const size_t maxlen)
679 {
680         size_t len;
681
682         for (len = 0; len < maxlen; len++, s++) {
683                 if (!*s)
684                         break;
685         }
686         return len;
687 }
688
689 void BLI_ascii_strtolower(char *str, const size_t len)
690 {
691         size_t i;
692
693         for (i = 0; (i < len) && str[i]; i++)
694                 if (str[i] >= 'A' && str[i] <= 'Z')
695                         str[i] += 'a' - 'A';
696 }
697
698 void BLI_ascii_strtoupper(char *str, const size_t len)
699 {
700         size_t i;
701
702         for (i = 0; (i < len) && str[i]; i++)
703                 if (str[i] >= 'a' && str[i] <= 'z')
704                         str[i] -= 'a' - 'A';
705 }
706
707 /**
708  * Strip trailing zeros from a float, eg:
709  *   0.0000 -> 0.0
710  *   2.0010 -> 2.001
711  *
712  * \param str
713  * \param pad
714  * \return The number of zeto's stripped.
715  */
716 int BLI_str_rstrip_float_zero(char *str, const char pad)
717 {
718         char *p = strchr(str, '.');
719         int totstrip = 0;
720         if (p) {
721                 char *end_p;
722                 p++;  /* position at first decimal place */
723                 end_p = p + (strlen(p) - 1);  /* position at last character */
724                 if (end_p > p) {
725                         while (end_p != p && *end_p == '0') {
726                                 *end_p = pad;
727                                 end_p--;
728                         }
729                 }
730         }
731
732         return totstrip;
733 }
734
735 /**
736  * Return index of a string in a string array.
737  *
738  * \param str The string to find.
739  * \param str_array Array of strings.
740  * \param str_array_len The length of the array, or -1 for a NULL-terminated array.
741  * \return The index of str in str_array or -1.
742  */
743 int BLI_str_index_in_array_n(const char *__restrict str, const char **__restrict str_array, const int str_array_len)
744 {
745         int index;
746         const char **str_iter = str_array;
747
748         for (index = 0; index < str_array_len; str_iter++, index++) {
749                 if (STREQ(str, *str_iter)) {
750                         return index;
751                 }
752         }
753         return -1;
754 }
755
756 /**
757  * Return index of a string in a string array.
758  *
759  * \param str The string to find.
760  * \param str_array Array of strings, (must be NULL-terminated).
761  * \return The index of str in str_array or -1.
762  */
763 int BLI_str_index_in_array(const char *__restrict str, const char **__restrict str_array)
764 {
765         int index;
766         const char **str_iter = str_array;
767
768         for (index = 0; *str_iter; str_iter++, index++) {
769                 if (STREQ(str, *str_iter)) {
770                         return index;
771                 }
772         }
773         return -1;
774 }
775
776 bool BLI_strn_endswith(const char *__restrict str, const char *__restrict end, size_t slength)
777 {
778         size_t elength = strlen(end);
779         
780         if (elength < slength) {
781                 const char *iter = &str[slength - elength];
782                 while (*iter) {
783                         if (*iter++ != *end++) {
784                                 return false;
785                         }
786                 }
787                 return true;
788         }
789         return false;
790 }
791
792 /**
793  * Find if a string ends with another string.
794  *
795  * \param str The string to search within.
796  * \param end The string we look for at the end.
797  * \return If str ends with end.
798  */
799 bool BLI_str_endswith(const char *__restrict str, const char *end)
800 {
801         const size_t slength = strlen(str);
802         return BLI_strn_endswith(str, end, slength);
803 }
804
805 /**
806  * Find the first char matching one of the chars in \a delim, from left.
807  *
808  * \param str The string to search within.
809  * \param delim The set of delimiters to search for, as unicode values.
810  * \param sep Return value, set to the first delimiter found (or NULL if none found).
811  * \param suf Return value, set to next char after the first delimiter found (or NULL if none found).
812  * \return The length of the prefix (i.e. *sep - str).
813  */
814 size_t BLI_str_partition(const char *str, const char delim[], char **sep, char **suf)
815 {
816         return BLI_str_partition_ex(str, delim, sep, suf, false);
817 }
818
819 /**
820  * Find the first char matching one of the chars in \a delim, from right.
821  *
822  * \param str The string to search within.
823  * \param delim The set of delimiters to search for, as unicode values.
824  * \param sep Return value, set to the first delimiter found (or NULL if none found).
825  * \param suf Return value, set to next char after the first delimiter found (or NULL if none found).
826  * \return The length of the prefix (i.e. *sep - str).
827  */
828 size_t BLI_str_rpartition(const char *str, const char delim[], char **sep, char **suf)
829 {
830         return BLI_str_partition_ex(str, delim, sep, suf, true);
831 }
832
833 /**
834  * Find the first char matching one of the chars in \a delim, either from left or right.
835  *
836  * \param str The string to search within.
837  * \param delim The set of delimiters to search for, as unicode values.
838  * \param sep Return value, set to the first delimiter found (or NULL if none found).
839  * \param suf Return value, set to next char after the first delimiter found (or NULL if none found).
840  * \param from_right If %true, search from the right of \a str, else, search from its left.
841  * \return The length of the prefix (i.e. *sep - str).
842  */
843 size_t BLI_str_partition_ex(const char *str, const char delim[], char **sep, char **suf, const bool from_right)
844 {
845         const char *d;
846         char *(*func)(const char *str, int c) = from_right ? strrchr : strchr;
847
848         *sep = *suf = NULL;
849
850         for (d = delim; *d != '\0'; ++d) {
851                 char *tmp = func(str, *d);
852
853                 if (tmp && (from_right ? (*sep < tmp) : (!*sep || *sep > tmp))) {
854                         *sep = tmp;
855                 }
856         }
857
858         if (*sep) {
859                 *suf = *sep + 1;
860                 return (size_t)(*sep - str);
861         }
862
863         return strlen(str);
864 }
865
866 /**
867  * Format ints with decimal grouping.
868  * 1000 -> 1,000
869  *
870  * \param dst  The resulting string
871  * \param num  Number to format
872  * \return The length of \a dst
873  */
874 size_t BLI_str_format_int_grouped(char dst[16], int num)
875 {
876         char src[16];
877         char *p_src = src;
878         char *p_dst = dst;
879
880         const char separator = ',';
881         int num_len, commas;
882
883         num_len = sprintf(src, "%d", num);
884
885         if (*p_src == '-') {
886                 *p_dst++ = *p_src++;
887                 num_len--;
888         }
889
890         for (commas = 2 - num_len % 3; *p_src; commas = (commas + 1) % 3) {
891                 *p_dst++ = *p_src++;
892                 if (commas == 1) {
893                         *p_dst++ = separator;
894                 }
895         }
896         *--p_dst = '\0';
897
898         return (size_t)(p_dst - dst);
899 }