2004-05-28 Michael Meeks <michael@ximian.com>
[platform/upstream/dbus.git] / dbus / dbus-message-builder.c
1 /* -*- mode: C; c-file-style: "gnu" -*- */
2 /* dbus-message-builder.c Build messages from text files for testing (internal to D-BUS implementation)
3  * 
4  * Copyright (C) 2003 Red Hat, Inc.
5  *
6  * Licensed under the Academic Free License version 2.0
7  * 
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  * 
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
21  *
22  */
23 #include <config.h>
24
25 #ifdef DBUS_BUILD_TESTS
26
27 #include "dbus-message-builder.h"
28 #include "dbus-hash.h"
29 #include "dbus-internals.h"
30 #include "dbus-marshal.h"
31
32 /**
33  * @defgroup DBusMessageBuilder code for loading test message data
34  * @ingroup  DBusInternals
35  * @brief code for loading up test data for unit tests
36  *
37  * The code in here is used for unit testing, it loads
38  * up message data from a description in a file.
39  *
40  * @{
41  */
42
43 /**
44  * Saved length
45  */
46 typedef struct
47 {
48   DBusString name; /**< Name of the length */
49   int start;  /**< Calculate length since here */
50   int length; /**< length to write */
51   int offset; /**< where to write it into the data */
52   int endian; /**< endianness to write with */
53 } SavedLength;
54
55 static void
56 free_saved_length (void *data)
57 {
58   SavedLength *sl = data;
59
60   if (sl == NULL)
61     return; /* all hash free functions have to accept NULL */
62   
63   _dbus_string_free (&sl->name);
64   dbus_free (sl);
65 }
66
67 static SavedLength*
68 ensure_saved_length (DBusHashTable    *hash,
69                      const DBusString *name)
70 {
71   SavedLength *sl;
72   const char *s;
73   
74   s = _dbus_string_get_const_data (name);
75
76   sl = _dbus_hash_table_lookup_string (hash, s);
77   if (sl != NULL)
78     return sl;
79   
80   sl = dbus_new0 (SavedLength, 1);
81
82   if (!_dbus_string_init (&sl->name))
83     {
84       dbus_free (sl);
85       return NULL;
86     }
87
88   if (!_dbus_string_copy (name, 0, &sl->name, 0))
89     goto failed;
90
91   s = _dbus_string_get_const_data (&sl->name);
92
93   if (!_dbus_hash_table_insert_string (hash, (char*)s, sl))
94     goto failed;
95
96   sl->start = -1;
97   sl->length = -1;
98   sl->offset = -1;
99   sl->endian = -1;
100   
101   return sl;
102   
103  failed:
104   free_saved_length (sl);
105   return NULL;
106 }
107
108 static dbus_bool_t
109 save_start (DBusHashTable    *hash,
110             const DBusString *name,
111             int               start)
112 {
113   SavedLength *sl;
114
115   sl = ensure_saved_length (hash, name);
116
117   if (sl == NULL)
118     return FALSE;
119   else if (sl->start >= 0)
120     {
121       _dbus_warn ("Same START_LENGTH given twice\n");
122       return FALSE;
123     }
124   else
125     sl->start = start;
126
127   return TRUE;
128 }
129
130 static dbus_bool_t
131 save_length (DBusHashTable    *hash,
132              const DBusString *name,
133              int               length)
134 {
135   SavedLength *sl;
136
137   sl = ensure_saved_length (hash, name);
138
139   if (sl == NULL)
140     return FALSE;
141   else if (sl->length >= 0)
142     {
143       _dbus_warn ("Same END_LENGTH given twice\n");
144       return FALSE;
145     }
146   else
147     sl->length = length;
148
149   return TRUE;
150 }
151
152 static dbus_bool_t
153 save_offset (DBusHashTable    *hash,
154              const DBusString *name,
155              int               offset,
156              int               endian)
157 {
158   SavedLength *sl;
159
160   sl = ensure_saved_length (hash, name);
161
162   if (sl == NULL)
163     return FALSE;
164   else if (sl->offset >= 0)
165     {
166       _dbus_warn ("Same LENGTH given twice\n");
167       return FALSE;
168     }
169   else
170     {
171       sl->offset = offset;
172       sl->endian = endian;
173     }
174
175   return TRUE;
176 }
177
178 /** Saves the segment to delete in order to unalign the next item */
179 #define SAVE_FOR_UNALIGN(str, boundary)                                 \
180   int align_pad_start = _dbus_string_get_length (str);                  \
181   int align_pad_end = _DBUS_ALIGN_VALUE (align_pad_start, (boundary))
182
183 /** Deletes the alignment padding */
184 #define PERFORM_UNALIGN(str)                                    \
185   if (unalign)                                                  \
186     {                                                           \
187       _dbus_string_delete ((str), align_pad_start,              \
188                            align_pad_end - align_pad_start);    \
189       unalign = FALSE;                                          \
190     }
191
192
193 static dbus_bool_t
194 append_quoted_string (DBusString       *dest,
195                       const DBusString *quoted,
196                       int               start_pos,
197                       int              *new_pos)
198 {
199   dbus_bool_t in_quotes = FALSE;
200   int i;
201
202   /* FIXME: We might want to add escaping in case we want to put '
203    * characters in our strings.
204    */
205   
206   i = start_pos;
207   while (i < _dbus_string_get_length (quoted))
208     {
209       unsigned char b;
210
211       b = _dbus_string_get_byte (quoted, i);
212       
213       if (in_quotes)
214         {
215           if (b == '\'')
216             break;
217           else
218             {
219               if (!_dbus_string_append_byte (dest, b))
220                 return FALSE;
221             }
222         }
223       else
224         {
225           if (b == '\'')
226             in_quotes = TRUE;
227           else if (b == ' ' || b == '\n' || b == '\t')
228             break; /* end on whitespace if not quoted */
229           else
230             {
231               if (!_dbus_string_append_byte (dest, b))
232                 return FALSE;
233             }
234         }
235       
236       ++i;
237     }
238
239   if (new_pos)
240     *new_pos = i;
241   
242   if (!_dbus_string_append_byte (dest, '\0'))
243     return FALSE;
244   return TRUE;
245 }
246
247 static dbus_bool_t
248 append_saved_length (DBusString       *dest,
249                      DBusHashTable    *length_hash,
250                      const DBusString *name,
251                      int               offset,
252                      int               endian)
253 {
254   if (!save_offset (length_hash, name,
255                     offset, endian))
256     {
257       _dbus_warn ("failed to save offset to LENGTH\n");
258       return FALSE;
259     }
260   
261   if (!_dbus_marshal_uint32 (dest, endian,
262                              -1))
263     {
264       _dbus_warn ("failed to append a length\n");
265       return FALSE;
266     }
267
268   return TRUE;
269 }
270
271 static int
272 message_type_from_string (const DBusString *str,
273                           int               start)
274 {
275   const char *s;
276
277   s = _dbus_string_get_const_data_len (str, start,
278                                        _dbus_string_get_length (str) - start);
279
280   if (strncmp (s, "method_call", strlen ("method_call")) == 0)
281     return DBUS_MESSAGE_TYPE_METHOD_CALL;
282   else if (strncmp (s, "method_return", strlen ("method_return")) == 0)
283     return DBUS_MESSAGE_TYPE_METHOD_RETURN;
284   else if (strncmp (s, "signal", strlen ("signal")) == 0)
285     return DBUS_MESSAGE_TYPE_SIGNAL;
286   else if (strncmp (s, "error", strlen ("error")) == 0)
287     return DBUS_MESSAGE_TYPE_ERROR;
288   else if (strncmp (s, "invalid", strlen ("invalid")) == 0)
289     return DBUS_MESSAGE_TYPE_INVALID;
290   else
291     return -1;
292 }
293
294 static dbus_bool_t
295 append_string_field (DBusString *dest,
296                      int         endian,
297                      int         field,
298                      int         type,
299                      const char *value)
300 {
301   int len;
302   
303   if (!_dbus_string_append_byte (dest, field))
304     {
305       _dbus_warn ("couldn't append field name byte\n");
306       return FALSE;
307     }
308   
309   if (!_dbus_string_append_byte (dest, type))
310     {
311       _dbus_warn ("could not append typecode byte\n");
312       return FALSE;
313     }
314
315   len = strlen (value);
316
317   if (!_dbus_marshal_uint32 (dest, endian, len))
318     {
319       _dbus_warn ("couldn't append string length\n");
320       return FALSE;
321     }
322   
323   if (!_dbus_string_append (dest, value))
324     {
325       _dbus_warn ("couldn't append field value\n");
326       return FALSE;
327     }
328
329   if (!_dbus_string_append_byte (dest, 0))
330     {
331       _dbus_warn ("couldn't append string nul term\n");
332       return FALSE;
333     }
334
335   return TRUE;
336 }
337
338 static dbus_bool_t
339 parse_basic_type (DBusString *src, char type,
340                   DBusString *dest, dbus_bool_t *unalign,
341                   int endian)
342 {
343   int align;
344   int align_pad_start, align_pad_end;
345   unsigned char data[16];
346
347   switch (type)
348     {
349     case DBUS_TYPE_BYTE:
350     case DBUS_TYPE_BOOLEAN:
351       align = 1;
352       break;
353     case DBUS_TYPE_UINT32:
354     case DBUS_TYPE_INT32:
355       align = 4;
356       break;
357     case DBUS_TYPE_DOUBLE:
358       align = 8;
359       break;
360     default:
361       _dbus_assert_not_reached ("not a basic type");
362       break;
363     }
364
365   align_pad_start = _dbus_string_get_length (dest);
366   align_pad_end = _DBUS_ALIGN_VALUE (align_pad_start, align);
367
368   _dbus_string_delete_first_word (src);
369
370   if (!_dbus_string_parse_basic_type (src, type, 0, data, NULL))
371     {
372       _dbus_verbose ("failed to parse type '%c'", type);
373       return FALSE;
374     }
375
376   if (!_dbus_marshal_basic_type (dest, type, data, endian))
377     {
378       _dbus_verbose ("failed to marshal type '%c'", type);
379       return FALSE;
380     }
381
382   if (*unalign)
383     {
384       _dbus_string_delete (dest, align_pad_start,
385                            align_pad_end - align_pad_start);
386       *unalign = FALSE;
387     }
388
389   return TRUE;
390 }
391
392 static dbus_bool_t
393 parse_basic_array (DBusString *src, char type,
394                    DBusString *dest, dbus_bool_t *unalign,
395                    int endian)
396 {
397   int array_align, elem_size;
398   int i, len, allocated;
399   unsigned char *values, b;
400   int values_offset;
401   int align_pad_start, align_pad_end;
402   dbus_bool_t retval = FALSE;
403
404   array_align = 4; /* length */
405   switch (type)
406     {
407     case DBUS_TYPE_BYTE:
408     case DBUS_TYPE_BOOLEAN:
409       elem_size = 1;
410       break;
411     case DBUS_TYPE_UINT32:
412     case DBUS_TYPE_INT32:
413       elem_size = 4;
414       break;
415     case DBUS_TYPE_DOUBLE:
416       array_align = 8;
417       elem_size = 8;
418       break;
419     default:
420       _dbus_assert_not_reached ("not a basic type");
421       break;
422     }
423
424   align_pad_start = _dbus_string_get_length (dest);
425   align_pad_end = _DBUS_ALIGN_VALUE (align_pad_start, array_align);
426
427   len = 0;
428   allocated = 2;
429   values = NULL;
430   values_offset = 0;
431           
432   _dbus_string_delete_first_word (src);
433   _dbus_string_skip_blank (src, 0, &i);
434   b = _dbus_string_get_byte (src, i++);
435
436   if (b != '{')
437     goto failed;
438
439   while (i < _dbus_string_get_length (src))
440     {
441       _dbus_string_skip_blank (src, i, &i);
442
443       if (!values || len == allocated - 1)
444         {
445           allocated *= 2;
446           values = dbus_realloc (values, allocated * elem_size);
447           if (!values)
448             {
449               _dbus_warn ("could not allocate memory for '%c' ARRAY\n", type);
450               goto failed;
451             }
452         }
453
454       if (!_dbus_string_parse_basic_type (src, type, i, values + values_offset, &i))
455         {
456           _dbus_warn ("could not parse integer element %d of '%c' ARRAY\n", len, type);
457           goto failed;
458         }
459
460       values_offset += elem_size;
461       len++;
462               
463       _dbus_string_skip_blank (src, i, &i);
464
465       b = _dbus_string_get_byte (src, i++);
466
467       if (b == '}')
468         break;
469       else if (b != ',')
470         goto failed;
471     }
472
473   if (!_dbus_marshal_basic_type_array (dest, type, values, len, endian))
474     {
475       _dbus_warn ("failed to append '%c' ARRAY\n", type);
476       goto failed;
477     }
478
479   if (*unalign)
480     {
481       _dbus_string_delete (dest, align_pad_start,
482                            align_pad_end - align_pad_start);
483       *unalign = FALSE;
484     }
485
486   retval = TRUE;
487
488  failed:
489   dbus_free (values);
490   return retval;
491 }
492
493 static char
494 lookup_basic_type (const DBusString *str, dbus_bool_t *is_array)
495 {
496   int i;
497   char type = DBUS_TYPE_INVALID;
498   static struct {
499     const char *name;
500     char        type;
501   } name_to_type[] = {
502     { "BYTE",    DBUS_TYPE_BYTE },
503     { "BOOLEAN", DBUS_TYPE_BOOLEAN },
504     { "INT32",   DBUS_TYPE_INT32 },
505     { "UINT32",  DBUS_TYPE_UINT32 },
506     { "DOUBLE",  DBUS_TYPE_DOUBLE }
507   };
508
509   for (i = 0; i < _DBUS_N_ELEMENTS(name_to_type); i++)
510     {
511       const char *name = name_to_type[i].name;
512       if (_dbus_string_starts_with_c_str (str, name)) 
513         {
514           int offset = strlen (name);
515           type = name_to_type[i].type;
516           if (is_array)
517             *is_array = _dbus_string_find (str, offset, "_ARRAY", NULL);
518           break;
519         }
520     }
521
522   return type;
523 }
524
525 /**
526  * Reads the given filename, which should be in "message description
527  * language" (look at some examples), and builds up the message data
528  * from it.  The message data may be invalid, or valid.
529  *
530  * The parser isn't very strict, it's just a hack for test programs.
531  * 
532  * The file format is:
533  * @code
534  *   VALID_HEADER <type> normal header; byte order, type, padding, header len, body len, serial
535  *   REQUIRED_FIELDS add required fields with placeholder values
536  *   BIG_ENDIAN switch to big endian
537  *   LITTLE_ENDIAN switch to little endian
538  *   OPPOSITE_ENDIAN switch to opposite endian
539  *   ALIGN <N> aligns to the given value
540  *   UNALIGN skips alignment for the next marshal
541  *   BYTE <N> inserts the given integer in [0,255] or char in 'a' format
542  *   START_LENGTH <name> marks the start of a length to measure
543  *   END_LENGTH <name> records the length since START_LENGTH under the given name
544  *                     (or if no START_LENGTH, absolute length)
545  *   LENGTH <name> inserts the saved length of the same name
546  *   CHOP <N> chops last N bytes off the data
547  *   HEADER_FIELD <fieldname> inserts a header field name byte
548  *   TYPE <typename> inserts a typecode byte 
549  * @endcode
550  * 
551  * Following commands insert aligned data unless
552  * preceded by "UNALIGN":
553  * @code
554  *   INT32 <N> marshals an INT32
555  *   UINT32 <N> marshals a UINT32
556  *   INT64 <N> marshals an INT64
557  *   UINT64 <N> marshals a UINT64
558  *   DOUBLE <N> marshals a double
559  *   STRING 'Foo' marshals a string
560  *   OBJECT_PATH '/foo/bar' marshals an object path
561  *   BYTE_ARRAY { 'a', 3, 4, 5, 6} marshals a BYTE array
562  *   BOOLEAN_ARRAY { false, true, false} marshals a BOOLEAN array
563  *   INT32_ARRAY { 3, 4, 5, 6} marshals an INT32 array
564  *   UINT32_ARRAY { 3, 4, 5, 6} marshals an UINT32 array
565  *   DOUBLE_ARRAY { 1.0, 2.0, 3.0, 4.0} marshals a DOUBLE array  
566  *   STRING_ARRAY { "foo", "bar", "gazonk"} marshals a STRING array  
567  * @endcode
568  *
569  * @todo add support for array types INT32_ARRAY { 3, 4, 5, 6 }
570  * and so forth.
571  * 
572  * @param dest the string to append the message data to
573  * @param filename the filename to load
574  * @returns #TRUE on success
575  */
576 dbus_bool_t
577 _dbus_message_data_load (DBusString       *dest,
578                          const DBusString *filename)
579 {
580   DBusString file;
581   DBusError error;
582   DBusString line;
583   dbus_bool_t retval;
584   int line_no;
585   dbus_bool_t unalign;
586   DBusHashTable *length_hash;
587   int endian;
588   DBusHashIter iter;
589   char type;
590   dbus_bool_t is_array;
591   
592   retval = FALSE;
593   length_hash = NULL;
594   
595   if (!_dbus_string_init (&file))
596     return FALSE;
597
598   if (!_dbus_string_init (&line))
599     {
600       _dbus_string_free (&file);
601       return FALSE;
602     }
603
604   _dbus_verbose ("Loading %s\n", _dbus_string_get_const_data (filename));
605
606   dbus_error_init (&error);
607   if (!_dbus_file_get_contents (&file, filename, &error))
608     {
609       _dbus_warn ("Getting contents of %s failed: %s\n",
610                   _dbus_string_get_const_data (filename), error.message);
611       dbus_error_free (&error);
612       goto out;
613     }
614
615   length_hash = _dbus_hash_table_new (DBUS_HASH_STRING,
616                                       NULL,
617                                       free_saved_length);
618   if (length_hash == NULL)
619     goto out;
620   
621   endian = DBUS_COMPILER_BYTE_ORDER;
622   unalign = FALSE;
623   line_no = 0;
624  next_iteration:
625   while (_dbus_string_pop_line (&file, &line))
626     {
627       dbus_bool_t just_set_unalign;
628
629       just_set_unalign = FALSE;
630       line_no += 1;
631
632       _dbus_string_delete_leading_blanks (&line);
633
634       if (_dbus_string_get_length (&line) == 0)
635         {
636           /* empty line */
637           goto next_iteration;
638         }
639       else if (_dbus_string_starts_with_c_str (&line,
640                                                "#"))
641         {
642           /* Ignore this comment */
643           goto next_iteration;
644         }
645       else if (_dbus_string_starts_with_c_str (&line,
646                                                "VALID_HEADER"))
647         {
648           int i;
649           DBusString name;
650           int message_type;
651
652           if (_dbus_string_get_length (&line) < (int) strlen ("VALID_HEADER "))
653             {
654               _dbus_warn ("no args to VALID_HEADER\n");
655               goto parse_failed;
656             }
657           
658           if (!_dbus_string_append_byte (dest, endian))
659             {
660               _dbus_warn ("could not append endianness\n");
661               goto parse_failed;
662             }
663
664           message_type = message_type_from_string (&line,
665                                                    strlen ("VALID_HEADER "));
666           if (message_type < 0)
667             {
668               _dbus_warn ("VALID_HEADER not followed by space then known message type\n");
669               goto parse_failed;
670             }
671           
672           if (!_dbus_string_append_byte (dest, message_type))
673             {
674               _dbus_warn ("could not append message type\n");
675               goto parse_failed;
676             }
677           
678           i = 0;
679           while (i < 2)
680             {
681               if (!_dbus_string_append_byte (dest, '\0'))
682                 {
683                   _dbus_warn ("could not append nul pad\n");
684                   goto parse_failed;
685                 }
686               ++i;
687             }
688
689           _dbus_string_init_const (&name, "Header");
690           if (!append_saved_length (dest, length_hash,
691                                     &name, _dbus_string_get_length (dest),
692                                     endian))
693             goto parse_failed;
694
695           _dbus_string_init_const (&name, "Body");
696           if (!append_saved_length (dest, length_hash,
697                                     &name, _dbus_string_get_length (dest),
698                                     endian))
699             goto parse_failed;
700           
701           /* client serial */
702           if (!_dbus_marshal_uint32 (dest, endian, 1))
703             {
704               _dbus_warn ("couldn't append client serial\n");
705               goto parse_failed;
706             }
707         }
708       else if (_dbus_string_starts_with_c_str (&line,
709                                                "REQUIRED_FIELDS"))
710         {
711           if (!append_string_field (dest, endian,
712                                     DBUS_HEADER_FIELD_INTERFACE,
713                                     DBUS_TYPE_STRING,
714                                     "org.freedesktop.BlahBlahInterface"))
715             goto parse_failed;
716           if (!append_string_field (dest, endian,
717                                     DBUS_HEADER_FIELD_MEMBER,
718                                     DBUS_TYPE_STRING,
719                                     "BlahBlahMethod"))
720             goto parse_failed;
721           if (!append_string_field (dest, endian,
722                                     DBUS_HEADER_FIELD_PATH,
723                                     DBUS_TYPE_OBJECT_PATH,
724                                     "/blah/blah/path"))
725             goto parse_failed;
726         }
727       else if (_dbus_string_starts_with_c_str (&line,
728                                                "BIG_ENDIAN"))
729         {
730           endian = DBUS_BIG_ENDIAN;
731         }
732       else if (_dbus_string_starts_with_c_str (&line,
733                                                "LITTLE_ENDIAN"))
734         {
735           endian = DBUS_LITTLE_ENDIAN;
736         }
737       else if (_dbus_string_starts_with_c_str (&line,
738                                                "OPPOSITE_ENDIAN"))
739         {
740           if (endian == DBUS_BIG_ENDIAN)
741             endian = DBUS_LITTLE_ENDIAN;
742           else
743             endian = DBUS_BIG_ENDIAN;
744         }
745       else if (_dbus_string_starts_with_c_str (&line,
746                                                "ALIGN"))
747         {
748           long val;
749           int end;
750           int orig_len;
751           
752           _dbus_string_delete_first_word (&line);
753
754           if (!_dbus_string_parse_int (&line, 0, &val, &end))
755             {
756               _dbus_warn ("Failed to parse integer\n");
757               goto parse_failed;
758             }
759
760           if (val > 8)
761             {
762               _dbus_warn ("Aligning to %ld boundary is crack\n",
763                           val);
764               goto parse_failed;
765             }
766
767           orig_len = _dbus_string_get_length (dest);
768           
769           if (!_dbus_string_align_length (dest, val))
770             goto parse_failed;
771
772           if (_dbus_string_parse_int (&line, end, &val, NULL))
773             {
774               /* If there's an optional second int argument,
775                * fill in align padding with that value
776                */
777               if (val < 0 || val > 255)
778                 {
779                   _dbus_warn ("can't fill align padding with %ld, must be a byte value\n", val);
780                   goto parse_failed;
781                 }
782
783               end = orig_len;
784               while (end < _dbus_string_get_length (dest))
785                 {
786                   _dbus_string_set_byte (dest, end, val);
787                   ++end;
788                 }
789             }
790         }
791       else if (_dbus_string_starts_with_c_str (&line, "UNALIGN"))
792         {
793           unalign = TRUE;
794           just_set_unalign = TRUE;
795         }
796       else if (_dbus_string_starts_with_c_str (&line, "CHOP"))
797         {
798           long val;
799
800           /* FIXME if you CHOP the offset for a LENGTH
801            * command, we segfault.
802            */
803           
804           _dbus_string_delete_first_word (&line);
805
806           if (!_dbus_string_parse_int (&line, 0, &val, NULL))
807             {
808               _dbus_warn ("Failed to parse integer to chop\n");
809               goto parse_failed;
810             }
811
812           if (val > _dbus_string_get_length (dest))
813             {
814               _dbus_warn ("Trying to chop %ld bytes but we only have %d\n",
815                           val,
816                           _dbus_string_get_length (dest));
817               goto parse_failed;
818             }
819           
820           _dbus_string_shorten (dest, val);
821         }
822       else if (_dbus_string_starts_with_c_str (&line,
823                                                "START_LENGTH"))
824         {
825           _dbus_string_delete_first_word (&line);
826
827           if (!save_start (length_hash, &line,
828                            _dbus_string_get_length (dest)))
829             {
830               _dbus_warn ("failed to save length start\n");
831               goto parse_failed;
832             }
833         }
834       else if (_dbus_string_starts_with_c_str (&line,
835                                                "END_LENGTH"))
836         {
837           _dbus_string_delete_first_word (&line);
838
839           if (!save_length (length_hash, &line,
840                             _dbus_string_get_length (dest)))
841             {
842               _dbus_warn ("failed to save length end\n");
843               goto parse_failed;
844             }
845         }
846       else if (_dbus_string_starts_with_c_str (&line,
847                                                "LENGTH"))
848         {
849           SAVE_FOR_UNALIGN (dest, 4);
850           
851           _dbus_string_delete_first_word (&line);
852
853           if (!append_saved_length (dest, length_hash,
854                                     &line,
855                                     unalign ? align_pad_start : align_pad_end,
856                                     endian))
857             {
858               _dbus_warn ("failed to add LENGTH\n");
859               goto parse_failed;
860             }
861
862           PERFORM_UNALIGN (dest);
863         }
864       else if (_dbus_string_starts_with_c_str (&line,
865                                                "HEADER_FIELD"))
866         {
867           int field;
868
869           _dbus_string_delete_first_word (&line);
870
871           if (_dbus_string_starts_with_c_str (&line, "INVALID"))
872             field = DBUS_HEADER_FIELD_INVALID;
873           else if (_dbus_string_starts_with_c_str (&line, "PATH"))
874             field = DBUS_HEADER_FIELD_PATH;
875           else if (_dbus_string_starts_with_c_str (&line, "INTERFACE"))
876             field = DBUS_HEADER_FIELD_INTERFACE;
877           else if (_dbus_string_starts_with_c_str (&line, "MEMBER"))
878             field = DBUS_HEADER_FIELD_MEMBER;
879           else if (_dbus_string_starts_with_c_str (&line, "ERROR_NAME"))
880             field = DBUS_HEADER_FIELD_ERROR_NAME;
881           else if (_dbus_string_starts_with_c_str (&line, "REPLY_SERIAL"))
882             field = DBUS_HEADER_FIELD_REPLY_SERIAL;
883           else if (_dbus_string_starts_with_c_str (&line, "DESTINATION"))
884             field = DBUS_HEADER_FIELD_DESTINATION;
885           else if (_dbus_string_starts_with_c_str (&line, "SENDER"))
886             field = DBUS_HEADER_FIELD_SENDER;
887           else if (_dbus_string_starts_with_c_str (&line, "UNKNOWN"))
888             field = 22; /* random unknown header field */
889           else
890             {
891               _dbus_warn ("%s is not a valid header field name\n",
892                           _dbus_string_get_const_data (&line));
893               goto parse_failed;
894             }
895
896           if (!_dbus_string_append_byte (dest, field))
897             {
898               _dbus_warn ("could not append header field name byte\n");
899               goto parse_failed;
900             }
901         }
902       else if (_dbus_string_starts_with_c_str (&line,
903                                                "TYPE"))
904         {
905           int code;
906           
907           _dbus_string_delete_first_word (&line);          
908
909           if (_dbus_string_starts_with_c_str (&line, "INVALID"))
910             code = DBUS_TYPE_INVALID;
911           else if (_dbus_string_starts_with_c_str (&line, "NIL"))
912             code = DBUS_TYPE_NIL;
913           else if ((code = lookup_basic_type (&line, NULL)) != DBUS_TYPE_INVALID)
914             ;
915           else if (_dbus_string_starts_with_c_str (&line, "STRING"))
916             code = DBUS_TYPE_STRING;
917           else if (_dbus_string_starts_with_c_str (&line, "OBJECT_PATH"))
918             code = DBUS_TYPE_OBJECT_PATH;
919           else if (_dbus_string_starts_with_c_str (&line, "CUSTOM"))
920             code = DBUS_TYPE_CUSTOM;
921           else if (_dbus_string_starts_with_c_str (&line, "ARRAY"))
922             code = DBUS_TYPE_ARRAY;
923           else if (_dbus_string_starts_with_c_str (&line, "DICT"))
924             code = DBUS_TYPE_DICT;
925           else
926             {
927               _dbus_warn ("%s is not a valid type name\n", _dbus_string_get_const_data (&line));
928               goto parse_failed;
929             }
930
931           if (!_dbus_string_append_byte (dest, code))
932             {
933               _dbus_warn ("could not append typecode byte\n");
934               goto parse_failed;
935             }
936         }
937       else if (_dbus_string_starts_with_c_str (&line,
938                                                "STRING_ARRAY"))
939         {
940           SAVE_FOR_UNALIGN (dest, 4);
941           int i, len, allocated;
942           char **values;
943           char *val;
944           DBusString val_str;
945           unsigned char b;
946
947           allocated = 4;
948           values = dbus_new (char *, allocated);
949           if (!values)
950             {
951               _dbus_warn ("could not allocate memory for STRING_ARRAY\n");
952               goto parse_failed;
953             }
954           
955           len = 0;
956           
957           _dbus_string_delete_first_word (&line);
958           _dbus_string_skip_blank (&line, 0, &i);
959           b = _dbus_string_get_byte (&line, i++);
960
961           if (b != '{')
962             goto parse_failed;
963
964           _dbus_string_init (&val_str);
965           while (i < _dbus_string_get_length (&line))
966             {
967               _dbus_string_skip_blank (&line, i, &i);
968
969               if (!append_quoted_string (&val_str, &line, i, &i))
970                 {
971                   _dbus_warn ("could not parse quoted string for STRING_ARRAY\n");
972                   goto parse_failed;
973                 }
974               i++;
975
976               if (!_dbus_string_steal_data (&val_str, &val))
977                 {
978                   _dbus_warn ("could not allocate memory for STRING_ARRAY string\n");
979                   goto parse_failed;
980                 }
981               
982               values[len++] = val;
983               if (len == allocated)
984                 {
985                   allocated *= 2;
986                   values = dbus_realloc (values, allocated * sizeof (char *));
987                   if (!values)
988                     {
989                       _dbus_warn ("could not allocate memory for STRING_ARRAY\n");
990                       goto parse_failed;
991                     }
992                 }
993               
994               _dbus_string_skip_blank (&line, i, &i);
995               
996               b = _dbus_string_get_byte (&line, i++);
997
998               if (b == '}')
999                 break;
1000               else if (b != ',')
1001                 {
1002                   _dbus_warn ("missing comma when parsing STRING_ARRAY\n");
1003                   goto parse_failed;
1004                 }
1005             }
1006           _dbus_string_free (&val_str);
1007           
1008           if (!_dbus_marshal_string_array (dest, endian, (const char **)values, len))
1009             {
1010               _dbus_warn ("failed to append STRING_ARRAY\n");
1011               goto parse_failed;
1012             }
1013
1014           values[len] = NULL;
1015           dbus_free_string_array (values);
1016           
1017           PERFORM_UNALIGN (dest);
1018         }
1019       else if (_dbus_string_starts_with_c_str (&line,
1020                                                "STRING"))
1021         {
1022           SAVE_FOR_UNALIGN (dest, 4);
1023           int size_offset;
1024           int old_len;
1025           
1026           _dbus_string_delete_first_word (&line);
1027
1028           size_offset = _dbus_string_get_length (dest);
1029           size_offset = _DBUS_ALIGN_VALUE (size_offset, 4);
1030           if (!_dbus_marshal_uint32 (dest, endian, 0))
1031             {
1032               _dbus_warn ("Failed to append string size\n");
1033               goto parse_failed;
1034             }
1035
1036           old_len = _dbus_string_get_length (dest);
1037           if (!append_quoted_string (dest, &line, 0, NULL))
1038             {
1039               _dbus_warn ("Failed to append quoted string\n");
1040               goto parse_failed;
1041             }
1042
1043           _dbus_marshal_set_uint32 (dest, endian, size_offset,
1044                                     /* subtract 1 for nul */
1045                                     _dbus_string_get_length (dest) - old_len - 1);
1046           
1047           PERFORM_UNALIGN (dest);
1048         }
1049       else if ((type = lookup_basic_type (&line, &is_array)) != DBUS_TYPE_INVALID)
1050         {
1051           if (is_array)
1052             {
1053               if (!parse_basic_array (&line, type, dest, &unalign, endian))
1054                 goto parse_failed;
1055             }
1056           else
1057             {
1058               if (!parse_basic_type (&line, type, dest, &unalign, endian))
1059                 goto parse_failed;
1060             }
1061         }
1062       else if (_dbus_string_starts_with_c_str (&line,
1063                                               "OBJECT_PATH"))
1064         {
1065           SAVE_FOR_UNALIGN (dest, 4);
1066           int size_offset;
1067           int old_len;
1068           
1069           _dbus_string_delete_first_word (&line);
1070           
1071           size_offset = _dbus_string_get_length (dest);
1072           size_offset = _DBUS_ALIGN_VALUE (size_offset, 4);
1073           if (!_dbus_marshal_uint32 (dest, endian, 0))
1074             {
1075               _dbus_warn ("Failed to append string size\n");
1076               goto parse_failed;
1077             }
1078
1079           old_len = _dbus_string_get_length (dest);
1080           if (!append_quoted_string (dest, &line, 0, NULL))
1081             {
1082               _dbus_warn ("Failed to append quoted string\n");
1083               goto parse_failed;
1084             }
1085
1086           _dbus_marshal_set_uint32 (dest, endian, size_offset,
1087                                     /* subtract 1 for nul */
1088                                     _dbus_string_get_length (dest) - old_len - 1);
1089           
1090           PERFORM_UNALIGN (dest);
1091         }      
1092       else
1093         goto parse_failed;
1094       
1095       if (!just_set_unalign && unalign)
1096         {
1097           _dbus_warn ("UNALIGN prior to something that isn't aligned\n");
1098           goto parse_failed;
1099         }
1100
1101       goto next_iteration; /* skip parse_failed */
1102       
1103     parse_failed:
1104       {
1105         _dbus_warn ("couldn't process line %d \"%s\"\n",
1106                     line_no, _dbus_string_get_const_data (&line));
1107         goto out;
1108       }
1109     }
1110
1111   _dbus_hash_iter_init (length_hash, &iter);
1112   while (_dbus_hash_iter_next (&iter))
1113     {
1114       SavedLength *sl = _dbus_hash_iter_get_value (&iter);
1115       const char *s;
1116
1117       s = _dbus_string_get_const_data (&sl->name);
1118       
1119       if (sl->length < 0)
1120         {
1121           _dbus_warn ("Used LENGTH %s but never did END_LENGTH\n",
1122                       s);
1123           goto out;
1124         }
1125       else if (sl->offset < 0)
1126         {
1127           _dbus_warn ("Did END_LENGTH %s but never used LENGTH\n",
1128                       s);
1129           goto out;
1130         }
1131       else
1132         {
1133           if (sl->start < 0)
1134             sl->start = 0;
1135           
1136           _dbus_verbose ("Filling in length %s endian = %d offset = %d start = %d length = %d\n",
1137                          s, sl->endian, sl->offset, sl->start, sl->length);
1138           _dbus_marshal_set_int32 (dest,
1139                                    sl->endian,
1140                                    sl->offset,
1141                                    sl->length - sl->start);
1142         }
1143
1144       _dbus_hash_iter_remove_entry (&iter);
1145     }
1146   
1147   retval = TRUE;
1148
1149   _dbus_verbose_bytes_of_string (dest, 0, _dbus_string_get_length (dest));
1150   
1151  out:
1152   if (length_hash != NULL)
1153     _dbus_hash_table_unref (length_hash);
1154   
1155   _dbus_string_free (&file);
1156   _dbus_string_free (&line);
1157   return retval;
1158 }
1159
1160 /** @} */
1161 #endif /* DBUS_BUILD_TESTS */