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