2003-03-31 Havoc Pennington <hp@redhat.com>
[platform/upstream/dbus.git] / dbus / dbus-string.c
index f19a30d..305b488 100644 (file)
@@ -162,39 +162,24 @@ undo_alignment (DBusRealString *real)
                real->str,
                real->len + 1);
 
-      real->align_offset = 0;
       real->str = real->str - real->align_offset;
+      real->align_offset = 0;
     }
 }
 
 /**
- * Initializes a string. The maximum length may be _DBUS_INT_MAX for
- * no maximum. The string starts life with zero length.
- * The string must eventually be freed with _dbus_string_free().
- *
- * @todo the max length feature is useless, because it looks to the
- * app like out of memory, and the app might try to "recover" - but
- * recovery in this case is impossible, as we can't ever "get more
- * memory" - so should delete the max length feature I think. Well, at
- * least there's a strong caveat that it can only be used when
- * out-of-memory is a permanent fatal error.
- *
- * @todo we could make this init routine not alloc any memory and
- * return void, would simplify a lot of code, however it might
- * complexify things elsewhere because _dbus_string_get_data()
- * etc. could suddenly fail as they'd need to alloc new memory.
+ * Initializes a string. The string starts life with zero length.  The
+ * string must eventually be freed with _dbus_string_free().
  * 
  * @param str memory to hold the string
- * @param max_length the maximum size of the string
- * @returns #TRUE on success */
+ * @returns #TRUE on success, #FALSE if no memory
+ */
 dbus_bool_t
-_dbus_string_init (DBusString *str,
-                   int         max_length)
+_dbus_string_init (DBusString *str)
 {
   DBusRealString *real;
   
   _dbus_assert (str != NULL);
-  _dbus_assert (max_length >= 0);
 
   _dbus_assert (sizeof (DBusString) == sizeof (DBusRealString));
   
@@ -214,9 +199,7 @@ _dbus_string_init (DBusString *str,
   real->len = 0;
   real->str[real->len] = '\0';
   
-  real->max_length = max_length;
-  if (real->max_length > MAX_MAX_LENGTH)
-    real->max_length = MAX_MAX_LENGTH;
+  real->max_length = MAX_MAX_LENGTH;
   real->constant = FALSE;
   real->locked = FALSE;
   real->invalid = FALSE;
@@ -227,6 +210,23 @@ _dbus_string_init (DBusString *str,
   return TRUE;
 }
 
+/* The max length thing is sort of a historical artifact
+ * from a feature that turned out to be dumb; perhaps
+ * we should purge it entirely. The problem with
+ * the feature is that it looks like memory allocation
+ * failure, but is not a transient or resolvable failure.
+ */
+static void
+set_max_length (DBusString *str,
+                int         max_length)
+{
+  DBusRealString *real;
+  
+  real = (DBusRealString*) str;
+
+  real->max_length = max_length;
+}
+
 /**
  * Initializes a constant string. The value parameter is not copied
  * (should be static), and the string may never be modified.
@@ -409,36 +409,28 @@ open_gap (int             len,
  * function on a const string.
  *
  * @param str the string
- * @param data_return place to store the returned data
+ * @returns the data
  */
-void
-_dbus_string_get_data (DBusString        *str,
-                       char             **data_return)
+char*
+_dbus_string_get_data (DBusString *str)
 {
   DBUS_STRING_PREAMBLE (str);
-  _dbus_assert (data_return != NULL);
   
-  *data_return = real->str;
+  return real->str;
 }
 
 /**
  * Gets the raw character buffer from a const string.
  *
- * @todo should return the const char* instead of using an out param;
- * the temporary variable encourages a bug where you use const data
- * after modifying the string and possibly causing a realloc.
- *
  * @param str the string
- * @param data_return location to store returned data
+ * @returns the string data
  */
-void
-_dbus_string_get_const_data (const DBusString  *str,
-                             const char       **data_return)
+const char*
+_dbus_string_get_const_data (const DBusString  *str)
 {
   DBUS_CONST_STRING_PREAMBLE (str);
-  _dbus_assert (data_return != NULL);
   
-  *data_return = real->str;
+  return real->str;
 }
 
 /**
@@ -450,24 +442,22 @@ _dbus_string_get_const_data (const DBusString  *str,
  * string, not at start + len.
  *
  * @param str the string
- * @param data_return location to return the buffer
  * @param start byte offset to return
  * @param len length of segment to return
+ * @returns the string data
  */
-void
+char*
 _dbus_string_get_data_len (DBusString *str,
-                           char      **data_return,
                            int         start,
                            int         len)
 {
   DBUS_STRING_PREAMBLE (str);
-  _dbus_assert (data_return != NULL);
   _dbus_assert (start >= 0);
   _dbus_assert (len >= 0);
   _dbus_assert (start <= real->len);
   _dbus_assert (len <= real->len - start);
   
-  *data_return = real->str + start;
+  return real->str + start;
 }
 
 /**
@@ -478,24 +468,22 @@ _dbus_string_get_data_len (DBusString *str,
  * after modifying the string and possibly causing a realloc.
  * 
  * @param str the string
- * @param data_return location to return the buffer
  * @param start byte offset to return
  * @param len length of segment to return
+ * @returns the string data
  */
-void
+const char*
 _dbus_string_get_const_data_len (const DBusString  *str,
-                                 const char       **data_return,
                                  int                start,
                                  int                len)
 {
   DBUS_CONST_STRING_PREAMBLE (str);
-  _dbus_assert (data_return != NULL);
   _dbus_assert (start >= 0);
   _dbus_assert (len >= 0);
   _dbus_assert (start <= real->len);
   _dbus_assert (len <= real->len - start);
   
-  *data_return = real->str + start;
+  return real->str + start;
 }
 
 /**
@@ -574,6 +562,7 @@ dbus_bool_t
 _dbus_string_steal_data (DBusString        *str,
                          char             **data_return)
 {
+  int old_max_length;
   DBUS_STRING_PREAMBLE (str);
   _dbus_assert (data_return != NULL);
 
@@ -581,8 +570,10 @@ _dbus_string_steal_data (DBusString        *str,
   
   *data_return = real->str;
 
+  old_max_length = real->max_length;
+  
   /* reset the string */
-  if (!_dbus_string_init (str, real->max_length))
+  if (!_dbus_string_init (str))
     {
       /* hrm, put it back then */
       real->str = *data_return;
@@ -591,6 +582,8 @@ _dbus_string_steal_data (DBusString        *str,
       return FALSE;
     }
 
+  real->max_length = old_max_length;
+
   return TRUE;
 }
 
@@ -616,7 +609,6 @@ _dbus_string_steal_data_len (DBusString        *str,
                              int                len)
 {
   DBusString dest;
-  
   DBUS_STRING_PREAMBLE (str);
   _dbus_assert (data_return != NULL);
   _dbus_assert (start >= 0);
@@ -624,16 +616,87 @@ _dbus_string_steal_data_len (DBusString        *str,
   _dbus_assert (start <= real->len);
   _dbus_assert (len <= real->len - start);
 
-  if (!_dbus_string_init (&dest, real->max_length))
+  if (!_dbus_string_init (&dest))
     return FALSE;
 
+  set_max_length (&dest, real->max_length);
+  
   if (!_dbus_string_move_len (str, start, len, &dest, 0))
     {
       _dbus_string_free (&dest);
       return FALSE;
     }
 
-  _dbus_warn ("Broken code in _dbus_string_steal_data_len(), FIXME\n");
+  _dbus_warn ("Broken code in _dbus_string_steal_data_len(), see @todo, FIXME\n");
+  if (!_dbus_string_steal_data (&dest, data_return))
+    {
+      _dbus_string_free (&dest);
+      return FALSE;
+    }
+
+  _dbus_string_free (&dest);
+  return TRUE;
+}
+
+
+/**
+ * Copies the data from the string into a char*
+ *
+ * @param str the string
+ * @param data_return place to return the data
+ * @returns #TRUE on success, #FALSE on no memory
+ */
+dbus_bool_t
+_dbus_string_copy_data (const DBusString  *str,
+                        char             **data_return)
+{
+  DBUS_CONST_STRING_PREAMBLE (str);
+  _dbus_assert (data_return != NULL);
+  
+  *data_return = dbus_malloc (real->len + 1);
+  if (*data_return == NULL)
+    return FALSE;
+
+  memcpy (*data_return, real->str, real->len + 1);
+
+  return TRUE;
+}
+
+/**
+ * Copies a segment of the string into a char*
+ *
+ * @param str the string
+ * @param data_return place to return the data
+ * @param start start index
+ * @param len length to copy
+ * @returns #FALSE if no memory
+ */
+dbus_bool_t
+_dbus_string_copy_data_len (const DBusString  *str,
+                            char             **data_return,
+                            int                start,
+                            int                len)
+{
+  DBusString dest;
+
+  DBUS_CONST_STRING_PREAMBLE (str);
+  _dbus_assert (data_return != NULL);
+  _dbus_assert (start >= 0);
+  _dbus_assert (len >= 0);
+  _dbus_assert (start <= real->len);
+  _dbus_assert (len <= real->len - start);
+
+  if (!_dbus_string_init (&dest))
+    return FALSE;
+
+  set_max_length (&dest, real->max_length);
+
+  if (!_dbus_string_copy_len (str, start, len, &dest, 0))
+    {
+      _dbus_string_free (&dest);
+      return FALSE;
+    }
+
   if (!_dbus_string_steal_data (&dest, data_return))
     {
       _dbus_string_free (&dest);
@@ -1152,7 +1215,9 @@ _dbus_string_replace_len (const DBusString *source,
   return TRUE;
 }
 
-/* Unicode macros from GLib */
+/* Unicode macros and utf8_validate() from GLib Owen Taylor, Havoc
+ * Pennington, and Tom Tromey are the authors and authorized relicense.
+ */
 
 /** computes length and mask of a unicode character
  * @param Char the char
@@ -1233,8 +1298,9 @@ _dbus_string_replace_len (const DBusString *source,
  */
 #define UNICODE_VALID(Char)                   \
     ((Char) < 0x110000 &&                     \
-     ((Char) < 0xD800 || (Char) >= 0xE000) && \
-     (Char) != 0xFFFE && (Char) != 0xFFFF)   
+     (((Char) & 0xFFFFF800) != 0xD800) &&     \
+     ((Char) < 0xFDD0 || (Char) > 0xFDEF) &&  \
+     ((Char) & 0xFFFF) != 0xFFFF)
 
 /**
  * Gets a unicode character from a UTF-8 string. Does no validation;
@@ -1424,6 +1490,7 @@ _dbus_string_find_blank (const DBusString *str,
 
 /**
  * Skips blanks from start, storing the first non-blank in *end
+ * (blank is space or tab).
  *
  * @param str the string
  * @param start where to start
@@ -1457,6 +1524,43 @@ _dbus_string_skip_blank (const DBusString *str,
 }
 
 /**
+ * Skips whitespace from start, storing the first non-whitespace in *end.
+ * (whitespace is space, tab, newline, CR).
+ *
+ * @param str the string
+ * @param start where to start
+ * @param end where to store the first non-whitespace byte index
+ */
+void
+_dbus_string_skip_white (const DBusString *str,
+                         int               start,
+                         int              *end)
+{
+  int i;
+  DBUS_CONST_STRING_PREAMBLE (str);
+  _dbus_assert (start <= real->len);
+  _dbus_assert (start >= 0);
+  
+  i = start;
+  while (i < real->len)
+    {
+      if (!(real->str[i] == ' ' ||
+            real->str[i] == '\n' ||
+            real->str[i] == '\r' ||
+            real->str[i] == '\t'))
+        break;
+      
+      ++i;
+    }
+
+  _dbus_assert (i == real->len || !(real->str[i] == ' ' ||
+                                    real->str[i] == '\t'));
+  
+  if (end)
+    *end = i;
+}
+
+/**
  * Assigns a newline-terminated or \r\n-terminated line from the front
  * of the string to the given dest string. The dest string's previous
  * contents are deleted. If the source string contains no newline,
@@ -2052,7 +2156,7 @@ _dbus_string_base64_decode (const DBusString *source,
   if (source_len == 0)
     return TRUE;
 
-  if (!_dbus_string_init (&result, _DBUS_INT_MAX))
+  if (!_dbus_string_init (&result))
     return FALSE;
 
   pad_count = 0;
@@ -2106,15 +2210,22 @@ _dbus_string_base64_decode (const DBusString *source,
               */
               
               if (pad_count < 1)
-                _dbus_string_append_byte (&result,
-                                          triplet >> 16);
+                {
+                  if (!_dbus_string_append_byte (&result,
+                                                 triplet >> 16))
+                    goto failed;
+                }
               
               if (pad_count < 2)
-                _dbus_string_append_byte (&result,
-                                          (triplet >> 8) & 0xff);              
+                {
+                  if (!_dbus_string_append_byte (&result,
+                                                 (triplet >> 8) & 0xff))
+                    goto failed;
+                }
               
-              _dbus_string_append_byte (&result,
-                                        triplet & 0xff);
+              if (!_dbus_string_append_byte (&result,
+                                             triplet & 0xff))
+                goto failed;
               
               sextet_count = 0;
               pad_count = 0;
@@ -2134,6 +2245,11 @@ _dbus_string_base64_decode (const DBusString *source,
   _dbus_string_free (&result);
 
   return TRUE;
+
+ failed:
+  _dbus_string_free (&result);
+
+  return FALSE;
 }
 
 /**
@@ -2163,12 +2279,12 @@ _dbus_string_hex_encode (const DBusString *source,
   
   _dbus_assert (start <= _dbus_string_get_length (source));
 
-  if (!_dbus_string_init (&result, _DBUS_INT_MAX))
+  if (!_dbus_string_init (&result))
     return FALSE;
 
   retval = FALSE;
   
-  _dbus_string_get_const_data (source, (const char**) &p);
+  p = (const unsigned char*) _dbus_string_get_const_data (source);
   end = p + _dbus_string_get_length (source);
   p += start;
   
@@ -2218,13 +2334,13 @@ _dbus_string_hex_decode (const DBusString *source,
   
   _dbus_assert (start <= _dbus_string_get_length (source));
 
-  if (!_dbus_string_init (&result, _DBUS_INT_MAX))
+  if (!_dbus_string_init (&result))
     return FALSE;
 
   retval = FALSE;
 
   high_bits = TRUE;
-  _dbus_string_get_const_data (source, (const char**) &p);
+  p = (const unsigned char*) _dbus_string_get_const_data (source);
   end = p + _dbus_string_get_length (source);
   p += start;
   
@@ -2376,9 +2492,8 @@ _dbus_string_validate_ascii (const DBusString *str,
  * Checks that the given range of the string is valid UTF-8. If the
  * given range is not entirely contained in the string, returns
  * #FALSE. If the string contains any nul bytes in the given range,
- * returns #FALSE.
- *
- * @todo right now just calls _dbus_string_validate_ascii()
+ * returns #FALSE. If the start and start+len are not on character
+ * boundaries, returns #FALSE.
  *
  * @todo this is inconsistent with most of DBusString in that
  * it allows a start,len range that isn't in the string.
@@ -2393,8 +2508,55 @@ _dbus_string_validate_utf8  (const DBusString *str,
                              int               start,
                              int               len)
 {
-  /* FIXME actually validate UTF-8 */
-  return _dbus_string_validate_ascii (str, start, len);
+  const unsigned char *p;
+  const unsigned char *end;
+  DBUS_CONST_STRING_PREAMBLE (str);
+  _dbus_assert (start >= 0);
+  _dbus_assert (start <= real->len);
+  _dbus_assert (len >= 0);
+
+  if (len > real->len - start)
+    return FALSE;
+  
+  p = real->str + start;
+  end = p + len;
+  
+  while (p < end)
+    {
+      int i, mask = 0, char_len;
+      dbus_unichar_t result;
+      unsigned char c = (unsigned char) *p;
+      
+      UTF8_COMPUTE (c, mask, char_len);
+
+      if (char_len == -1)
+        break;
+
+      /* check that the expected number of bytes exists in the remaining length */
+      if ((end - p) < char_len)
+        break;
+        
+      UTF8_GET (result, p, i, mask, char_len);
+
+      if (UTF8_LENGTH (result) != char_len) /* Check for overlong UTF-8 */
+        break;
+
+      if (result == (dbus_unichar_t)-1)
+        break;
+
+      if (!UNICODE_VALID (result))
+        break;
+      
+      p += char_len;
+    }
+
+  /* See that we covered the entire length if a length was
+   * passed in
+   */
+  if (p != end)
+    return FALSE;
+  else
+    return TRUE;
 }
 
 /**
@@ -2486,13 +2648,13 @@ test_base64_roundtrip (const unsigned char *data,
   if (len < 0)
     len = strlen (data);
   
-  if (!_dbus_string_init (&orig, _DBUS_INT_MAX))
+  if (!_dbus_string_init (&orig))
     _dbus_assert_not_reached ("could not init string");
 
-  if (!_dbus_string_init (&encoded, _DBUS_INT_MAX))
+  if (!_dbus_string_init (&encoded))
     _dbus_assert_not_reached ("could not init string");
   
-  if (!_dbus_string_init (&decoded, _DBUS_INT_MAX))
+  if (!_dbus_string_init (&decoded))
     _dbus_assert_not_reached ("could not init string");
 
   if (!_dbus_string_append_len (&orig, data, len))
@@ -2513,7 +2675,7 @@ test_base64_roundtrip (const unsigned char *data,
               _dbus_string_get_length (&encoded),
               _dbus_string_get_length (&decoded));
       printf ("Original: %s\n", data);
-      _dbus_string_get_const_data (&decoded, &s);
+      s = _dbus_string_get_const_data (&decoded);
       printf ("Decoded: %s\n", s);
       _dbus_assert_not_reached ("original string not the same as string decoded from base64");
     }
@@ -2534,13 +2696,13 @@ test_hex_roundtrip (const unsigned char *data,
   if (len < 0)
     len = strlen (data);
   
-  if (!_dbus_string_init (&orig, _DBUS_INT_MAX))
+  if (!_dbus_string_init (&orig))
     _dbus_assert_not_reached ("could not init string");
 
-  if (!_dbus_string_init (&encoded, _DBUS_INT_MAX))
+  if (!_dbus_string_init (&encoded))
     _dbus_assert_not_reached ("could not init string");
   
-  if (!_dbus_string_init (&decoded, _DBUS_INT_MAX))
+  if (!_dbus_string_init (&decoded))
     _dbus_assert_not_reached ("could not init string");
 
   if (!_dbus_string_append_len (&orig, data, len))
@@ -2561,7 +2723,7 @@ test_hex_roundtrip (const unsigned char *data,
               _dbus_string_get_length (&encoded),
               _dbus_string_get_length (&decoded));
       printf ("Original: %s\n", data);
-      _dbus_string_get_const_data (&decoded, &s);
+      s = _dbus_string_get_const_data (&decoded);
       printf ("Decoded: %s\n", s);
       _dbus_assert_not_reached ("original string not the same as string decoded from base64");
     }
@@ -2639,8 +2801,10 @@ _dbus_string_test (void)
   i = 0;
   while (i < _DBUS_N_ELEMENTS (lens))
     {
-      if (!_dbus_string_init (&str, lens[i]))
+      if (!_dbus_string_init (&str))
         _dbus_assert_not_reached ("failed to init string");
+
+      set_max_length (&str, lens[i]);
       
       test_max_len (&str, lens[i]);
       _dbus_string_free (&str);
@@ -2654,8 +2818,10 @@ _dbus_string_test (void)
     {
       int j;
       
-      if (!_dbus_string_init (&str, lens[i]))
+      if (!_dbus_string_init (&str))
         _dbus_assert_not_reached ("failed to init string");
+
+      set_max_length (&str, lens[i]);
       
       if (!_dbus_string_set_length (&str, lens[i]))
         _dbus_assert_not_reached ("failed to set string length");
@@ -2678,7 +2844,7 @@ _dbus_string_test (void)
     }
 
   /* Test appending data */
-  if (!_dbus_string_init (&str, _DBUS_INT_MAX))
+  if (!_dbus_string_init (&str))
     _dbus_assert_not_reached ("failed to init string");
 
   i = 0;
@@ -2701,7 +2867,7 @@ _dbus_string_test (void)
 
   /* Check steal_data */
   
-  if (!_dbus_string_init (&str, _DBUS_INT_MAX))
+  if (!_dbus_string_init (&str))
     _dbus_assert_not_reached ("failed to init string");
 
   if (!_dbus_string_append (&str, "Hello World"))
@@ -2724,7 +2890,7 @@ _dbus_string_test (void)
 
   i = _dbus_string_get_length (&str);
 
-  if (!_dbus_string_init (&other, _DBUS_INT_MAX))
+  if (!_dbus_string_init (&other))
     _dbus_assert_not_reached ("could not init string");
   
   if (!_dbus_string_move (&str, 0, &other, 0))
@@ -2760,7 +2926,7 @@ _dbus_string_test (void)
 
   i = _dbus_string_get_length (&str);
   
-  if (!_dbus_string_init (&other, _DBUS_INT_MAX))
+  if (!_dbus_string_init (&other))
     _dbus_assert_not_reached ("could not init string");
   
   if (!_dbus_string_copy (&str, 0, &other, 0))
@@ -2790,7 +2956,7 @@ _dbus_string_test (void)
 
   /* Check replace */
 
-  if (!_dbus_string_init (&str, _DBUS_INT_MAX))
+  if (!_dbus_string_init (&str))
     _dbus_assert_not_reached ("failed to init string");
   
   if (!_dbus_string_append (&str, "Hello World"))
@@ -2798,7 +2964,7 @@ _dbus_string_test (void)
 
   i = _dbus_string_get_length (&str);
   
-  if (!_dbus_string_init (&other, _DBUS_INT_MAX))
+  if (!_dbus_string_init (&other))
     _dbus_assert_not_reached ("could not init string");
   
   if (!_dbus_string_replace_len (&str, 0, _dbus_string_get_length (&str),
@@ -2835,7 +3001,7 @@ _dbus_string_test (void)
   
   /* Check append/get unichar */
   
-  if (!_dbus_string_init (&str, _DBUS_INT_MAX))
+  if (!_dbus_string_init (&str))
     _dbus_assert_not_reached ("failed to init string");
 
   ch = 0;
@@ -2851,7 +3017,7 @@ _dbus_string_test (void)
 
   /* Check insert/set/get byte */
   
-  if (!_dbus_string_init (&str, _DBUS_INT_MAX))
+  if (!_dbus_string_init (&str))
     _dbus_assert_not_reached ("failed to init string");
 
   if (!_dbus_string_append (&str, "Hello"))
@@ -2888,7 +3054,7 @@ _dbus_string_test (void)
   
   /* Check append/parse int/double */
   
-  if (!_dbus_string_init (&str, _DBUS_INT_MAX))
+  if (!_dbus_string_init (&str))
     _dbus_assert_not_reached ("failed to init string");
 
   if (!_dbus_string_append_int (&str, 27))
@@ -2904,7 +3070,7 @@ _dbus_string_test (void)
 
   _dbus_string_free (&str);
   
-  if (!_dbus_string_init (&str, _DBUS_INT_MAX))
+  if (!_dbus_string_init (&str))
     _dbus_assert_not_reached ("failed to init string");
   
   if (!_dbus_string_append_double (&str, 50.3))
@@ -2921,7 +3087,7 @@ _dbus_string_test (void)
   _dbus_string_free (&str);
 
   /* Test find */
-  if (!_dbus_string_init (&str, _DBUS_INT_MAX))
+  if (!_dbus_string_init (&str))
     _dbus_assert_not_reached ("failed to init string");
 
   if (!_dbus_string_append (&str, "Hello"))