2003-02-01 Havoc Pennington <hp@pobox.com>
authorHavoc Pennington <hp@redhat.com>
Sat, 1 Feb 2003 04:58:16 +0000 (04:58 +0000)
committerHavoc Pennington <hp@redhat.com>
Sat, 1 Feb 2003 04:58:16 +0000 (04:58 +0000)
* dbus/dbus-break-loader.c (main): new program to find messages
that break the loader.

* dbus/dbus-sysdeps.c (_dbus_string_append_uint): new function
* dbus/dbus-sysdeps.c (_dbus_string_save_to_file): new function

* dbus/dbus-string.c (_dbus_string_set_byte): new

ChangeLog
dbus/Makefile.am
dbus/dbus-break-loader.c [new file with mode: 0644]
dbus/dbus-marshal.c
dbus/dbus-message.c
dbus/dbus-string.c
dbus/dbus-string.h
dbus/dbus-sysdeps.c
dbus/dbus-sysdeps.h
dbus/dbus-test.h

index d758468..20c98a4 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,13 @@
+2003-02-01  Havoc Pennington  <hp@pobox.com>
+
+       * dbus/dbus-break-loader.c (main): new program to find messages
+       that break the loader.
+
+       * dbus/dbus-sysdeps.c (_dbus_string_append_uint): new function
+       * dbus/dbus-sysdeps.c (_dbus_string_save_to_file): new function
+
+       * dbus/dbus-string.c (_dbus_string_set_byte): new
+       
 2003-01-31  Havoc Pennington  <hp@pobox.com>
 
        * dbus/dbus-message.c: refactor the test code to be more general, 
index 98090a6..4ffe7e2 100644 (file)
@@ -85,12 +85,16 @@ libdbus_1_la_LIBADD=  $(DBUS_CLIENT_LIBS) libdbus-convenience.la
 ## convention for internal symbols)
 libdbus_1_la_LDFLAGS= -export-symbols-regex "^[^_].*"
 
+## FIXME it would be less annoying when hacking if we didn't have 
+## to relink these test binaries, so moving them to the test/* 
+## subdir would be nice.
+
 ## note that TESTS has special meaning (stuff to use in make check)
 ## so if adding tests not to be run in make check, don't add them to 
 ## TESTS
 if DBUS_BUILD_TESTS
 TESTS_ENVIRONMENT=DBUS_TEST_DATA=$(top_srcdir)/test/data
-TESTS=dbus-test
+TESTS=dbus-test dbus-break-loader
 else
 TESTS=
 endif
@@ -103,3 +107,8 @@ dbus_test_SOURCES=                          \
        dbus-test-main.c
 
 dbus_test_LDADD= $(DBUS_CLIENT_LIBS) libdbus-convenience.la libdbus-1.la
+
+dbus_break_loader_SOURCES=                     \
+       dbus-break-loader.c
+
+dbus_break_loader_LDADD= $(DBUS_CLIENT_LIBS) libdbus-convenience.la libdbus-1.la
\ No newline at end of file
diff --git a/dbus/dbus-break-loader.c b/dbus/dbus-break-loader.c
new file mode 100644 (file)
index 0000000..941b7e5
--- /dev/null
@@ -0,0 +1,539 @@
+/* -*- mode: C; c-file-style: "gnu" -*- */
+/* dbus-break-loader.c  Program to find byte streams that break the message loader
+ *
+ * Copyright (C) 2003  Red Hat Inc.
+ *
+ * Licensed under the Academic Free License version 1.2
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include "dbus-types.h"
+#include "dbus-test.h"
+#include "dbus-internals.h"
+#include "dbus-sysdeps.h"
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <sys/wait.h>
+#include <string.h>
+
+static DBusString failure_dir;
+static int total_attempts;
+static int failures_this_iteration;
+
+static int
+random_int_in_range (int start,
+                     int end)
+{
+  /* such elegant math */
+  double gap;
+  double v_double;
+  int v;
+
+  if (start == end)
+    return start;
+
+  _dbus_assert (end > start);
+  
+  gap = end - start - 1; /* -1 to not include "end" */
+  v_double = ((double)start) + (((double)rand ())/RAND_MAX) * gap;
+  if (v_double < 0.0)
+    v = (v_double - 0.5);
+  else
+    v = (v_double + 0.5);
+  
+  if (v < start)
+    {
+      fprintf (stderr, "random_int_in_range() generated %d for range [%d,%d)\n",
+               v, start, end);
+      v = start;
+    }
+  else if (v >= end)
+    {
+      fprintf (stderr, "random_int_in_range() generated %d for range [%d,%d)\n",
+               v, start, end);
+      v = end - 1;
+    }
+
+  /* printf ("  %d of [%d,%d)\n", v, start, end); */
+  
+  return v;
+}
+
+static dbus_bool_t
+try_mutated_data (const DBusString *data)
+{
+  int pid;
+
+  total_attempts += 1;
+  /* printf ("  attempt %d\n", total_attempts); */
+  
+  pid = fork ();
+
+  if (pid < 0)
+    {
+      fprintf (stderr, "fork() failed: %s\n",
+               strerror (errno));
+      exit (1);
+      return FALSE;
+    }
+
+  if (pid == 0)
+    {
+      /* Child, try loading the data */
+      if (!dbus_internal_do_not_use_try_message_data (data, _DBUS_MESSAGE_UNKNOWN))
+        exit (1);
+      else
+        exit (0);
+    }
+  else
+    {
+      /* Parent, wait for child */
+      int status;
+      DBusString filename;
+      dbus_bool_t failed;
+
+      if (waitpid (pid, &status, 0) < 0)
+        {
+          fprintf (stderr, "waitpid() failed: %s\n", strerror (errno));
+          exit (1);
+          return FALSE;
+        }
+
+      failed = FALSE;
+
+      if (!_dbus_string_init (&filename, _DBUS_INT_MAX) ||
+          !_dbus_string_copy (&failure_dir, 0,
+                              &filename, 0) ||
+          !_dbus_string_append_byte (&filename, '/'))
+        {
+          fprintf (stderr, "out of memory\n");
+          exit (1);
+        }
+
+      _dbus_string_append_int (&filename, total_attempts);
+
+      if (WIFEXITED (status))
+        {
+          if (WEXITSTATUS (status) != 0)
+            {
+              _dbus_string_append (&filename, "exited-");
+              _dbus_string_append_int (&filename, WEXITSTATUS (status));
+              failed = TRUE;
+            }
+        }
+      else if (WIFSIGNALED (status))
+        {
+          _dbus_string_append (&filename, "signaled-");
+          _dbus_string_append_int (&filename, WTERMSIG (status));
+          failed = TRUE;
+        }
+
+      if (failed)
+        {
+          const char *filename_c;
+          DBusResultCode result;
+
+          _dbus_string_get_const_data (&filename, &filename_c);
+          printf ("Child failed, writing %s\n",
+                  filename_c);
+
+          result = _dbus_string_save_to_file (data, &filename);
+
+          if (result != DBUS_RESULT_SUCCESS)
+            {
+              fprintf (stderr, "Failed to save failed message data: %s\n",
+                       dbus_result_to_string (result));
+              exit (1); /* so we can see the seed that was printed out */
+            }
+
+          failures_this_iteration += 1;
+
+          return FALSE;
+        }
+      else
+        return TRUE;
+    }
+
+  _dbus_assert_not_reached ("should not be reached");
+  return TRUE;
+}
+
+static void
+randomly_shorten_or_lengthen (const DBusString *orig_data,
+                              DBusString       *mutated)
+{
+  int delta;
+
+  if (orig_data != mutated)
+    {
+      _dbus_string_set_length (mutated, 0);
+      
+      if (!_dbus_string_copy (orig_data, 0, mutated, 0))
+        _dbus_assert_not_reached ("out of mem");
+    }
+
+  if (_dbus_string_get_length (mutated) == 0)
+    delta = random_int_in_range (0, 10);
+  else
+    delta = random_int_in_range (- _dbus_string_get_length (mutated),
+                                 _dbus_string_get_length (mutated) * 3);
+  
+  if (delta < 0)
+    _dbus_string_shorten (mutated, - delta);
+  else if (delta > 0)
+    {
+      int i = 0;
+
+      i = _dbus_string_get_length (mutated);
+      if (!_dbus_string_lengthen (mutated, delta))
+        _dbus_assert_not_reached ("couldn't lengthen string");
+
+      while (i < _dbus_string_get_length (mutated))
+        {
+          _dbus_string_set_byte (mutated,
+                                 i,
+                                 random_int_in_range (0, 256));
+          ++i;
+        }
+    }
+}
+
+static void
+randomly_change_one_byte (const DBusString *orig_data,
+                          DBusString       *mutated)
+{
+  int i;
+
+  if (orig_data != mutated)
+    {
+      _dbus_string_set_length (mutated, 0);
+      
+      if (!_dbus_string_copy (orig_data, 0, mutated, 0))
+        _dbus_assert_not_reached ("out of mem");
+    }
+
+  if (_dbus_string_get_length (mutated) == 0)
+    return;
+  
+  i = random_int_in_range (0, _dbus_string_get_length (mutated));
+
+  _dbus_string_set_byte (mutated, i,
+                         random_int_in_range (0, 256));
+}
+
+static void
+randomly_remove_one_byte (const DBusString *orig_data,
+                          DBusString       *mutated)
+{
+  int i;
+
+  if (orig_data != mutated)
+    {
+      _dbus_string_set_length (mutated, 0);
+      
+      if (!_dbus_string_copy (orig_data, 0, mutated, 0))
+        _dbus_assert_not_reached ("out of mem");
+    }
+
+  if (_dbus_string_get_length (mutated) == 0)
+    return;
+  
+  i = random_int_in_range (0, _dbus_string_get_length (mutated));
+
+  _dbus_string_delete (mutated, i, 1);
+}
+
+
+static void
+randomly_add_one_byte (const DBusString *orig_data,
+                       DBusString       *mutated)
+{
+  int i;
+
+  if (orig_data != mutated)
+    {
+      _dbus_string_set_length (mutated, 0);
+      
+      if (!_dbus_string_copy (orig_data, 0, mutated, 0))
+        _dbus_assert_not_reached ("out of mem");
+    }
+
+  i = random_int_in_range (0, _dbus_string_get_length (mutated));
+
+  _dbus_string_insert_byte (mutated, i,
+                            random_int_in_range (0, 256));
+}
+
+static void
+randomly_do_n_things (const DBusString *orig_data,
+                      DBusString       *mutated,
+                      int               n)
+{
+  int i;
+  void (* functions[]) (const DBusString *orig_data,
+                        DBusString       *mutated) =
+    {
+      randomly_shorten_or_lengthen,
+      randomly_change_one_byte,
+      randomly_add_one_byte,
+      randomly_remove_one_byte
+    };
+
+  _dbus_string_set_length (mutated, 0);
+
+  if (!_dbus_string_copy (orig_data, 0, mutated, 0))
+    _dbus_assert_not_reached ("out of mem");
+
+  i = 0;
+  while (i < n)
+    {
+      int which;
+
+      which = random_int_in_range (0, _DBUS_N_ELEMENTS (functions));
+
+      (* functions[which]) (mutated, mutated);
+      
+      ++i;
+    }
+}
+
+static dbus_bool_t
+find_breaks_based_on (const DBusString   *filename,
+                      dbus_bool_t         is_raw,
+                      DBusMessageValidity expected_validity,
+                      void               *data)
+{
+  DBusString orig_data;
+  DBusString mutated;
+  const char *filename_c;
+  dbus_bool_t retval;
+  int i;
+
+  _dbus_string_get_const_data (filename, &filename_c);
+
+  retval = FALSE;
+
+  if (!_dbus_string_init (&orig_data, _DBUS_INT_MAX))
+    _dbus_assert_not_reached ("could not allocate string\n");
+
+  if (!_dbus_string_init (&mutated, _DBUS_INT_MAX))
+    _dbus_assert_not_reached ("could not allocate string\n");
+
+  if (!dbus_internal_do_not_use_load_message_file (filename, is_raw,
+                                                   &orig_data))
+    {
+      fprintf (stderr, "could not load file %s\n", filename_c);
+      goto failed;
+    }
+
+  i = 0;
+  while (i < 100)
+    {
+      randomly_change_one_byte (&orig_data, &mutated);
+      try_mutated_data (&mutated);
+
+      ++i;
+    }
+
+  i = 0;
+  while (i < 50)
+    {
+      randomly_remove_one_byte (&orig_data, &mutated);
+      try_mutated_data (&mutated);
+
+      ++i;
+    }
+
+  i = 0;
+  while (i < 50)
+    {
+      randomly_add_one_byte (&orig_data, &mutated);
+      try_mutated_data (&mutated);
+
+      ++i;
+    }
+  
+  i = 0;
+  while (i < 15)
+    {
+      randomly_shorten_or_lengthen (&orig_data, &mutated);
+      try_mutated_data (&mutated);
+
+      ++i;
+    }
+
+  i = 0;
+  while (i < 42)
+    {
+      randomly_do_n_things (&orig_data, &mutated, 2);
+      try_mutated_data (&mutated);
+
+      ++i;
+    }
+
+  i = 0;
+  while (i < 42)
+    {
+      randomly_do_n_things (&orig_data, &mutated, 3);
+      try_mutated_data (&mutated);
+
+      ++i;
+    }
+
+  i = 0;
+  while (i < 42)
+    {
+      randomly_do_n_things (&orig_data, &mutated, 4);
+      try_mutated_data (&mutated);
+
+      ++i;
+    }
+  
+  retval = TRUE;
+  
+ failed:
+
+  _dbus_string_free (&orig_data);
+  _dbus_string_free (&mutated);
+
+  /* FALSE means end the whole process */
+  return retval;
+}
+
+static unsigned int
+get_random_seed (void)
+{
+  DBusString bytes;
+  unsigned int seed;
+  int fd;
+  const char *s;
+
+  seed = 0;
+
+  if (!_dbus_string_init (&bytes, _DBUS_INT_MAX))
+    exit (1);
+
+  fd = open ("/dev/urandom", O_RDONLY);
+  if (fd < 0)
+    goto use_fallback;
+
+  if (_dbus_read (fd, &bytes, 4) != 4)
+    goto use_fallback;
+
+  close (fd);
+
+  _dbus_string_get_const_data (&bytes, &s);
+
+  seed = * (unsigned int*) s;
+  goto out;
+
+ use_fallback:
+  {
+    long tv_usec;
+
+    fprintf (stderr, "could not open/read /dev/urandom, using current time for seed\n");
+
+    _dbus_get_current_time (NULL, &tv_usec);
+
+    seed = tv_usec;
+  }
+
+ out:
+  _dbus_string_free (&bytes);
+
+  return seed;
+}
+
+int
+main (int    argc,
+      char **argv)
+{
+  const char *test_data_dir;
+  const char *failure_dir_c;
+  int total_failures_found;
+  
+  if (argc > 1)
+    test_data_dir = argv[1];
+  else
+    {
+      fprintf (stderr, "Must specify a top_srcdir/test/data directory\n");
+      return 1;
+    }
+
+  total_failures_found = 0;
+  total_attempts = 0;
+
+  if (!_dbus_string_init (&failure_dir, _DBUS_INT_MAX))
+    return 1;
+
+  /* so you can leave it overnight safely */
+#define MAX_FAILURES 1000
+
+  while (total_failures_found < MAX_FAILURES)
+    {
+      unsigned int seed;
+
+      failures_this_iteration = 0;
+
+      seed = get_random_seed ();
+
+      _dbus_string_set_length (&failure_dir, 0);
+
+      if (!_dbus_string_append (&failure_dir, "failures-"))
+        return 1;
+
+      if (!_dbus_string_append_uint (&failure_dir, seed))
+        return 1;
+
+      _dbus_string_get_const_data (&failure_dir, &failure_dir_c);
+
+      if (mkdir (failure_dir_c, 0700) < 0)
+        {
+          if (errno != EEXIST)
+            fprintf (stderr, "didn't mkdir %s: %s\n",
+                     failure_dir_c, strerror (errno));
+        }
+
+      printf ("next seed = %u \ttotal failures %d of %d attempts\n",
+              seed, total_failures_found, total_attempts);
+
+      srand (seed);
+
+      if (!dbus_internal_do_not_use_foreach_message_file (test_data_dir,
+                                                          find_breaks_based_on,
+                                                          NULL))
+        {
+          fprintf (stderr, "fatal error iterating over message files\n");
+          rmdir (failure_dir_c);
+          return 1;
+        }
+
+      printf ("Found %d failures with seed %u stored in %s\n",
+              failures_this_iteration, seed, failure_dir_c);
+
+      total_failures_found += failures_this_iteration;
+
+      rmdir (failure_dir_c); /* does nothing if non-empty */
+    }
+
+  return 0;
+}
index 2a67498..37128a2 100644 (file)
@@ -1225,7 +1225,7 @@ _dbus_marshal_validate_arg (const DBusString *str,
       break;
       
     default:
-      _dbus_warn ("Unknown message arg type %d\n", *data);
+      _dbus_verbose ("Unknown message arg type %d\n", *data);
       return FALSE;
     }
 
index 7b11660..c4eca84 100644 (file)
@@ -2483,39 +2483,27 @@ check_loader_results (DBusMessageLoader      *loader,
 
 
 /**
- * Tries loading the message in the given message file.
- * The file must end in .message for our message-builder language
- * or .message-raw for a binary file to be treated as a message
- * verbatim.
- *
+ * Loads the message in the given message file.
  *
  * @param filename filename to load
  * @param is_raw if #TRUE load as binary data, if #FALSE as message builder language
- * @param expected_validity what the message has to be like to return #TRUE
- * @returns #TRUE if the message has the expected validity
+ * @param data string to load message into
+ * @returns #TRUE if the message was loaded
  */
 dbus_bool_t
-dbus_internal_do_not_use_try_message_file (const DBusString    *filename,
-                                           dbus_bool_t          is_raw,
-                                           DBusMessageValidity  expected_validity)
+dbus_internal_do_not_use_load_message_file (const DBusString    *filename,
+                                            dbus_bool_t          is_raw,
+                                            DBusString          *data)
 {
-  DBusString data;
-  DBusMessageLoader *loader;
   dbus_bool_t retval;
-  int len;
-  int i;
 
-  loader = NULL;
-  retval = FALSE;
-  
-  if (!_dbus_string_init (&data, _DBUS_INT_MAX))
-    _dbus_assert_not_reached ("could not allocate string\n");
+  retval = FALSE;  
 
   if (is_raw)
     {
       DBusResultCode result;
 
-      result = _dbus_file_get_contents (&data, filename);
+      result = _dbus_file_get_contents (data, filename);
       if (result != DBUS_RESULT_SUCCESS)
         {
           const char *s;      
@@ -2526,7 +2514,7 @@ dbus_internal_do_not_use_try_message_file (const DBusString    *filename,
     }
   else
     {
-      if (!_dbus_message_data_load (&data, filename))
+      if (!_dbus_message_data_load (data, filename))
         {
           const char *s;      
           _dbus_string_get_const_data (filename, &s);
@@ -2535,18 +2523,93 @@ dbus_internal_do_not_use_try_message_file (const DBusString    *filename,
         }
     }
 
+  retval = TRUE;
+  
+ failed:
+
+  return retval;
+}
+
+/**
+ * Tries loading the message in the given message file
+ * and verifies that DBusMessageLoader can handle it.
+ *
+ * @param filename filename to load
+ * @param is_raw if #TRUE load as binary data, if #FALSE as message builder language
+ * @param expected_validity what the message has to be like to return #TRUE
+ * @returns #TRUE if the message has the expected validity
+ */
+dbus_bool_t
+dbus_internal_do_not_use_try_message_file (const DBusString    *filename,
+                                           dbus_bool_t          is_raw,
+                                           DBusMessageValidity  expected_validity)
+{
+  DBusString data;
+  dbus_bool_t retval;
+
+  retval = FALSE;
+  
+  if (!_dbus_string_init (&data, _DBUS_INT_MAX))
+    _dbus_assert_not_reached ("could not allocate string\n");
+
+  if (!dbus_internal_do_not_use_load_message_file (filename, is_raw,
+                                                   &data))
+    goto failed;
+
+  retval = dbus_internal_do_not_use_try_message_data (&data, expected_validity);
+
+ failed:
+
+  if (!retval)
+    {
+      const char *s;
+
+      if (_dbus_string_get_length (&data) > 0)
+        _dbus_verbose_bytes_of_string (&data, 0,
+                                       _dbus_string_get_length (&data));
+      
+      _dbus_string_get_const_data (filename, &s);
+      _dbus_warn ("Failed message loader test on %s\n",
+                  s);
+    }
+  
+  _dbus_string_free (&data);
+
+  return retval;
+}
+
+/**
+ * Tries loading the given message data.
+ *
+ *
+ * @param data the message data
+ * @param expected_validity what the message has to be like to return #TRUE
+ * @returns #TRUE if the message has the expected validity
+ */
+dbus_bool_t
+dbus_internal_do_not_use_try_message_data (const DBusString    *data,
+                                           DBusMessageValidity  expected_validity)
+{
+  DBusMessageLoader *loader;
+  dbus_bool_t retval;
+  int len;
+  int i;
+
+  loader = NULL;
+  retval = FALSE;
+
   /* Write the data one byte at a time */
   
   loader = _dbus_message_loader_new ();
 
-  len = _dbus_string_get_length (&data);
+  len = _dbus_string_get_length (data);
   for (i = 0; i < len; i++)
     {
       DBusString *buffer;
 
       _dbus_message_loader_get_buffer (loader, &buffer);
       _dbus_string_append_byte (buffer,
-                                _dbus_string_get_byte (&data, i));
+                                _dbus_string_get_byte (data, i));
       _dbus_message_loader_return_buffer (loader, buffer, 1);
     }
   
@@ -2564,7 +2627,7 @@ dbus_internal_do_not_use_try_message_file (const DBusString    *filename,
     DBusString *buffer;
     
     _dbus_message_loader_get_buffer (loader, &buffer);
-    _dbus_string_copy (&data, 0, buffer,
+    _dbus_string_copy (data, 0, buffer,
                        _dbus_string_get_length (buffer));
     _dbus_message_loader_return_buffer (loader, buffer, 1);
   }
@@ -2579,17 +2642,17 @@ dbus_internal_do_not_use_try_message_file (const DBusString    *filename,
   
   loader = _dbus_message_loader_new ();
 
-  len = _dbus_string_get_length (&data);
+  len = _dbus_string_get_length (data);
   for (i = 0; i < len; i += 2)
     {
       DBusString *buffer;
 
       _dbus_message_loader_get_buffer (loader, &buffer);
       _dbus_string_append_byte (buffer,
-                                _dbus_string_get_byte (&data, i));
+                                _dbus_string_get_byte (data, i));
       if ((i+1) < len)
         _dbus_string_append_byte (buffer,
-                                  _dbus_string_get_byte (&data, i+1));
+                                  _dbus_string_get_byte (data, i+1));
       _dbus_message_loader_return_buffer (loader, buffer, 1);
     }
   
@@ -2602,22 +2665,9 @@ dbus_internal_do_not_use_try_message_file (const DBusString    *filename,
   retval = TRUE;
   
  failed:
-  if (!retval)
-    {
-      const char *s;
-
-      if (_dbus_string_get_length (&data) > 0)
-        _dbus_verbose_bytes_of_string (&data, 0,
-                                       _dbus_string_get_length (&data));
-      
-      _dbus_string_get_const_data (filename, &s);
-      _dbus_warn ("Failed message loader test on %s\n",
-                  s);
-    }
   
   if (loader)
     _dbus_message_loader_unref (loader);
-  _dbus_string_free (&data);
   
   return retval;
 }
@@ -2648,7 +2698,7 @@ process_test_subdir (const DBusString          *test_base_dir,
     _dbus_assert_not_reached ("couldn't copy test_base_dir to test_directory");
   
   if (!_dbus_concat_dir_and_file (&test_directory, &filename))    
-    _dbus_assert_not_reached ("could't allocate full path");
+    _dbus_assert_not_reached ("couldn't allocate full path");
 
   _dbus_string_free (&filename);
   if (!_dbus_string_init (&filename, _DBUS_INT_MAX))
index 05d14ba..17a7d91 100644 (file)
@@ -299,6 +299,59 @@ _dbus_string_lock (DBusString *str)
     }
 }
 
+static dbus_bool_t
+set_length (DBusRealString *real,
+            int             new_length)
+{
+  /* Note, we are setting the length without nul termination */
+
+  /* exceeding max length is the same as failure to allocate memory */
+  if (new_length > real->max_length)
+    return FALSE;
+  
+  while (new_length >= real->allocated)
+    {
+      int new_allocated;
+      char *new_str;
+      
+      new_allocated = 2 + real->allocated * 2;
+      if (new_allocated < real->allocated)
+        return FALSE; /* overflow */
+        
+      new_str = dbus_realloc (real->str, new_allocated);
+      if (new_str == NULL)
+        return FALSE;
+
+      real->str = new_str;
+      real->allocated = new_allocated;
+
+      ASSERT_8_BYTE_ALIGNED (real);
+    }
+
+  real->len = new_length;
+  real->str[real->len] = '\0';
+
+  return TRUE;
+}
+
+static dbus_bool_t
+open_gap (int             len,
+          DBusRealString *dest,
+          int             insert_at)
+{
+  if (len == 0)
+    return TRUE;
+
+  if (!set_length (dest, dest->len + len))
+    return FALSE;
+
+  memmove (dest->str + insert_at + len, 
+           dest->str + insert_at,
+           dest->len - len - insert_at);
+
+  return TRUE;
+}
+
 /**
  * Gets the raw character buffer from the string.  The returned buffer
  * will be nul-terminated, but note that strings may contain binary
@@ -388,13 +441,31 @@ _dbus_string_get_const_data_len (const DBusString  *str,
 }
 
 /**
+ * Sets the value of the byte at the given position.
+ *
+ * @param str the string
+ * @param i the position
+ * @param byte the new value
+ */
+void
+_dbus_string_set_byte (DBusString    *str,
+                       int            i,
+                       unsigned char  byte)
+{
+  DBUS_STRING_PREAMBLE (str);
+  _dbus_assert (i < real->len);
+
+  real->str[i] = byte;
+}
+
+/**
  * Gets the byte at the given position.
  *
  * @param str the string
  * @param start the position
  * @returns the byte at that position
  */
-char
+unsigned char
 _dbus_string_get_byte (const DBusString  *str,
                        int                start)
 {
@@ -405,6 +476,30 @@ _dbus_string_get_byte (const DBusString  *str,
 }
 
 /**
+ * Inserts the given byte at the given position.
+ *
+ * @param str the string
+ * @param i the position
+ * @param byte the value to insert
+ * @returns #TRUE on success
+ */
+dbus_bool_t
+_dbus_string_insert_byte (DBusString   *str,
+                          int           i,
+                          unsigned char byte)
+{
+  DBUS_STRING_PREAMBLE (str);
+  _dbus_assert (i <= real->len);
+
+  if (!open_gap (1, real, i))
+    return FALSE;
+  
+  real->str[i] = byte;
+
+  return TRUE;
+}
+
+/**
  * Like _dbus_string_get_data(), but removes the
  * gotten data from the original string. The caller
  * must free the data returned. This function may
@@ -493,41 +588,6 @@ _dbus_string_get_length (const DBusString  *str)
   return real->len;
 }
 
-static dbus_bool_t
-set_length (DBusRealString *real,
-            int             new_length)
-{
-  /* Note, we are setting the length without nul termination */
-
-  /* exceeding max length is the same as failure to allocate memory */
-  if (new_length > real->max_length)
-    return FALSE;
-  
-  while (new_length >= real->allocated)
-    {
-      int new_allocated;
-      char *new_str;
-      
-      new_allocated = 2 + real->allocated * 2;
-      if (new_allocated < real->allocated)
-        return FALSE; /* overflow */
-        
-      new_str = dbus_realloc (real->str, new_allocated);
-      if (new_str == NULL)
-        return FALSE;
-
-      real->str = new_str;
-      real->allocated = new_allocated;
-
-      ASSERT_8_BYTE_ALIGNED (real);
-    }
-
-  real->len = new_length;
-  real->str[real->len] = '\0';
-
-  return TRUE;
-}
-
 /**
  * Makes a string longer by the given number of bytes.  Checks whether
  * adding additional_length to the current length would overflow an
@@ -812,24 +872,6 @@ _dbus_string_delete (DBusString       *str,
 }
 
 static dbus_bool_t
-open_gap (int             len,
-          DBusRealString *dest,
-          int             insert_at)
-{
-  if (len == 0)
-    return TRUE;
-
-  if (!set_length (dest, dest->len + len))
-    return FALSE;
-
-  memmove (dest->str + insert_at + len, 
-           dest->str + insert_at,
-           dest->len - len - insert_at);
-
-  return TRUE;
-}
-
-static dbus_bool_t
 copy (DBusRealString *source,
       int             start,
       int             len,
@@ -2282,6 +2324,43 @@ _dbus_string_test (void)
   _dbus_assert (i == _dbus_string_get_length (&str));
 
   _dbus_string_free (&str);
+
+  /* Check insert/set/get byte */
+  
+  if (!_dbus_string_init (&str, _DBUS_INT_MAX))
+    _dbus_assert_not_reached ("failed to init string");
+
+  if (!_dbus_string_append (&str, "Hello"))
+    _dbus_assert_not_reached ("failed to append Hello");
+
+  _dbus_assert (_dbus_string_get_byte (&str, 0) == 'H');
+  _dbus_assert (_dbus_string_get_byte (&str, 1) == 'e');
+  _dbus_assert (_dbus_string_get_byte (&str, 2) == 'l');
+  _dbus_assert (_dbus_string_get_byte (&str, 3) == 'l');
+  _dbus_assert (_dbus_string_get_byte (&str, 4) == 'o');
+
+  _dbus_string_set_byte (&str, 1, 'q');
+  _dbus_assert (_dbus_string_get_byte (&str, 1) == 'q');
+
+  if (!_dbus_string_insert_byte (&str, 0, 255))
+    _dbus_assert_not_reached ("can't insert byte");
+
+  if (!_dbus_string_insert_byte (&str, 2, 'Z'))
+    _dbus_assert_not_reached ("can't insert byte");
+
+  if (!_dbus_string_insert_byte (&str, _dbus_string_get_length (&str), 'W'))
+    _dbus_assert_not_reached ("can't insert byte");
+  
+  _dbus_assert (_dbus_string_get_byte (&str, 0) == 255);
+  _dbus_assert (_dbus_string_get_byte (&str, 1) == 'H');
+  _dbus_assert (_dbus_string_get_byte (&str, 2) == 'Z');
+  _dbus_assert (_dbus_string_get_byte (&str, 3) == 'q');
+  _dbus_assert (_dbus_string_get_byte (&str, 4) == 'l');
+  _dbus_assert (_dbus_string_get_byte (&str, 5) == 'l');
+  _dbus_assert (_dbus_string_get_byte (&str, 6) == 'o');
+  _dbus_assert (_dbus_string_get_byte (&str, 7) == 'W');
+
+  _dbus_string_free (&str);
   
   /* Check append/parse int/double */
   
index 641eaf5..54297ee 100644 (file)
@@ -66,8 +66,14 @@ void        _dbus_string_get_const_data_len (const DBusString  *str,
                                              const char       **data_return,
                                              int                start,
                                              int                len);
-char        _dbus_string_get_byte           (const DBusString  *str,
+void        _dbus_string_set_byte           (DBusString        *str,
+                                             int                i,
+                                             unsigned char      byte);
+unsigned char _dbus_string_get_byte         (const DBusString  *str,
                                              int                start);
+dbus_bool_t _dbus_string_insert_byte        (DBusString        *str,
+                                             int                i,
+                                             unsigned char      byte);
 dbus_bool_t _dbus_string_steal_data         (DBusString        *str,
                                              char             **data_return);
 dbus_bool_t _dbus_string_steal_data_len     (DBusString        *str,
@@ -93,6 +99,8 @@ dbus_bool_t _dbus_string_append_len     (DBusString    *str,
                                          int            len);
 dbus_bool_t _dbus_string_append_int     (DBusString    *str,
                                          long           value);
+dbus_bool_t _dbus_string_append_uint    (DBusString    *str,
+                                         unsigned long  value);
 dbus_bool_t _dbus_string_append_double  (DBusString    *str,
                                          double         value);
 dbus_bool_t _dbus_string_append_byte    (DBusString    *str,
index bcb81f7..17445a8 100644 (file)
@@ -618,6 +618,44 @@ _dbus_string_append_int (DBusString *str,
 }
 
 /**
+ * Appends an unsigned integer to a DBusString.
+ * 
+ * @param str the string
+ * @param value the integer value
+ * @returns #FALSE if not enough memory or other failure.
+ */
+dbus_bool_t
+_dbus_string_append_uint (DBusString    *str,
+                          unsigned long  value)
+{
+  /* this is wrong, but definitely on the high side. */
+#define MAX_ULONG_LEN (MAX_LONG_LEN * 2)
+  int orig_len;
+  int i;
+  char *buf;
+  
+  orig_len = _dbus_string_get_length (str);
+
+  if (!_dbus_string_lengthen (str, MAX_ULONG_LEN))
+    return FALSE;
+
+  _dbus_string_get_data_len (str, &buf, orig_len, MAX_ULONG_LEN);
+
+  snprintf (buf, MAX_ULONG_LEN, "%lu", value);
+
+  i = 0;
+  while (*buf)
+    {
+      ++buf;
+      ++i;
+    }
+  
+  _dbus_string_shorten (str, MAX_ULONG_LEN - i);
+  
+  return TRUE;
+}
+
+/**
  * Appends a double to a DBusString.
  * 
  * @param str the string
@@ -1103,6 +1141,59 @@ _dbus_file_get_contents (DBusString       *str,
 }
 
 /**
+ * Writes a string out to a file.
+ *
+ * @param str the string to write out
+ * @param filename the file to save string to
+ * @returns result code
+ */
+DBusResultCode
+_dbus_string_save_to_file (const DBusString *str,
+                           const DBusString *filename)
+{
+  int fd;
+  int bytes_to_write;
+  const char *filename_c;
+  int total;
+
+  _dbus_string_get_const_data (filename, &filename_c);
+  
+  fd = open (filename_c, O_WRONLY | O_BINARY | O_EXCL | O_CREAT,
+             0700);
+  if (fd < 0)
+    return _dbus_result_from_errno (errno);
+
+  total = 0;
+  bytes_to_write = _dbus_string_get_length (str);
+
+  while (total < bytes_to_write)
+    {
+      int bytes_written;
+
+      bytes_written = _dbus_write (fd, str, total,
+                                   bytes_to_write - total);
+
+      if (bytes_written <= 0)
+        {
+          DBusResultCode result;
+          
+          result = _dbus_result_from_errno (errno); /* prior to close() */
+          
+          _dbus_verbose ("write() failed: %s",
+                         _dbus_strerror (errno));
+          
+          close (fd);          
+          return result;
+        }
+
+      total += bytes_written;
+    }
+
+  close (fd);
+  return DBUS_RESULT_SUCCESS;
+}
+
+/**
  * Appends the given filename to the given directory.
  *
  * @param dir the directory name
index fc552e7..fd0d93d 100644 (file)
@@ -122,8 +122,10 @@ void _dbus_sleep_milliseconds (int milliseconds);
 void _dbus_get_current_time (long *tv_sec,
                              long *tv_usec);
 
-DBusResultCode _dbus_file_get_contents (DBusString       *str,
-                                        const DBusString *filename);
+DBusResultCode _dbus_file_get_contents   (DBusString       *str,
+                                          const DBusString *filename);
+DBusResultCode _dbus_string_save_to_file (const DBusString *str,
+                                          const DBusString *filename);
 
 dbus_bool_t _dbus_concat_dir_and_file (DBusString       *dir,
                                        const DBusString *next_component);
index 28a8444..31753ac 100644 (file)
@@ -43,10 +43,16 @@ dbus_bool_t _dbus_string_test   (void);
 dbus_bool_t _dbus_address_test  (void);
 dbus_bool_t _dbus_message_test  (const char *test_data_dir);
 
-void        dbus_internal_do_not_use_run_tests        (const char          *test_data_dir);
-dbus_bool_t dbus_internal_do_not_use_try_message_file (const DBusString    *filename,
-                                                       dbus_bool_t          is_raw,
-                                                       DBusMessageValidity  expected_validity);
+void        dbus_internal_do_not_use_run_tests         (const char          *test_data_dir);
+dbus_bool_t dbus_internal_do_not_use_try_message_file  (const DBusString    *filename,
+                                                        dbus_bool_t          is_raw,
+                                                        DBusMessageValidity  expected_validity);
+dbus_bool_t dbus_internal_do_not_use_try_message_data  (const DBusString    *data,
+                                                        DBusMessageValidity  expected_validity);
+dbus_bool_t dbus_internal_do_not_use_load_message_file (const DBusString    *filename,
+                                                        dbus_bool_t          is_raw,
+                                                        DBusString          *data);
+
 
 /* returns FALSE on fatal failure */
 typedef dbus_bool_t (* DBusForeachMessageFileFunc) (const DBusString   *filename,
@@ -55,8 +61,8 @@ typedef dbus_bool_t (* DBusForeachMessageFileFunc) (const DBusString   *filename
                                                     void               *data);
 
 dbus_bool_t dbus_internal_do_not_use_foreach_message_file (const char                 *test_data_dir,
-                                                    DBusForeachMessageFileFunc  func,
-                                                    void                       *user_data);
+                                                           DBusForeachMessageFileFunc  func,
+                                                           void                       *user_data);