Code Cleanup: style and redundant casts
[blender.git] / source / blender / blenlib / intern / string.c
index b2245226a3cca5f166d64799f5539c18c3c452ea..6b738fab45e281a167ec5873918fd33fe755d76c 100644 (file)
 #include "BLI_dynstr.h"
 #include "BLI_string.h"
 
+#include "BLI_utildefines.h"
+
+#ifdef __GNUC__
+#  pragma GCC diagnostic error "-Wsign-conversion"
+#endif
+
+// #define DEBUG_STRSIZE
+
+/**
+ * Duplicates the first \a len bytes of cstring \a str
+ * into a newly mallocN'd string and returns it. \a str
+ * is assumed to be at least len bytes long.
+ *
+ * \param str The string to be duplicated
+ * \param len The number of bytes to duplicate
+ * \retval Returns the duplicated string
+ */
 char *BLI_strdupn(const char *str, const size_t len)
 {
-       char *n= MEM_mallocN(len+1, "strdup");
+       char *n = MEM_mallocN(len + 1, "strdup");
        memcpy(n, str, len);
-       n[len]= '\0';
+       n[len] = '\0';
        
        return n;
 }
+
+/**
+ * Duplicates the cstring \a str into a newly mallocN'd
+ * string and returns it.
+ *
+ * \param str The string to be duplicated
+ * \retval Returns the duplicated string
+ */
 char *BLI_strdup(const char *str)
 {
        return BLI_strdupn(str, strlen(str));
 }
 
-char *BLI_strdupcat(const char *str1, const char *str2)
+/**
+ * Appends the two strings, and returns new mallocN'ed string
+ * \param str1 first string for copy
+ * \param str2 second string for append
+ * \retval Returns dst
+ */
+char *BLI_strdupcat(const char *__restrict str1, const char *__restrict str2)
 {
-       size_t len;
-       char *n;
+       /* include the NULL terminator of str2 only */
+       const size_t str1_len = strlen(str1);
+       const size_t str2_len = strlen(str2) + 1;
+       char *str, *s;
        
-       len= strlen(str1)+strlen(str2);
-       n= MEM_mallocN(len+1, "strdupcat");
-       strcpy(n, str1);
-       strcat(n, str2);
-       
-       return n;
+       str = MEM_mallocN(str1_len + str2_len, "strdupcat");
+       s = str;
+
+       memcpy(s, str1, str1_len); s += str1_len;
+       memcpy(s, str2, str2_len);
+
+       return str;
 }
 
-char *BLI_strncpy(char *dst, const char *src, const size_t maxncpy)
+/**
+ * Like strncpy but ensures dst is always
+ * '\0' terminated.
+ *
+ * \param dst Destination for copy
+ * \param src Source string to copy
+ * \param maxncpy Maximum number of characters to copy (generally
+ * the size of dst)
+ * \retval Returns dst
+ */
+char *BLI_strncpy(char *__restrict dst, const char *__restrict src, const size_t maxncpy)
 {
-       size_t srclen= strlen(src);
-       size_t cpylen= (srclen>(maxncpy-1))?(maxncpy-1):srclen;
-       
-       memcpy(dst, src, cpylen);
-       dst[cpylen]= '\0';
-       
+       size_t srclen = BLI_strnlen(src, maxncpy - 1);
+       BLI_assert(maxncpy != 0);
+
+#ifdef DEBUG_STRSIZE
+       memset(dst, 0xff, sizeof(*dst) * maxncpy);
+#endif
+
+       memcpy(dst, src, srclen);
+       dst[srclen] = '\0';
        return dst;
 }
 
-size_t BLI_snprintf(char *buffer, size_t count, const char *format, ...)
+/**
+ * Like strncpy but ensures dst is always
+ * '\0' terminated.
+ *
+ * \note This is a duplicate of #BLI_strncpy that returns bytes copied.
+ * And is a drop in replacement for 'snprintf(str, sizeof(str), "%s", arg);'
+ *
+ * \param dst Destination for copy
+ * \param src Source string to copy
+ * \param maxncpy Maximum number of characters to copy (generally
+ * the size of dst)
+ * \retval The number of bytes copied (The only difference from BLI_strncpy).
+ */
+size_t BLI_strncpy_rlen(char *__restrict dst, const char *__restrict src, const size_t maxncpy)
+{
+       size_t srclen = BLI_strnlen(src, maxncpy - 1);
+       BLI_assert(maxncpy != 0);
+
+#ifdef DEBUG_STRSIZE
+       memset(dst, 0xff, sizeof(*dst) * maxncpy);
+#endif
+
+       memcpy(dst, src, srclen);
+       dst[srclen] = '\0';
+       return srclen;
+}
+
+size_t BLI_strcpy_rlen(char *__restrict dst, const char *__restrict src)
+{
+       size_t srclen = strlen(src);
+       memcpy(dst, src, srclen + 1);
+       return srclen;
+}
+
+/**
+ * Portable replacement for #vsnprintf
+ */
+size_t BLI_vsnprintf(char *__restrict buffer, size_t maxncpy, const char *__restrict format, va_list arg)
 {
        size_t n;
-       va_list arg;
 
-       va_start(arg, format);
-       n = vsnprintf(buffer, count, format, arg);
-       
-       if (n != -1 && n < count) {
+       BLI_assert(buffer != NULL);
+       BLI_assert(maxncpy > 0);
+       BLI_assert(format != NULL);
+
+       n = (size_t)vsnprintf(buffer, maxncpy, format, arg);
+
+       if (n != -1 && n < maxncpy) {
                buffer[n] = '\0';
        }
        else {
-               buffer[count-1] = '\0';
+               buffer[maxncpy - 1] = '\0';
        }
-       
+
+       return n;
+}
+
+/**
+ * Portable replacement for #snprintf
+ */
+size_t BLI_snprintf(char *__restrict dst, size_t maxncpy, const char *__restrict format, ...)
+{
+       size_t n;
+       va_list arg;
+
+#ifdef DEBUG_STRSIZE
+       memset(dst, 0xff, sizeof(*dst) * maxncpy);
+#endif
+
+       va_start(arg, format);
+       n = BLI_vsnprintf(dst, maxncpy, format, arg);
        va_end(arg);
+
        return n;
 }
 
-char *BLI_sprintfN(const char *format, ...)
+/**
+ * Print formatted string into a newly #MEM_mallocN'd string
+ * and return it.
+ */
+char *BLI_sprintfN(const char *__restrict format, ...)
 {
        DynStr *ds;
        va_list arg;
        char *n;
 
+       BLI_assert(format != NULL);
+
        va_start(arg, format);
 
-       ds= BLI_dynstr_new();
+       ds = BLI_dynstr_new();
        BLI_dynstr_vappendf(ds, format, arg);
-       n= BLI_dynstr_get_cstring(ds);
+       n = BLI_dynstr_get_cstring(ds);
        BLI_dynstr_free(ds);
 
        va_end(arg);
@@ -121,21 +231,25 @@ char *BLI_sprintfN(const char *format, ...)
  * TODO: support more fancy string escaping. current code is primitive
  *    this basically is an ascii version of PyUnicode_EncodeUnicodeEscape()
  *    which is a useful reference. */
-size_t BLI_strescape(char *dst, const char *src, const size_t maxlen)
+size_t BLI_strescape(char *__restrict dst, const char *__restrict src, const size_t maxncpy)
 {
-       size_t len= 0;
-       while (len < maxlen) {
+       size_t len = 0;
+
+       BLI_assert(maxncpy != 0);
+
+       while (len < maxncpy) {
                switch (*src) {
                        case '\0':
                                goto escape_finish;
                        case '\\':
                        case '"':
+                               /* fall-through */
 
-                               /* less common but should also be support */
+                       /* less common but should also be support */
                        case '\t':
                        case '\n':
                        case '\r':
-                               if (len + 1 <  maxlen) {
+                               if (len + 1 < maxncpy) {
                                        *dst++ = '\\';
                                        len++;
                                }
@@ -143,9 +257,10 @@ size_t BLI_strescape(char *dst, const char *src, const size_t maxlen)
                                        /* not enough space to escape */
                                        break;
                                }
-                               /* intentionally pass through */
+                               /* fall-through */
                        default:
                                *dst = *src;
+                               break;
                }
                dst++;
                src++;
@@ -154,98 +269,103 @@ size_t BLI_strescape(char *dst, const char *src, const size_t maxlen)
 
 escape_finish:
 
-       *dst= '\0';
+       *dst = '\0';
 
        return len;
 }
 
-
-/* Makes a copy of the text within the "" that appear after some text 'blahblah'
+/**
+ * Makes a copy of the text within the "" that appear after some text 'blahblah'
  * i.e. for string 'pose["apples"]' with prefix 'pose[', it should grab "apples"
- * 
- *     - str: is the entire string to chop
- *     - prefix: is the part of the string to leave out 
  *
- * Assume that the strings returned must be freed afterwards, and that the inputs will contain 
+ * - str: is the entire string to chop
+ * - prefix: is the part of the string to leave out
+ *
+ * Assume that the strings returned must be freed afterwards, and that the inputs will contain
  * data we want...
+ *
+ * \return the offset and a length so as to avoid doing an allocation.
  */
-char *BLI_getQuotedStr (const char *str, const char *prefix)
+char *BLI_str_quoted_substrN(const char *__restrict str, const char *__restrict prefix)
 {
        size_t prefixLen = strlen(prefix);
        char *startMatch, *endMatch;
        
        /* get the starting point (i.e. where prefix starts, and add prefixLen+1 to it to get be after the first " */
-       startMatch= strstr(str, prefix) + prefixLen + 1;
-       
-       /* get the end point (i.e. where the next occurance of " is after the starting point) */
-       endMatch= strchr(startMatch, '"'); // "  NOTE: this comment here is just so that my text editor still shows the functions ok...
-       
-       /* return the slice indicated */
-       return BLI_strdupn(startMatch, (size_t)(endMatch-startMatch));
+       startMatch = strstr(str, prefix) + prefixLen + 1;
+       if (startMatch) {
+               /* get the end point (i.e. where the next occurance of " is after the starting point) */
+               endMatch = strchr(startMatch, '"'); /* "  NOTE: this comment here is just so that my text editor still shows the functions ok... */
+               
+               if (endMatch)
+                       /* return the slice indicated */
+                       return BLI_strdupn(startMatch, (size_t)(endMatch - startMatch));
+       }
+       return BLI_strdupn("", 0);
 }
 
-/* Replaces all occurrences of oldText with newText in str, returning a new string that doesn't 
- * contain the 'replaced' occurrences.
+/**
+ * string with all instances of substr_old replaced with substr_new,
+ * Returns a copy of the cstring \a str into a newly mallocN'd
+ * and returns it.
+ *
+ * \note A rather wasteful string-replacement utility, though this shall do for now...
+ * Feel free to replace this with an even safe + nicer alternative
+ *
+ * \param str The string to replace occurrences of substr_old in
+ * \param substr_old The text in the string to find and replace
+ * \param substr_new The text in the string to find and replace
+ * \retval Returns the duplicated string
  */
-// A rather wasteful string-replacement utility, though this shall do for now...
-// Feel free to replace this with an even safe + nicer alternative 
-char *BLI_replacestr(char *str, const char *oldText, const char *newText)
+char *BLI_replacestrN(const char *__restrict str, const char *__restrict substr_old, const char *__restrict substr_new)
 {
-       DynStr *ds= NULL;
-       size_t lenOld= strlen(oldText);
-       char *match;
-       
-       /* sanity checks */
-       if ((str == NULL) || (str[0]==0))
-               return NULL;
-       else if ((oldText == NULL) || (newText == NULL) || (oldText[0]==0))
-               return BLI_strdup(str);
-       
+       DynStr *ds = NULL;
+       size_t len_old = strlen(substr_old);
+       const char *match;
+
+       BLI_assert(substr_old[0] != '\0');
+
        /* while we can still find a match for the old substring that we're searching for, 
         * keep dicing and replacing
         */
-       while ( (match = strstr(str, oldText)) ) {
+       while ((match = strstr(str, substr_old))) {
                /* the assembly buffer only gets created when we actually need to rebuild the string */
                if (ds == NULL)
-                       ds= BLI_dynstr_new();
+                       ds = BLI_dynstr_new();
                        
                /* if the match position does not match the current position in the string, 
                 * copy the text up to this position and advance the current position in the string
                 */
                if (str != match) {
-                       /* replace the token at the 'match' position with \0 so that the copied string will be ok,
-                        * add the segment of the string from str to match to the buffer, then restore the value at match
+                       /* add the segment of the string from str to match to the buffer, then restore the value at match
                         */
-                       match[0]= 0;
-                       BLI_dynstr_append(ds, str);
-                       match[0]= oldText[0];
+                       BLI_dynstr_nappend(ds, str, (match - str));
                        
                        /* now our current position should be set on the start of the match */
-                       str= match;
+                       str = match;
                }
                
                /* add the replacement text to the accumulation buffer */
-               BLI_dynstr_append(ds, newText);
+               BLI_dynstr_append(ds, substr_new);
                
                /* advance the current position of the string up to the end of the replaced segment */
-               str += lenOld;
+               str += len_old;
        }
        
        /* finish off and return a new string that has had all occurrences of */
        if (ds) {
-               char *newStr;
+               char *str_new;
                
                /* add what's left of the string to the assembly buffer 
-                *      - we've been adjusting str to point at the end of the replaced segments
+                * - we've been adjusting str to point at the end of the replaced segments
                 */
-               if (str != NULL)
-                       BLI_dynstr_append(ds, str);
+               BLI_dynstr_append(ds, str);
                
                /* convert to new c-string (MEM_malloc'd), and free the buffer */
-               newStr= BLI_dynstr_get_cstring(ds);
+               str_new = BLI_dynstr_get_cstring(ds);
                BLI_dynstr_free(ds);
                
-               return newStr;
+               return str_new;
        }
        else {
                /* just create a new copy of the entire string - we avoid going through the assembly buffer 
@@ -255,25 +375,32 @@ char *BLI_replacestr(char *str, const char *oldText, const char *newText)
        }
 } 
 
+/**
+ * Compare two strings without regard to case.
+ *
+ * \retval True if the strings are equal, false otherwise.
+ */
 int BLI_strcaseeq(const char *a, const char *b) 
 {
-       return (BLI_strcasecmp(a, b)==0);
+       return (BLI_strcasecmp(a, b) == 0);
 }
 
-/* strcasestr not available in MSVC */
+/**
+ * Portable replacement for #strcasestr (not available in MSVC)
+ */
 char *BLI_strcasestr(const char *s, const char *find)
 {
        register char c, sc;
        register size_t len;
        
        if ((c = *find++) != 0) {
-               c= tolower(c);
+               c = tolower(c);
                len = strlen(find);
                do {
                        do {
                                if ((sc = *s++) == 0)
                                        return (NULL);
-                               sc= tolower(sc);
+                               sc = tolower(sc);
                        } while (sc != c);
                } while (BLI_strncasecmp(s, find, len) != 0);
                s--;
@@ -284,19 +411,20 @@ char *BLI_strcasestr(const char *s, const char *find)
 
 int BLI_strcasecmp(const char *s1, const char *s2)
 {
-       int i;
+       register int i;
+       register char c1, c2;
 
-       for (i=0; ; i++) {
-               char c1 = tolower(s1[i]);
-               char c2 = tolower(s2[i]);
+       for (i = 0;; i++) {
+               c1 = tolower(s1[i]);
+               c2 = tolower(s2[i]);
 
-               if (c1<c2) {
+               if (c1 < c2) {
                        return -1;
                }
-               else if (c1>c2) {
+               else if (c1 > c2) {
                        return 1;
                }
-               else if (c1==0) {
+               else if (c1 == 0) {
                        break;
                }
        }
@@ -306,19 +434,20 @@ int BLI_strcasecmp(const char *s1, const char *s2)
 
 int BLI_strncasecmp(const char *s1, const char *s2, size_t len)
 {
-       int i;
+       register size_t i;
+       register char c1, c2;
 
-       for (i=0; i<len; i++) {
-               char c1 = tolower(s1[i]);
-               char c2 = tolower(s2[i]);
+       for (i = 0; i < len; i++) {
+               c1 = tolower(s1[i]);
+               c2 = tolower(s2[i]);
 
-               if (c1<c2) {
+               if (c1 < c2) {
                        return -1;
                }
-               else if (c1>c2) {
+               else if (c1 > c2) {
                        return 1;
                }
-               else if (c1==0) {
+               else if (c1 == 0) {
                        break;
                }
        }
@@ -326,102 +455,171 @@ int BLI_strncasecmp(const char *s1, const char *s2, size_t len)
        return 0;
 }
 
+/* compare number on the left size of the string */
+static int left_number_strcmp(const char *s1, const char *s2, int *tiebreaker)
+{
+       const char *p1 = s1, *p2 = s2;
+       int numdigit, numzero1, numzero2;
+
+       /* count and skip leading zeros */
+       for (numzero1 = 0; *p1 && (*p1 == '0'); numzero1++)
+               p1++;
+       for (numzero2 = 0; *p2 && (*p2 == '0'); numzero2++)
+               p2++;
+
+       /* find number of consecutive digits */
+       for (numdigit = 0; ; numdigit++) {
+               if (isdigit(*(p1 + numdigit)) && isdigit(*(p2 + numdigit)))
+                       continue;
+               else if (isdigit(*(p1 + numdigit)))
+                       return 1; /* s2 is bigger */
+               else if (isdigit(*(p2 + numdigit)))
+                       return -1; /* s1 is bigger */
+               else
+                       break;
+       }
+
+       /* same number of digits, compare size of number */
+       if (numdigit > 0) {
+               int compare = (int)strncmp(p1, p2, (size_t)numdigit);
+
+               if (compare != 0)
+                       return compare;
+       }
+
+       /* use number of leading zeros as tie breaker if still equal */
+       if (*tiebreaker == 0) {
+               if (numzero1 > numzero2)
+                       *tiebreaker = 1;
+               else if (numzero1 < numzero2)
+                       *tiebreaker = -1;
+       }
+
+       return 0;
+}
+
 /* natural string compare, keeping numbers in order */
 int BLI_natstrcmp(const char *s1, const char *s2)
 {
-       int d1= 0, d2= 0;
+       register int d1 = 0, d2 = 0;
+       register char c1, c2;
+       int tiebreaker = 0;
 
-       /* if both chars are numeric, to a strtol().
+       /* if both chars are numeric, to a left_number_strcmp().
         * then increase string deltas as long they are 
         * numeric, else do a tolower and char compare */
 
        while (1) {
-               char c1 = tolower(s1[d1]);
-               char c2 = tolower(s2[d2]);
+               c1 = tolower(s1[d1]);
+               c2 = tolower(s2[d2]);
                
-               if ( isdigit(c1) && isdigit(c2) ) {
-                       int val1, val2;
-                       
-                       val1= (int)strtol(s1+d1, (char **)NULL, 10);
-                       val2= (int)strtol(s2+d2, (char **)NULL, 10);
+               if (isdigit(c1) && isdigit(c2)) {
+                       int numcompare = left_number_strcmp(s1 + d1, s2 + d2, &tiebreaker);
                        
-                       if (val1<val2) {
-                               return -1;
-                       }
-                       else if (val1>val2) {
-                               return 1;
-                       }
+                       if (numcompare != 0)
+                               return numcompare;
+
                        d1++;
-                       while ( isdigit(s1[d1]) )
+                       while (isdigit(s1[d1]))
                                d1++;
                        d2++;
-                       while ( isdigit(s2[d2]) )
+                       while (isdigit(s2[d2]))
                                d2++;
                        
                        c1 = tolower(s1[d1]);
                        c2 = tolower(s2[d2]);
                }
        
-               /* first check for '.' so "foo.bar" comes before "foo 1.bar" */ 
-               if (c1=='.' && c2!='.')
+               /* first check for '.' so "foo.bar" comes before "foo 1.bar" */
+               if (c1 == '.' && c2 != '.')
                        return -1;
-               if (c1!='.' && c2=='.')
+               if (c1 != '.' && c2 == '.')
                        return 1;
-               else if (c1<c2) {
+               else if (c1 < c2) {
                        return -1;
                }
-               else if (c1>c2) {
+               else if (c1 > c2) {
                        return 1;
                }
-               else if (c1==0) {
+               else if (c1 == 0) {
                        break;
                }
                d1++;
                d2++;
        }
-       return 0;
+       return tiebreaker;
 }
 
-void BLI_timestr(double _time, char *str)
+void BLI_timestr(double _time, char *str, size_t maxlen)
 {
        /* format 00:00:00.00 (hr:min:sec) string has to be 12 long */
-       int  hr= ( (int)  _time) / (60*60);
-       int min= (((int)  _time) / 60 ) % 60;
-       int sec= ( (int) (_time)) % 60;
-       int hun= ( (int) (_time   * 100.0)) % 100;
-       
+       int  hr = ( (int)  _time) / (60 * 60);
+       int min = (((int)  _time) / 60 ) % 60;
+       int sec = ( (int)  _time) % 60;
+       int hun = ( (int) (_time   * 100.0)) % 100;
+
        if (hr) {
-               sprintf(str, "%.2d:%.2d:%.2d.%.2d",hr,min,sec,hun);
+               BLI_snprintf(str, maxlen, "%.2d:%.2d:%.2d.%.2d", hr, min, sec, hun);
        }
        else {
-               sprintf(str, "%.2d:%.2d.%.2d",min,sec,hun);
+               BLI_snprintf(str, maxlen, "%.2d:%.2d.%.2d", min, sec, hun);
        }
-       
-       str[11]=0;
 }
 
 /* determine the length of a fixed-size string */
-size_t BLI_strnlen(const char *str, size_t maxlen)
+size_t BLI_strnlen(const char *s, const size_t maxlen)
 {
-       const char *end = memchr(str, '\0', maxlen);
-       return end ? (size_t) (end - str) : maxlen;
+       size_t len;
+
+       for (len = 0; len < maxlen; len++, s++) {
+               if (!*s)
+                       break;
+       }
+       return len;
 }
 
-void BLI_ascii_strtolower(char *str, int len)
+void BLI_ascii_strtolower(char *str, const size_t len)
 {
-       int i;
+       size_t i;
 
-       for (i=0; i<len; i++)
+       for (i = 0; (i < len) && str[i]; i++)
                if (str[i] >= 'A' && str[i] <= 'Z')
                        str[i] += 'a' - 'A';
 }
 
-void BLI_ascii_strtoupper(char *str, int len)
+void BLI_ascii_strtoupper(char *str, const size_t len)
 {
-       int i;
+       size_t i;
 
-       for (i=0; i<len; i++)
+       for (i = 0; (i < len) && str[i]; i++)
                if (str[i] >= 'a' && str[i] <= 'z')
                        str[i] -= 'a' - 'A';
 }
 
+/**
+ * Strip trailing zeros from a float, eg:
+ *   0.0000 -> 0.0
+ *   2.0010 -> 2.001
+ *
+ * \param str
+ * \param pad
+ * \return The number of zeto's stripped.
+ */
+int BLI_str_rstrip_float_zero(char *str, const char pad)
+{
+       char *p = strchr(str, '.');
+       int totstrip = 0;
+       if (p) {
+               char *end_p;
+               p++;  /* position at first decimal place */
+               end_p = p + (strlen(p) - 1);  /* position at last character */
+               if (end_p > p) {
+                       while (end_p != p && *end_p == '0') {
+                               *end_p = pad;
+                               end_p--;
+                       }
+               }
+       }
+
+       return totstrip;
+}