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