ee5bd17c901665bf75a238cf6b815cfc912428b3
[blender-staging.git] / source / blender / blenlib / intern / string.c
1 /* util.c
2  *
3  * various string, file, list operations.
4  *
5  *
6  * $Id$
7  *
8  * ***** BEGIN GPL LICENSE BLOCK *****
9  *
10  * This program is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU General Public License
12  * as published by the Free Software Foundation; either version 2
13  * of the License, or (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software Foundation,
22  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
23  *
24  * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
25  * All rights reserved.
26  *
27  * The Original Code is: all of this file.
28  *
29  * Contributor(s): none yet.
30  *
31  * ***** END GPL LICENSE BLOCK *****
32  * 
33  */
34
35 /** \file blender/blenlib/intern/string.c
36  *  \ingroup bli
37  */
38
39
40 #include <string.h>
41 #include <stdlib.h>
42 #include <stdarg.h>
43 #include <ctype.h>
44
45 #include "MEM_guardedalloc.h"
46
47 #include "BLI_dynstr.h"
48 #include "BLI_string.h"
49
50 char *BLI_strdupn(const char *str, const size_t len) {
51         char *n= MEM_mallocN(len+1, "strdup");
52         memcpy(n, str, len);
53         n[len]= '\0';
54         
55         return n;
56 }
57 char *BLI_strdup(const char *str) {
58         return BLI_strdupn(str, strlen(str));
59 }
60
61 char *BLI_strdupcat(const char *str1, const char *str2)
62 {
63         size_t len;
64         char *n;
65         
66         len= strlen(str1)+strlen(str2);
67         n= MEM_mallocN(len+1, "strdupcat");
68         strcpy(n, str1);
69         strcat(n, str2);
70         
71         return n;
72 }
73
74 char *BLI_strncpy(char *dst, const char *src, const size_t maxncpy) {
75         size_t srclen= strlen(src);
76         size_t cpylen= (srclen>(maxncpy-1))?(maxncpy-1):srclen;
77         
78         memcpy(dst, src, cpylen);
79         dst[cpylen]= '\0';
80         
81         return dst;
82 }
83
84 size_t BLI_snprintf(char *buffer, size_t count, const char *format, ...)
85 {
86         size_t n;
87         va_list arg;
88
89         va_start(arg, format);
90         n = vsnprintf(buffer, count, format, arg);
91         
92         if (n != -1 && n < count) {
93                 buffer[n] = '\0';
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 /* Makes a copy of the text within the "" that appear after some text 'blahblah'
121  * i.e. for string 'pose["apples"]' with prefix 'pose[', it should grab "apples"
122  * 
123  *      - str: is the entire string to chop
124  *      - prefix: is the part of the string to leave out 
125  *
126  * Assume that the strings returned must be freed afterwards, and that the inputs will contain 
127  * data we want...
128  */
129 char *BLI_getQuotedStr (const char *str, const char *prefix)
130 {
131         size_t prefixLen = strlen(prefix);
132         char *startMatch, *endMatch;
133         
134         /* get the starting point (i.e. where prefix starts, and add prefixLen+1 to it to get be after the first " */
135         startMatch= strstr(str, prefix) + prefixLen + 1;
136         
137         /* get the end point (i.e. where the next occurance of " is after the starting point) */
138         endMatch= strchr(startMatch, '"'); // "  NOTE: this comment here is just so that my text editor still shows the functions ok...
139         
140         /* return the slice indicated */
141         return BLI_strdupn(startMatch, (size_t)(endMatch-startMatch));
142 }
143
144 /* Replaces all occurances of oldText with newText in str, returning a new string that doesn't 
145  * contain the 'replaced' occurances.
146  */
147 // A rather wasteful string-replacement utility, though this shall do for now...
148 // Feel free to replace this with an even safe + nicer alternative 
149 char *BLI_replacestr(char *str, const char *oldText, const char *newText)
150 {
151         DynStr *ds= NULL;
152         size_t lenOld= strlen(oldText);
153         char *match;
154         
155         /* sanity checks */
156         if ((str == NULL) || (str[0]==0))
157                 return NULL;
158         else if ((oldText == NULL) || (newText == NULL) || (oldText[0]==0))
159                 return BLI_strdup(str);
160         
161         /* while we can still find a match for the old substring that we're searching for, 
162          * keep dicing and replacing
163          */
164         while ( (match = strstr(str, oldText)) ) {
165                 /* the assembly buffer only gets created when we actually need to rebuild the string */
166                 if (ds == NULL)
167                         ds= BLI_dynstr_new();
168                         
169                 /* if the match position does not match the current position in the string, 
170                  * copy the text up to this position and advance the current position in the string
171                  */
172                 if (str != match) {
173                         /* replace the token at the 'match' position with \0 so that the copied string will be ok,
174                          * add the segment of the string from str to match to the buffer, then restore the value at match
175                          */
176                         match[0]= 0;
177                         BLI_dynstr_append(ds, str);
178                         match[0]= oldText[0];
179                         
180                         /* now our current position should be set on the start of the match */
181                         str= match;
182                 }
183                 
184                 /* add the replacement text to the accumulation buffer */
185                 BLI_dynstr_append(ds, newText);
186                 
187                 /* advance the current position of the string up to the end of the replaced segment */
188                 str += lenOld;
189         }
190         
191         /* finish off and return a new string that has had all occurances of */
192         if (ds) {
193                 char *newStr;
194                 
195                 /* add what's left of the string to the assembly buffer 
196                  *      - we've been adjusting str to point at the end of the replaced segments
197                  */
198                 if (str != NULL)
199                         BLI_dynstr_append(ds, str);
200                 
201                 /* convert to new c-string (MEM_malloc'd), and free the buffer */
202                 newStr= BLI_dynstr_get_cstring(ds);
203                 BLI_dynstr_free(ds);
204                 
205                 return newStr;
206         }
207         else {
208                 /* just create a new copy of the entire string - we avoid going through the assembly buffer 
209                  * for what should be a bit more efficiency...
210                  */
211                 return BLI_strdup(str);
212         }
213
214
215 int BLI_streq(const char *a, const char *b) 
216 {
217         return (strcmp(a, b)==0);
218 }
219
220 int BLI_strcaseeq(const char *a, const char *b) 
221 {
222         return (BLI_strcasecmp(a, b)==0);
223 }
224
225 /* strcasestr not available in MSVC */
226 char *BLI_strcasestr(const char *s, const char *find)
227 {
228         register char c, sc;
229         register size_t len;
230         
231         if ((c = *find++) != 0) {
232                 c= tolower(c);
233                 len = strlen(find);
234                 do {
235                         do {
236                                 if ((sc = *s++) == 0)
237                                         return (NULL);
238                                 sc= tolower(sc);
239                         } while (sc != c);
240                 } while (BLI_strncasecmp(s, find, len) != 0);
241                 s--;
242         }
243         return ((char *) s);
244 }
245
246
247 int BLI_strcasecmp(const char *s1, const char *s2) {
248         int i;
249
250         for (i=0; ; i++) {
251                 char c1 = tolower(s1[i]);
252                 char c2 = tolower(s2[i]);
253
254                 if (c1<c2) {
255                         return -1;
256                 } else if (c1>c2) {
257                         return 1;
258                 } else if (c1==0) {
259                         break;
260                 }
261         }
262
263         return 0;
264 }
265
266 int BLI_strncasecmp(const char *s1, const char *s2, size_t len) {
267         int i;
268
269         for (i=0; i<len; i++) {
270                 char c1 = tolower(s1[i]);
271                 char c2 = tolower(s2[i]);
272
273                 if (c1<c2) {
274                         return -1;
275                 } else if (c1>c2) {
276                         return 1;
277                 } else if (c1==0) {
278                         break;
279                 }
280         }
281
282         return 0;
283 }
284
285 /* natural string compare, keeping numbers in order */
286 int BLI_natstrcmp(const char *s1, const char *s2)
287 {
288         int d1= 0, d2= 0;
289         
290         /* if both chars are numeric, to a strtol().
291            then increase string deltas as long they are 
292            numeric, else do a tolower and char compare */
293         
294         while(1) {
295                 char c1 = tolower(s1[d1]);
296                 char c2 = tolower(s2[d2]);
297                 
298                 if( isdigit(c1) && isdigit(c2) ) {
299                         int val1, val2;
300                         
301                         val1= (int)strtol(s1+d1, (char **)NULL, 10);
302                         val2= (int)strtol(s2+d2, (char **)NULL, 10);
303                         
304                         if (val1<val2) {
305                                 return -1;
306                         } else if (val1>val2) {
307                                 return 1;
308                         }
309                         d1++;
310                         while( isdigit(s1[d1]) )
311                                 d1++;
312                         d2++;
313                         while( isdigit(s2[d2]) )
314                                 d2++;
315                         
316                         c1 = tolower(s1[d1]);
317                         c2 = tolower(s2[d2]);
318                 }
319         
320                 /* first check for '.' so "foo.bar" comes before "foo 1.bar" */ 
321                 if(c1=='.' && c2!='.')
322                         return -1;
323                 if(c1!='.' && c2=='.')
324                         return 1;
325                 else if (c1<c2) {
326                         return -1;
327                 } else if (c1>c2) {
328                         return 1;
329                 } else if (c1==0) {
330                         break;
331                 }
332                 d1++;
333                 d2++;
334         }
335         return 0;
336 }
337
338 void BLI_timestr(double _time, char *str)
339 {
340         /* format 00:00:00.00 (hr:min:sec) string has to be 12 long */
341         int  hr= ( (int)  _time) / (60*60);
342         int min= (((int)  _time) / 60 ) % 60;
343         int sec= ( (int) (_time)) % 60;
344         int hun= ( (int) (_time   * 100.0)) % 100;
345         
346         if (hr) {
347                 sprintf(str, "%.2d:%.2d:%.2d.%.2d",hr,min,sec,hun);
348         } else {
349                 sprintf(str, "%.2d:%.2d.%.2d",min,sec,hun);
350         }
351         
352         str[11]=0;
353 }
354
355 /* determine the length of a fixed-size string */
356 size_t BLI_strnlen(const char *str, size_t maxlen)
357 {
358         const char *end = memchr(str, '\0', maxlen);
359         return end ? (size_t) (end - str) : maxlen;
360 }
361
362 /* from libswish3, originally called u8_isvalid(),
363  * modified to return the index of the bad character (byte index not utf).
364  * http://svn.swish-e.org/libswish3/trunk/src/libswish3/utf8.c r3044 - campbell */
365
366 /* based on the valid_utf8 routine from the PCRE library by Philip Hazel
367
368    length is in bytes, since without knowing whether the string is valid
369    it's hard to know how many characters there are! */
370
371 static const char trailingBytesForUTF8[256] = {
372         0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
373         0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
374         0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
375         0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
376         0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
377         0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
378         1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
379         2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,3,3,3,3,3,3,3,3,4,4,4,4,5,5,5,5
380 };
381
382 int BLI_utf8_invalid_byte(const char *str, int length)
383 {
384         const unsigned char *p, *pend = (unsigned char*)str + length;
385         unsigned char c;
386         int ab;
387
388         for (p = (unsigned char*)str; p < pend; p++) {
389                 c = *p;
390                 if (c < 128)
391                         continue;
392                 if ((c & 0xc0) != 0xc0)
393                         goto utf8_error;
394                 ab = trailingBytesForUTF8[c];
395                 if (length < ab)
396                         goto utf8_error;
397                 length -= ab;
398
399                 p++;
400                 /* Check top bits in the second byte */
401                 if ((*p & 0xc0) != 0x80)
402                         goto utf8_error;
403
404                 /* Check for overlong sequences for each different length */
405                 switch (ab) {
406                         /* Check for xx00 000x */
407                 case 1:
408                         if ((c & 0x3e) == 0) goto utf8_error;
409                         continue;   /* We know there aren't any more bytes to check */
410
411                         /* Check for 1110 0000, xx0x xxxx */
412                 case 2:
413                         if (c == 0xe0 && (*p & 0x20) == 0) goto utf8_error;
414                         break;
415
416                         /* Check for 1111 0000, xx00 xxxx */
417                 case 3:
418                         if (c == 0xf0 && (*p & 0x30) == 0) goto utf8_error;
419                         break;
420
421                         /* Check for 1111 1000, xx00 0xxx */
422                 case 4:
423                         if (c == 0xf8 && (*p & 0x38) == 0) goto utf8_error;
424                         break;
425
426                         /* Check for leading 0xfe or 0xff,
427                            and then for 1111 1100, xx00 00xx */
428                 case 5:
429                         if (c == 0xfe || c == 0xff ||
430                                 (c == 0xfc && (*p & 0x3c) == 0)) goto utf8_error;
431                         break;
432                 }
433
434                 /* Check for valid bytes after the 2nd, if any; all must start 10 */
435                 while (--ab > 0) {
436                         if ((*(p+1) & 0xc0) != 0x80) goto utf8_error;
437                         p++; /* do this after so we get usable offset - campbell */
438                 }
439         }
440
441         return -1;
442
443 utf8_error:
444
445         return (int)((char *)p - (char *)str) - 1;
446 }
447
448 int BLI_utf8_invalid_strip(char *str, int length)
449 {
450         int bad_char, tot= 0;
451
452         while((bad_char= BLI_utf8_invalid_byte(str, length)) != -1) {
453                 str += bad_char;
454                 length -= bad_char;
455
456                 if(length == 0) {
457                         /* last character bad, strip it */
458                         *str= '\0';
459                         tot++;
460                         break;
461                 }
462                 else {
463                         /* strip, keep looking */
464                         memmove(str, str + 1, length);
465                         tot++;
466                 }
467         }
468
469         return tot;
470 }
471