Merge with trunk r40782
[blender.git] / source / blender / blenlib / intern / string.c
1 /*
2  * $Id$
3  *
4  * ***** BEGIN GPL LICENSE BLOCK *****
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version 2
9  * of the License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software Foundation,
18  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19  *
20  * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
21  * All rights reserved.
22  *
23  * The Original Code is: all of this file.
24  *
25  * Contributor(s): none yet.
26  *
27  * ***** END GPL LICENSE BLOCK *****
28  * 
29  */
30
31 /** \file blender/blenlib/intern/string.c
32  *  \ingroup bli
33  */
34
35
36 #include <string.h>
37 #include <stdlib.h>
38 #include <stdarg.h>
39 #include <ctype.h>
40
41 #include "MEM_guardedalloc.h"
42
43 #include "BLI_dynstr.h"
44 #include "BLI_string.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 *str1, const char *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 *dst, const char *src, const size_t maxncpy)
73 {
74         size_t srclen= strlen(src);
75         size_t cpylen= (srclen>(maxncpy-1))?(maxncpy-1):srclen;
76         
77         memcpy(dst, src, cpylen);
78         dst[cpylen]= '\0';
79         
80         return dst;
81 }
82
83 size_t BLI_snprintf(char *buffer, size_t count, const char *format, ...)
84 {
85         size_t n;
86         va_list arg;
87
88         va_start(arg, format);
89         n = vsnprintf(buffer, count, format, arg);
90         
91         if (n != -1 && n < count) {
92                 buffer[n] = '\0';
93         }
94         else {
95                 buffer[count-1] = '\0';
96         }
97         
98         va_end(arg);
99         return n;
100 }
101
102 char *BLI_sprintfN(const char *format, ...)
103 {
104         DynStr *ds;
105         va_list arg;
106         char *n;
107
108         va_start(arg, format);
109
110         ds= BLI_dynstr_new();
111         BLI_dynstr_vappendf(ds, format, arg);
112         n= BLI_dynstr_get_cstring(ds);
113         BLI_dynstr_free(ds);
114
115         va_end(arg);
116
117         return n;
118 }
119
120
121 /* match pythons string escaping, assume double quotes - (")
122  * TODO: should be used to create RNA animation paths.
123  * TODO: support more fancy string escaping. current code is primitive
124  *    this basically is an ascii version of PyUnicode_EncodeUnicodeEscape()
125  *    which is a useful reference. */
126 size_t BLI_strescape(char *dst, const char *src, const size_t maxlen)
127 {
128         size_t len= 0;
129         while(len < maxlen) {
130                 switch(*src) {
131                         case '\0':
132                                 goto escape_finish;
133                         case '\\':
134                         case '"':
135
136                                 /* less common but should also be support */
137                         case '\t':
138                         case '\n':
139                         case '\r':
140                                 if(len + 1 <  maxlen) {
141                                         *dst++ = '\\';
142                                         len++;
143                                 }
144                                 else {
145                                         /* not enough space to escape */
146                                         break;
147                                 }
148                                 /* intentionally pass through */
149                         default:
150                                 *dst = *src;
151                 }
152                 dst++;
153                 src++;
154                 len++;
155         }
156
157 escape_finish:
158
159         *dst= '\0';
160
161         return len;
162 }
163
164
165 /* Makes a copy of the text within the "" that appear after some text 'blahblah'
166  * i.e. for string 'pose["apples"]' with prefix 'pose[', it should grab "apples"
167  * 
168  *      - str: is the entire string to chop
169  *      - prefix: is the part of the string to leave out 
170  *
171  * Assume that the strings returned must be freed afterwards, and that the inputs will contain 
172  * data we want...
173  */
174 char *BLI_getQuotedStr (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 occurances of oldText with newText in str, returning a new string that doesn't 
190  * contain the 'replaced' occurances.
191  */
192 // A rather wasteful string-replacement utility, though this shall do for now...
193 // Feel free to replace this with an even safe + nicer alternative 
194 char *BLI_replacestr(char *str, const char *oldText, const char *newText)
195 {
196         DynStr *ds= NULL;
197         size_t lenOld= strlen(oldText);
198         char *match;
199         
200         /* sanity checks */
201         if ((str == NULL) || (str[0]==0))
202                 return NULL;
203         else if ((oldText == NULL) || (newText == NULL) || (oldText[0]==0))
204                 return BLI_strdup(str);
205         
206         /* while we can still find a match for the old substring that we're searching for, 
207          * keep dicing and replacing
208          */
209         while ( (match = strstr(str, oldText)) ) {
210                 /* the assembly buffer only gets created when we actually need to rebuild the string */
211                 if (ds == NULL)
212                         ds= BLI_dynstr_new();
213                         
214                 /* if the match position does not match the current position in the string, 
215                  * copy the text up to this position and advance the current position in the string
216                  */
217                 if (str != match) {
218                         /* replace the token at the 'match' position with \0 so that the copied string will be ok,
219                          * add the segment of the string from str to match to the buffer, then restore the value at match
220                          */
221                         match[0]= 0;
222                         BLI_dynstr_append(ds, str);
223                         match[0]= oldText[0];
224                         
225                         /* now our current position should be set on the start of the match */
226                         str= match;
227                 }
228                 
229                 /* add the replacement text to the accumulation buffer */
230                 BLI_dynstr_append(ds, newText);
231                 
232                 /* advance the current position of the string up to the end of the replaced segment */
233                 str += lenOld;
234         }
235         
236         /* finish off and return a new string that has had all occurances of */
237         if (ds) {
238                 char *newStr;
239                 
240                 /* add what's left of the string to the assembly buffer 
241                  *      - we've been adjusting str to point at the end of the replaced segments
242                  */
243                 if (str != NULL)
244                         BLI_dynstr_append(ds, str);
245                 
246                 /* convert to new c-string (MEM_malloc'd), and free the buffer */
247                 newStr= BLI_dynstr_get_cstring(ds);
248                 BLI_dynstr_free(ds);
249                 
250                 return newStr;
251         }
252         else {
253                 /* just create a new copy of the entire string - we avoid going through the assembly buffer 
254                  * for what should be a bit more efficiency...
255                  */
256                 return BLI_strdup(str);
257         }
258
259
260 int BLI_strcaseeq(const char *a, const char *b) 
261 {
262         return (BLI_strcasecmp(a, b)==0);
263 }
264
265 /* strcasestr not available in MSVC */
266 char *BLI_strcasestr(const char *s, const char *find)
267 {
268         register char c, sc;
269         register size_t len;
270         
271         if ((c = *find++) != 0) {
272                 c= tolower(c);
273                 len = strlen(find);
274                 do {
275                         do {
276                                 if ((sc = *s++) == 0)
277                                         return (NULL);
278                                 sc= tolower(sc);
279                         } while (sc != c);
280                 } while (BLI_strncasecmp(s, find, len) != 0);
281                 s--;
282         }
283         return ((char *) s);
284 }
285
286
287 int BLI_strcasecmp(const char *s1, const char *s2)
288 {
289         int i;
290
291         for (i=0; ; i++) {
292                 char c1 = tolower(s1[i]);
293                 char c2 = tolower(s2[i]);
294
295                 if (c1<c2) {
296                         return -1;
297                 } else if (c1>c2) {
298                         return 1;
299                 } else if (c1==0) {
300                         break;
301                 }
302         }
303
304         return 0;
305 }
306
307 int BLI_strncasecmp(const char *s1, const char *s2, size_t len)
308 {
309         int i;
310
311         for (i=0; i<len; i++) {
312                 char c1 = tolower(s1[i]);
313                 char c2 = tolower(s2[i]);
314
315                 if (c1<c2) {
316                         return -1;
317                 } else if (c1>c2) {
318                         return 1;
319                 } else if (c1==0) {
320                         break;
321                 }
322         }
323
324         return 0;
325 }
326
327 /* natural string compare, keeping numbers in order */
328 int BLI_natstrcmp(const char *s1, const char *s2)
329 {
330         int d1= 0, d2= 0;
331         
332         /* if both chars are numeric, to a strtol().
333            then increase string deltas as long they are 
334            numeric, else do a tolower and char compare */
335         
336         while(1) {
337                 char c1 = tolower(s1[d1]);
338                 char c2 = tolower(s2[d2]);
339                 
340                 if( isdigit(c1) && isdigit(c2) ) {
341                         int val1, val2;
342                         
343                         val1= (int)strtol(s1+d1, (char **)NULL, 10);
344                         val2= (int)strtol(s2+d2, (char **)NULL, 10);
345                         
346                         if (val1<val2) {
347                                 return -1;
348                         } else if (val1>val2) {
349                                 return 1;
350                         }
351                         d1++;
352                         while( isdigit(s1[d1]) )
353                                 d1++;
354                         d2++;
355                         while( isdigit(s2[d2]) )
356                                 d2++;
357                         
358                         c1 = tolower(s1[d1]);
359                         c2 = tolower(s2[d2]);
360                 }
361         
362                 /* first check for '.' so "foo.bar" comes before "foo 1.bar" */ 
363                 if(c1=='.' && c2!='.')
364                         return -1;
365                 if(c1!='.' && c2=='.')
366                         return 1;
367                 else if (c1<c2) {
368                         return -1;
369                 } else if (c1>c2) {
370                         return 1;
371                 } else if (c1==0) {
372                         break;
373                 }
374                 d1++;
375                 d2++;
376         }
377         return 0;
378 }
379
380 void BLI_timestr(double _time, char *str)
381 {
382         /* format 00:00:00.00 (hr:min:sec) string has to be 12 long */
383         int  hr= ( (int)  _time) / (60*60);
384         int min= (((int)  _time) / 60 ) % 60;
385         int sec= ( (int) (_time)) % 60;
386         int hun= ( (int) (_time   * 100.0)) % 100;
387         
388         if (hr) {
389                 sprintf(str, "%.2d:%.2d:%.2d.%.2d",hr,min,sec,hun);
390         } else {
391                 sprintf(str, "%.2d:%.2d.%.2d",min,sec,hun);
392         }
393         
394         str[11]=0;
395 }
396
397 /* determine the length of a fixed-size string */
398 size_t BLI_strnlen(const char *str, size_t maxlen)
399 {
400         const char *end = memchr(str, '\0', maxlen);
401         return end ? (size_t) (end - str) : maxlen;
402 }
403
404 void BLI_ascii_strtolower(char *str, int len)
405 {
406         int i;
407
408         for(i=0; i<len; i++)
409                 if(str[i] >= 'A' && str[i] <= 'Z')
410                         str[i] += 'a' - 'A';
411 }
412
413 void BLI_ascii_strtoupper(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