Merged changes in the trunk up to revision 52690.
[blender.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 char *BLI_strdupn(const char *str, const size_t len)
47 {
48         char *n = MEM_mallocN(len + 1, "strdup");
49         memcpy(n, str, len);
50         n[len] = '\0';
51         
52         return n;
53 }
54 char *BLI_strdup(const char *str)
55 {
56         return BLI_strdupn(str, strlen(str));
57 }
58
59 char *BLI_strdupcat(const char *__restrict str1, const char *__restrict str2)
60 {
61         size_t len;
62         char *n;
63         
64         len = strlen(str1) + strlen(str2);
65         n = MEM_mallocN(len + 1, "strdupcat");
66         strcpy(n, str1);
67         strcat(n, str2);
68         
69         return n;
70 }
71
72 char *BLI_strncpy(char *__restrict dst, const char *__restrict src, const size_t maxncpy)
73 {
74         size_t srclen = strlen(src);
75         size_t cpylen = (srclen > (maxncpy - 1)) ? (maxncpy - 1) : srclen;
76         BLI_assert(maxncpy != 0);
77         
78         memcpy(dst, src, cpylen);
79         dst[cpylen] = '\0';
80         
81         return dst;
82 }
83
84 size_t BLI_vsnprintf(char *__restrict buffer, size_t count, const char *__restrict format, va_list arg)
85 {
86         size_t n;
87
88         BLI_assert(buffer != NULL);
89         BLI_assert(count > 0);
90         BLI_assert(format != NULL);
91
92         n = vsnprintf(buffer, count, format, arg);
93
94         if (n != -1 && n < count) {
95                 buffer[n] = '\0';
96         }
97         else {
98                 buffer[count - 1] = '\0';
99         }
100
101         return n;
102 }
103
104 size_t BLI_snprintf(char *__restrict buffer, size_t count, const char *__restrict format, ...)
105 {
106         size_t n;
107         va_list arg;
108
109         va_start(arg, format);
110         n = BLI_vsnprintf(buffer, count, format, arg);
111         va_end(arg);
112
113         return n;
114 }
115
116 char *BLI_sprintfN(const char *__restrict format, ...)
117 {
118         DynStr *ds;
119         va_list arg;
120         char *n;
121
122         BLI_assert(format != NULL);
123
124         va_start(arg, format);
125
126         ds = BLI_dynstr_new();
127         BLI_dynstr_vappendf(ds, format, arg);
128         n = BLI_dynstr_get_cstring(ds);
129         BLI_dynstr_free(ds);
130
131         va_end(arg);
132
133         return n;
134 }
135
136
137 /* match pythons string escaping, assume double quotes - (")
138  * TODO: should be used to create RNA animation paths.
139  * TODO: support more fancy string escaping. current code is primitive
140  *    this basically is an ascii version of PyUnicode_EncodeUnicodeEscape()
141  *    which is a useful reference. */
142 size_t BLI_strescape(char *__restrict dst, const char *__restrict src, const size_t maxncpy)
143 {
144         size_t len = 0;
145
146         BLI_assert(maxncpy != 0);
147
148         while (len < maxncpy) {
149                 switch (*src) {
150                         case '\0':
151                                 goto escape_finish;
152                         case '\\':
153                         case '"':
154
155                         /* less common but should also be support */
156                         case '\t':
157                         case '\n':
158                         case '\r':
159                                 if (len + 1 < maxncpy) {
160                                         *dst++ = '\\';
161                                         len++;
162                                 }
163                                 else {
164                                         /* not enough space to escape */
165                                         break;
166                                 }
167                         /* intentionally pass through */
168                         default:
169                                 *dst = *src;
170                 }
171                 dst++;
172                 src++;
173                 len++;
174         }
175
176 escape_finish:
177
178         *dst = '\0';
179
180         return len;
181 }
182
183
184 /* Makes a copy of the text within the "" that appear after some text 'blahblah'
185  * i.e. for string 'pose["apples"]' with prefix 'pose[', it should grab "apples"
186  * 
187  *  - str: is the entire string to chop
188  *      - prefix: is the part of the string to leave out 
189  *
190  * Assume that the strings returned must be freed afterwards, and that the inputs will contain 
191  * data we want...
192  *
193  * TODO, return the offset and a length so as to avoid doing an allocation.
194  */
195 char *BLI_str_quoted_substrN(const char *__restrict str, const char *__restrict prefix)
196 {
197         size_t prefixLen = strlen(prefix);
198         char *startMatch, *endMatch;
199         
200         /* get the starting point (i.e. where prefix starts, and add prefixLen+1 to it to get be after the first " */
201         startMatch = strstr(str, prefix) + prefixLen + 1;
202         
203         /* get the end point (i.e. where the next occurance of " is after the starting point) */
204         endMatch = strchr(startMatch, '"'); /* "  NOTE: this comment here is just so that my text editor still shows the functions ok... */
205
206         /* return the slice indicated */
207         return BLI_strdupn(startMatch, (size_t)(endMatch - startMatch));
208 }
209
210 /* Replaces all occurrences of oldText with newText in str, returning a new string that doesn't 
211  * contain the 'replaced' occurrences.
212  */
213
214 /* A rather wasteful string-replacement utility, though this shall do for now...
215  * Feel free to replace this with an even safe + nicer alternative */
216 char *BLI_replacestr(char *__restrict str, const char *__restrict oldText, const char *__restrict newText)
217 {
218         DynStr *ds = NULL;
219         size_t lenOld = strlen(oldText);
220         char *match;
221         
222         /* sanity checks */
223         if ((str == NULL) || (str[0] == 0))
224                 return NULL;
225         else if ((oldText == NULL) || (newText == NULL) || (oldText[0] == 0))
226                 return BLI_strdup(str);
227         
228         /* while we can still find a match for the old substring that we're searching for, 
229          * keep dicing and replacing
230          */
231         while ( (match = strstr(str, oldText)) ) {
232                 /* the assembly buffer only gets created when we actually need to rebuild the string */
233                 if (ds == NULL)
234                         ds = BLI_dynstr_new();
235                         
236                 /* if the match position does not match the current position in the string, 
237                  * copy the text up to this position and advance the current position in the string
238                  */
239                 if (str != match) {
240                         /* replace the token at the 'match' position with \0 so that the copied string will be ok,
241                          * add the segment of the string from str to match to the buffer, then restore the value at match
242                          */
243                         match[0] = 0;
244                         BLI_dynstr_append(ds, str);
245                         match[0] = oldText[0];
246                         
247                         /* now our current position should be set on the start of the match */
248                         str = match;
249                 }
250                 
251                 /* add the replacement text to the accumulation buffer */
252                 BLI_dynstr_append(ds, newText);
253                 
254                 /* advance the current position of the string up to the end of the replaced segment */
255                 str += lenOld;
256         }
257         
258         /* finish off and return a new string that has had all occurrences of */
259         if (ds) {
260                 char *newStr;
261                 
262                 /* add what's left of the string to the assembly buffer 
263                  *      - we've been adjusting str to point at the end of the replaced segments
264                  */
265                 if (str != NULL)
266                         BLI_dynstr_append(ds, str);
267                 
268                 /* convert to new c-string (MEM_malloc'd), and free the buffer */
269                 newStr = BLI_dynstr_get_cstring(ds);
270                 BLI_dynstr_free(ds);
271                 
272                 return newStr;
273         }
274         else {
275                 /* just create a new copy of the entire string - we avoid going through the assembly buffer 
276                  * for what should be a bit more efficiency...
277                  */
278                 return BLI_strdup(str);
279         }
280
281
282 int BLI_strcaseeq(const char *a, const char *b) 
283 {
284         return (BLI_strcasecmp(a, b) == 0);
285 }
286
287 /* strcasestr not available in MSVC */
288 char *BLI_strcasestr(const char *s, const char *find)
289 {
290         register char c, sc;
291         register size_t len;
292         
293         if ((c = *find++) != 0) {
294                 c = tolower(c);
295                 len = strlen(find);
296                 do {
297                         do {
298                                 if ((sc = *s++) == 0)
299                                         return (NULL);
300                                 sc = tolower(sc);
301                         } while (sc != c);
302                 } while (BLI_strncasecmp(s, find, len) != 0);
303                 s--;
304         }
305         return ((char *) s);
306 }
307
308
309 int BLI_strcasecmp(const char *s1, const char *s2)
310 {
311         register int i;
312         register char c1, c2;
313
314         for (i = 0;; i++) {
315                 c1 = tolower(s1[i]);
316                 c2 = tolower(s2[i]);
317
318                 if (c1 < c2) {
319                         return -1;
320                 }
321                 else if (c1 > c2) {
322                         return 1;
323                 }
324                 else if (c1 == 0) {
325                         break;
326                 }
327         }
328
329         return 0;
330 }
331
332 int BLI_strncasecmp(const char *s1, const char *s2, size_t len)
333 {
334         register size_t i;
335         register char c1, c2;
336
337         for (i = 0; i < len; i++) {
338                 c1 = tolower(s1[i]);
339                 c2 = tolower(s2[i]);
340
341                 if (c1 < c2) {
342                         return -1;
343                 }
344                 else if (c1 > c2) {
345                         return 1;
346                 }
347                 else if (c1 == 0) {
348                         break;
349                 }
350         }
351
352         return 0;
353 }
354
355 /* natural string compare, keeping numbers in order */
356 int BLI_natstrcmp(const char *s1, const char *s2)
357 {
358         register int d1 = 0, d2 = 0;
359         register char c1, c2;
360
361         /* if both chars are numeric, to a strtol().
362          * then increase string deltas as long they are 
363          * numeric, else do a tolower and char compare */
364
365         while (1) {
366                 c1 = tolower(s1[d1]);
367                 c2 = tolower(s2[d2]);
368                 
369                 if (isdigit(c1) && isdigit(c2) ) {
370                         int val1, val2;
371                         
372                         val1 = (int)strtol(s1 + d1, (char **)NULL, 10);
373                         val2 = (int)strtol(s2 + d2, (char **)NULL, 10);
374                         
375                         if (val1 < val2) {
376                                 return -1;
377                         }
378                         else if (val1 > val2) {
379                                 return 1;
380                         }
381                         d1++;
382                         while (isdigit(s1[d1]) )
383                                 d1++;
384                         d2++;
385                         while (isdigit(s2[d2]) )
386                                 d2++;
387                         
388                         c1 = tolower(s1[d1]);
389                         c2 = tolower(s2[d2]);
390                 }
391         
392                 /* first check for '.' so "foo.bar" comes before "foo 1.bar" */
393                 if (c1 == '.' && c2 != '.')
394                         return -1;
395                 if (c1 != '.' && c2 == '.')
396                         return 1;
397                 else if (c1 < c2) {
398                         return -1;
399                 }
400                 else if (c1 > c2) {
401                         return 1;
402                 }
403                 else if (c1 == 0) {
404                         break;
405                 }
406                 d1++;
407                 d2++;
408         }
409         return 0;
410 }
411
412 void BLI_timestr(double _time, char *str)
413 {
414         /* format 00:00:00.00 (hr:min:sec) string has to be 12 long */
415         int  hr = ( (int)  _time) / (60*60);
416         int min = (((int)  _time) / 60 ) % 60;
417         int sec = ( (int) (_time)) % 60;
418         int hun = ( (int) (_time   * 100.0)) % 100;
419         
420         if (hr) {
421                 sprintf(str, "%.2d:%.2d:%.2d.%.2d", hr, min, sec, hun);
422         }
423         else {
424                 sprintf(str, "%.2d:%.2d.%.2d", min, sec, hun);
425         }
426         
427         str[11] = 0;
428 }
429
430 /* determine the length of a fixed-size string */
431 size_t BLI_strnlen(const char *str, const size_t maxlen)
432 {
433         const char *end = memchr(str, '\0', maxlen);
434         return end ? (size_t) (end - str) : maxlen;
435 }
436
437 void BLI_ascii_strtolower(char *str, const size_t len)
438 {
439         size_t i;
440
441         for (i = 0; i < len; i++)
442                 if (str[i] >= 'A' && str[i] <= 'Z')
443                         str[i] += 'a' - 'A';
444 }
445
446 void BLI_ascii_strtoupper(char *str, const size_t len)
447 {
448         size_t i;
449
450         for (i = 0; i < len; i++)
451                 if (str[i] >= 'a' && str[i] <= 'z')
452                         str[i] -= 'a' - 'A';
453 }