From 2f440457d5fe45afb732820da64a147157e2e82d Mon Sep 17 00:00:00 2001 From: Havoc Pennington Date: Sun, 26 Jan 2003 21:16:06 +0000 Subject: 2003-01-26 Havoc Pennington * dbus/dbus-message-builder.c: implement, completely untested. * test/data/*: add data to be used in testing. ".message" files are our simple loadable text format. ".message-raw" will be binary dumps of messages. * dbus/dbus-string.c (_dbus_string_starts_with_c_str): new --- dbus/dbus-message-builder.c | 710 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 707 insertions(+), 3 deletions(-) (limited to 'dbus/dbus-message-builder.c') diff --git a/dbus/dbus-message-builder.c b/dbus/dbus-message-builder.c index e91fa391..d290ecd4 100644 --- a/dbus/dbus-message-builder.c +++ b/dbus/dbus-message-builder.c @@ -20,8 +20,14 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ +#include + #ifdef DBUS_BUILD_TESTS + #include "dbus-message-builder.h" +#include "dbus-hash.h" +#include "dbus-internals.h" +#include "dbus-marshal.h" /** * @defgroup DBusMessageBuilder code for loading test message data @@ -34,18 +40,274 @@ * @{ */ +static dbus_bool_t +pop_line (DBusString *source, + DBusString *dest) +{ + int eol; + + _dbus_string_set_length (dest, 0); + + eol = 0; + if (_dbus_string_find (source, 0, "\n", &eol)) + eol += 1; /* include newline */ + else + eol = _dbus_string_get_length (source); + + if (eol == 0) + { + _dbus_verbose ("no more data in file\n"); + return FALSE; + } + + if (!_dbus_string_move_len (source, 0, eol, + dest, 0)) + { + _dbus_warn ("failed to pop line\n"); + return FALSE; + } + + /* dump the newline */ + _dbus_string_set_length (dest, + _dbus_string_get_length (dest) - 1); + + return TRUE; +} + +static void +strip_command_name (DBusString *str) +{ + int i; + + i = 0; + if (_dbus_string_find_blank (str, 0, &i)) + _dbus_string_skip_blank (str, i, &i); + + _dbus_string_delete (str, 0, i); +} + +static void +strip_leading_space (DBusString *str) +{ + int i; + + i = 0; + _dbus_string_skip_blank (str, 0, &i); + + if (i > 0) + _dbus_string_delete (str, 0, i); +} + +typedef struct +{ + DBusString name; + int length; /**< length to write */ + int offset; /**< where to write it into the data */ + int endian; /**< endianness to write with */ +} SavedLength; + +static void +free_saved_length (void *data) +{ + SavedLength *sl = data; + + _dbus_string_free (&sl->name); + dbus_free (sl); +} + +static SavedLength* +ensure_saved_length (DBusHashTable *hash, + const DBusString *name) +{ + SavedLength *sl; + const char *s; + + _dbus_string_get_const_data (name, &s); + + sl = _dbus_hash_table_lookup_string (hash, s); + if (sl != NULL) + return sl; + + sl = dbus_new0 (SavedLength, 1); + + if (!_dbus_string_init (&sl->name, _DBUS_INT_MAX)) + { + dbus_free (sl); + return NULL; + } + + if (!_dbus_string_copy (name, 0, &sl->name, 0)) + goto failed; + + _dbus_string_get_const_data (&sl->name, &s); + + if (!_dbus_hash_table_insert_string (hash, (char*)s, sl)) + goto failed; + + sl->length = -1; + sl->offset = -1; + sl->endian = -1; + + return sl; + + failed: + free_saved_length (sl); + return NULL; +} + +static dbus_bool_t +save_length (DBusHashTable *hash, + const DBusString *name, + int length) +{ + SavedLength *sl; + + sl = ensure_saved_length (hash, name); + + if (sl == NULL) + return FALSE; + else if (sl->length >= 0) + { + _dbus_warn ("Same SAVE_LENGTH given twice\n"); + return FALSE; + } + else + sl->length = length; + + return TRUE; +} + +static dbus_bool_t +save_offset (DBusHashTable *hash, + const DBusString *name, + int offset, + int endian) +{ + SavedLength *sl; + + sl = ensure_saved_length (hash, name); + + if (sl == NULL) + return FALSE; + else if (sl->offset >= 0) + { + _dbus_warn ("Same LENGTH given twice\n"); + return FALSE; + } + else + { + sl->offset = offset; + sl->endian = endian; + } + + return TRUE; +} + +/** Saves the segment to delete in order to unalign the next item */ +#define SAVE_FOR_UNALIGN(str, boundary) \ + int align_pad_start = _dbus_string_get_length (str); \ + int align_pad_end = _DBUS_ALIGN_VALUE (align_pad_start, (boundary)) + +/** Deletes the alignment padding */ +#define PERFORM_UNALIGN(str) \ + if (unalign) \ + { \ + _dbus_string_delete ((str), align_pad_start, \ + align_pad_end - align_pad_start); \ + unalign = FALSE; \ + } + + +static dbus_bool_t +append_quoted_string (DBusString *dest, + const DBusString *quoted) +{ + dbus_bool_t in_quotes = FALSE; + int i; + + i = 0; + while (i < _dbus_string_get_length (quoted)) + { + unsigned char b; + + b = _dbus_string_get_byte (quoted, i); + + if (in_quotes) + { + if (b == '\'') + in_quotes = FALSE; + else + { + if (!_dbus_string_append_byte (dest, b)) + return FALSE; + } + } + else + { + if (b == '\'') + in_quotes = TRUE; + else if (b == ' ' || b == '\n' || b == '\t') + break; /* end on whitespace if not quoted */ + else + { + if (!_dbus_string_append_byte (dest, b)) + return FALSE; + } + } + + ++i; + } + + if (!_dbus_string_append_byte (dest, '\0')) + return FALSE; + return TRUE; +} + +static dbus_bool_t +append_saved_length (DBusString *dest, + DBusHashTable *length_hash, + const DBusString *name, + int offset, + int endian) +{ + if (!save_offset (length_hash, name, + offset, endian)) + { + _dbus_warn ("failed to save offset to LENGTH\n"); + return FALSE; + } + + if (!_dbus_marshal_int32 (dest, endian, + -1)) + { + _dbus_warn ("failed to append a length\n"); + return FALSE; + } + + return TRUE; +} + /** * Reads the given filename, which should be in "message description * language" (look at some examples), and builds up the message data * from it. The message data may be invalid, or valid. * + * The parser isn't very strict, it's just a hack for test programs. + * * The file format is: * @code + * VALID_HEADER normal header; byte order, padding, header len, body len, serial + * BIG_ENDIAN switch to big endian + * LITTLE_ENDIAN switch to little endian + * OPPOSITE_ENDIAN switch to opposite endian * ALIGN aligns to the given value * UNALIGN skips alignment for the next marshal - * BYTE inserts the given integer in [0,255] + * BYTE inserts the given integer in [0,255] or char in 'a' format * SAVE_LENGTH records the current length under the given name * LENGTH inserts the saved length of the same name + * CHOP chops last N bytes off the data + * FIELD_NAME inserts 4-byte field name + * TYPE inserts a typecode byte * @endcode * * Following commands insert aligned data unless @@ -54,8 +316,11 @@ * INT32 marshals an INT32 * UINT32 marshals a UINT32 * DOUBLE marshals a double - * STRING "Foo" marshals a string + * STRING 'Foo' marshals a string * @endcode + * + * @todo add support for array types INT32_ARRAY { 3, 4, 5, 6 } + * and so forth. * * @param dest the string to append the message data to * @param filename the filename to load @@ -65,8 +330,447 @@ dbus_bool_t _dbus_message_data_load (DBusString *dest, const DBusString *filename) { - /* FIXME implement */ + DBusString file; + DBusResultCode result; + DBusString line; + dbus_bool_t retval; + int line_no; + dbus_bool_t unalign; + DBusHashTable *length_hash; + int endian; + DBusHashIter iter; + + retval = FALSE; + length_hash = NULL; + + if (!_dbus_string_init (&file, _DBUS_INT_MAX)) + return FALSE; + + if (!_dbus_string_init (&line, _DBUS_INT_MAX)) + { + _dbus_string_free (&file); + return FALSE; + } + + if ((result = _dbus_file_get_contents (&file, filename)) != DBUS_RESULT_SUCCESS) + { + const char *s; + _dbus_string_get_const_data (filename, &s); + _dbus_warn ("Getting contents of %s failed: %s\n", + s, dbus_result_to_string (result)); + + goto out; + } + + length_hash = _dbus_hash_table_new (DBUS_HASH_STRING, + NULL, + free_saved_length); + if (length_hash == NULL) + goto out; + + endian = DBUS_COMPILER_BYTE_ORDER; + unalign = FALSE; + line_no = 0; + next_iteration: + while (pop_line (&file, &line)) + { + dbus_bool_t just_set_unalign; + + just_set_unalign = FALSE; + line_no += 1; + + strip_leading_space (&line); + + if (_dbus_string_starts_with_c_str (&line, + "#")) + { + /* Ignore this comment */ + goto next_iteration; + } + else if (_dbus_string_starts_with_c_str (&line, + "VALID_HEADER")) + { + int i; + DBusString name; + + if (!_dbus_string_append_byte (dest, endian)) + { + _dbus_warn ("could not append endianness\n"); + goto parse_failed; + } + + i = 0; + while (i < 3) + { + if (!_dbus_string_append_byte (dest, '\0')) + { + _dbus_warn ("could not append nul pad\n"); + goto parse_failed; + } + ++i; + } + + _dbus_string_init_const (&name, "Header"); + if (!append_saved_length (dest, length_hash, + &name, _dbus_string_get_length (dest), + endian)) + goto parse_failed; + + _dbus_string_init_const (&name, "Body"); + if (!append_saved_length (dest, length_hash, + &name, _dbus_string_get_length (dest), + endian)) + goto parse_failed; + + /* client serial */ + if (!_dbus_marshal_int32 (dest, endian, 1)) + { + _dbus_warn ("couldn't append client serial\n"); + goto parse_failed; + } + } + else if (_dbus_string_starts_with_c_str (&line, + "BIG_ENDIAN")) + { + endian = DBUS_BIG_ENDIAN; + } + else if (_dbus_string_starts_with_c_str (&line, + "LITTLE_ENDIAN")) + { + endian = DBUS_LITTLE_ENDIAN; + } + else if (_dbus_string_starts_with_c_str (&line, + "OPPOSITE_ENDIAN")) + { + if (endian == DBUS_BIG_ENDIAN) + endian = DBUS_LITTLE_ENDIAN; + else + endian = DBUS_BIG_ENDIAN; + } + else if (_dbus_string_starts_with_c_str (&line, + "ALIGN")) + { + long val; + + strip_command_name (&line); + + if (!_dbus_string_parse_int (&line, 0, &val, NULL)) + goto parse_failed; + + if (val > 16) + { + _dbus_warn ("Aligning to %ld boundary is crack\n", + val); + goto parse_failed; + } + + if (!_dbus_string_align_length (dest, val)) + goto parse_failed; + } + else if (_dbus_string_starts_with_c_str (&line, "UNALIGN")) + { + unalign = TRUE; + just_set_unalign = TRUE; + } + else if (_dbus_string_starts_with_c_str (&line, "CHOP")) + { + long val; + + /* FIXME if you CHOP the offset for a LENGTH + * command, we segfault. + */ + + strip_command_name (&line); + + if (!_dbus_string_parse_int (&line, 0, &val, NULL)) + goto parse_failed; + + if (val > _dbus_string_get_length (dest)) + { + _dbus_warn ("Trying to chop %ld bytes but we only have %d\n", + val, + _dbus_string_get_length (dest)); + goto parse_failed; + } + + _dbus_string_shorten (dest, val); + } + else if (_dbus_string_starts_with_c_str (&line, "BYTE")) + { + unsigned char the_byte; + + strip_command_name (&line); + + if (_dbus_string_equal_c_str (&line, "'\\''")) + the_byte = '\''; + else if (_dbus_string_get_byte (&line, 0) == '\'' && + _dbus_string_get_length (&line) >= 3 && + _dbus_string_get_byte (&line, 2) == '\'') + the_byte = _dbus_string_get_byte (&line, 1); + else + { + long val; + if (!_dbus_string_parse_int (&line, 0, &val, NULL)) + goto parse_failed; + if (val > 255) + { + _dbus_warn ("A byte must be in range 0-255 not %ld\n", + val); + goto parse_failed; + } + the_byte = (unsigned char) val; + } + + _dbus_string_append_byte (dest, the_byte); + } + else if (_dbus_string_starts_with_c_str (&line, + "SAVE_LENGTH")) + { + strip_command_name (&line); + + if (!save_length (length_hash, &line, + _dbus_string_get_length (dest))) + { + _dbus_warn ("failed to save length\n"); + goto parse_failed; + } + } + else if (_dbus_string_starts_with_c_str (&line, + "LENGTH")) + { + SAVE_FOR_UNALIGN (dest, 4); + + strip_command_name (&line); + + if (!append_saved_length (dest, length_hash, + &line, + unalign ? align_pad_start : align_pad_end, + endian)) + { + _dbus_warn ("failed to add LENGTH\n"); + goto parse_failed; + } + + PERFORM_UNALIGN (dest); + } + else if (_dbus_string_starts_with_c_str (&line, + "FIELD_NAME")) + { + strip_command_name (&line); + + if (_dbus_string_get_length (&line) != 4) + { + const char *s; + _dbus_string_get_const_data (&line, &s); + _dbus_warn ("Field name must be four characters not \"%s\"\n", + s); + goto parse_failed; + } + + if (unalign) + unalign = FALSE; + else + _dbus_string_align_length (dest, 4); + + if (!_dbus_string_copy (&line, 0, dest, + _dbus_string_get_length (dest))) + goto parse_failed; + } + else if (_dbus_string_starts_with_c_str (&line, + "TYPE")) + { + int code; + + strip_command_name (&line); + + if (_dbus_string_starts_with_c_str (&line, "INVALID")) + code = DBUS_TYPE_INVALID; + else if (_dbus_string_starts_with_c_str (&line, "NIL")) + code = DBUS_TYPE_NIL; + else if (_dbus_string_starts_with_c_str (&line, "INT32")) + code = DBUS_TYPE_INT32; + else if (_dbus_string_starts_with_c_str (&line, "UINT32")) + code = DBUS_TYPE_UINT32; + else if (_dbus_string_starts_with_c_str (&line, "DOUBLE")) + code = DBUS_TYPE_DOUBLE; + else if (_dbus_string_starts_with_c_str (&line, "STRING")) + code = DBUS_TYPE_STRING; + else if (_dbus_string_starts_with_c_str (&line, "INT32_ARRAY")) + code = DBUS_TYPE_INT32_ARRAY; + else if (_dbus_string_starts_with_c_str (&line, "UINT32_ARRAY")) + code = DBUS_TYPE_UINT32_ARRAY; + else if (_dbus_string_starts_with_c_str (&line, "DOUBLE_ARRAY")) + code = DBUS_TYPE_DOUBLE_ARRAY; + else if (_dbus_string_starts_with_c_str (&line, "BYTE_ARRAY")) + code = DBUS_TYPE_BYTE_ARRAY; + else if (_dbus_string_starts_with_c_str (&line, "STRING_ARRAY")) + code = DBUS_TYPE_STRING_ARRAY; + else + { + const char *s; + _dbus_string_get_const_data (&line, &s); + _dbus_warn ("%s is not a valid type name\n", s); + goto parse_failed; + } + + if (!_dbus_string_append_byte (dest, code)) + { + _dbus_warn ("could not append typecode byte\n"); + goto parse_failed; + } + } + else if (_dbus_string_starts_with_c_str (&line, + "INT32")) + { + SAVE_FOR_UNALIGN (dest, 4); + long val; + + strip_command_name (&line); + + if (!_dbus_string_parse_int (&line, 0, &val, NULL)) + goto parse_failed; + + if (!_dbus_marshal_int32 (dest, endian, + val)) + { + _dbus_warn ("failed to append INT32\n"); + goto parse_failed; + } + + PERFORM_UNALIGN (dest); + } + else if (_dbus_string_starts_with_c_str (&line, + "UINT32")) + { + SAVE_FOR_UNALIGN (dest, 4); + long val; + + strip_command_name (&line); + + /* FIXME should have _dbus_string_parse_uint32 */ + if (!_dbus_string_parse_int (&line, 0, &val, NULL)) + goto parse_failed; + + if (!_dbus_marshal_uint32 (dest, endian, + val)) + { + _dbus_warn ("failed to append UINT32\n"); + goto parse_failed; + } + + PERFORM_UNALIGN (dest); + } + else if (_dbus_string_starts_with_c_str (&line, + "DOUBLE")) + { + SAVE_FOR_UNALIGN (dest, 8); + double val; + + strip_command_name (&line); + + if (!_dbus_string_parse_double (&line, 0, &val, NULL)) + goto parse_failed; + + if (!_dbus_marshal_double (dest, endian, + val)) + { + _dbus_warn ("failed to append DOUBLE\n"); + goto parse_failed; + } + + PERFORM_UNALIGN (dest); + } + else if (_dbus_string_starts_with_c_str (&line, + "STRING")) + { + SAVE_FOR_UNALIGN (dest, 4); + int size_offset; + int old_len; + + strip_command_name (&line); + + size_offset = _dbus_string_get_length (dest); + size_offset = _DBUS_ALIGN_VALUE (size_offset, 4); + if (!_dbus_marshal_uint32 (dest, endian, 0)) + { + _dbus_warn ("Failed to append string size\n"); + goto parse_failed; + } + + old_len = _dbus_string_get_length (dest); + if (!append_quoted_string (dest, &line)) + { + _dbus_warn ("Failed to append quoted string\n"); + goto parse_failed; + } + + _dbus_marshal_set_uint32 (dest, endian, size_offset, + /* subtract 1 for nul */ + _dbus_string_get_length (dest) - old_len - 1); + + PERFORM_UNALIGN (dest); + } + else + goto parse_failed; + + if (!just_set_unalign && unalign) + { + _dbus_warn ("UNALIGN prior to something that isn't aligned\n"); + goto parse_failed; + } + + goto next_iteration; /* skip parse_failed */ + + parse_failed: + { + const char *s; + _dbus_string_get_const_data (&line, &s); + _dbus_warn ("couldn't process line %d \"%s\"\n", + line_no, s); + goto out; + } + } + + _dbus_hash_iter_init (length_hash, &iter); + while (_dbus_hash_iter_next (&iter)) + { + SavedLength *sl = _dbus_hash_iter_get_value (&iter); + const char *s; + + _dbus_string_get_const_data (&sl->name, &s); + + if (sl->length < 0) + { + _dbus_warn ("Used LENGTH %s but never did SAVE_LENGTH\n", + s); + goto out; + } + else if (sl->offset < 0) + { + _dbus_warn ("Did SAVE_LENGTH %s but never used LENGTH\n", + s); + goto out; + } + else + { + _dbus_verbose ("Filling in length %s endian = %d offset = %d length = %d\n", + s, sl->endian, sl->offset, sl->length); + _dbus_marshal_set_int32 (dest, + sl->endian, + sl->offset, + sl->length); + } + } + + retval = TRUE; + + out: + if (length_hash != NULL) + _dbus_hash_table_unref (length_hash); + _dbus_string_free (&file); + _dbus_string_free (&line); + return retval; } /** @} */ -- cgit