2004-08-09 Havoc Pennington <hp@redhat.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, 2004 Red Hat, Inc.
5  *
6  * Licensed under the Academic Free License version 2.1
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           /* FIXME later we'll validate this, and then it will break
728            * and the .message files will have to include the right thing
729            */
730           if (!append_string_field (dest, endian,
731                                     DBUS_HEADER_FIELD_SIGNATURE,
732                                     DBUS_TYPE_STRING,
733                                     "iii"))
734             goto parse_failed;
735         }
736       else if (_dbus_string_starts_with_c_str (&line,
737                                                "BIG_ENDIAN"))
738         {
739           endian = DBUS_BIG_ENDIAN;
740         }
741       else if (_dbus_string_starts_with_c_str (&line,
742                                                "LITTLE_ENDIAN"))
743         {
744           endian = DBUS_LITTLE_ENDIAN;
745         }
746       else if (_dbus_string_starts_with_c_str (&line,
747                                                "OPPOSITE_ENDIAN"))
748         {
749           if (endian == DBUS_BIG_ENDIAN)
750             endian = DBUS_LITTLE_ENDIAN;
751           else
752             endian = DBUS_BIG_ENDIAN;
753         }
754       else if (_dbus_string_starts_with_c_str (&line,
755                                                "ALIGN"))
756         {
757           long val;
758           int end;
759           int orig_len;
760           
761           _dbus_string_delete_first_word (&line);
762
763           if (!_dbus_string_parse_int (&line, 0, &val, &end))
764             {
765               _dbus_warn ("Failed to parse integer\n");
766               goto parse_failed;
767             }
768
769           if (val > 8)
770             {
771               _dbus_warn ("Aligning to %ld boundary is crack\n",
772                           val);
773               goto parse_failed;
774             }
775
776           orig_len = _dbus_string_get_length (dest);
777           
778           if (!_dbus_string_align_length (dest, val))
779             goto parse_failed;
780
781           if (_dbus_string_parse_int (&line, end, &val, NULL))
782             {
783               /* If there's an optional second int argument,
784                * fill in align padding with that value
785                */
786               if (val < 0 || val > 255)
787                 {
788                   _dbus_warn ("can't fill align padding with %ld, must be a byte value\n", val);
789                   goto parse_failed;
790                 }
791
792               end = orig_len;
793               while (end < _dbus_string_get_length (dest))
794                 {
795                   _dbus_string_set_byte (dest, end, val);
796                   ++end;
797                 }
798             }
799         }
800       else if (_dbus_string_starts_with_c_str (&line, "UNALIGN"))
801         {
802           unalign = TRUE;
803           just_set_unalign = TRUE;
804         }
805       else if (_dbus_string_starts_with_c_str (&line, "CHOP"))
806         {
807           long val;
808
809           /* FIXME if you CHOP the offset for a LENGTH
810            * command, we segfault.
811            */
812           
813           _dbus_string_delete_first_word (&line);
814
815           if (!_dbus_string_parse_int (&line, 0, &val, NULL))
816             {
817               _dbus_warn ("Failed to parse integer to chop\n");
818               goto parse_failed;
819             }
820
821           if (val > _dbus_string_get_length (dest))
822             {
823               _dbus_warn ("Trying to chop %ld bytes but we only have %d\n",
824                           val,
825                           _dbus_string_get_length (dest));
826               goto parse_failed;
827             }
828           
829           _dbus_string_shorten (dest, val);
830         }
831       else if (_dbus_string_starts_with_c_str (&line,
832                                                "START_LENGTH"))
833         {
834           _dbus_string_delete_first_word (&line);
835
836           if (!save_start (length_hash, &line,
837                            _dbus_string_get_length (dest)))
838             {
839               _dbus_warn ("failed to save length start\n");
840               goto parse_failed;
841             }
842         }
843       else if (_dbus_string_starts_with_c_str (&line,
844                                                "END_LENGTH"))
845         {
846           _dbus_string_delete_first_word (&line);
847
848           if (!save_length (length_hash, &line,
849                             _dbus_string_get_length (dest)))
850             {
851               _dbus_warn ("failed to save length end\n");
852               goto parse_failed;
853             }
854         }
855       else if (_dbus_string_starts_with_c_str (&line,
856                                                "LENGTH"))
857         {
858           SAVE_FOR_UNALIGN (dest, 4);
859           
860           _dbus_string_delete_first_word (&line);
861
862           if (!append_saved_length (dest, length_hash,
863                                     &line,
864                                     unalign ? align_pad_start : align_pad_end,
865                                     endian))
866             {
867               _dbus_warn ("failed to add LENGTH\n");
868               goto parse_failed;
869             }
870
871           PERFORM_UNALIGN (dest);
872         }
873       else if (_dbus_string_starts_with_c_str (&line,
874                                                "HEADER_FIELD"))
875         {
876           int field;
877
878           _dbus_string_delete_first_word (&line);
879
880           if (_dbus_string_starts_with_c_str (&line, "INVALID"))
881             field = DBUS_HEADER_FIELD_INVALID;
882           else if (_dbus_string_starts_with_c_str (&line, "PATH"))
883             field = DBUS_HEADER_FIELD_PATH;
884           else if (_dbus_string_starts_with_c_str (&line, "INTERFACE"))
885             field = DBUS_HEADER_FIELD_INTERFACE;
886           else if (_dbus_string_starts_with_c_str (&line, "MEMBER"))
887             field = DBUS_HEADER_FIELD_MEMBER;
888           else if (_dbus_string_starts_with_c_str (&line, "ERROR_NAME"))
889             field = DBUS_HEADER_FIELD_ERROR_NAME;
890           else if (_dbus_string_starts_with_c_str (&line, "REPLY_SERIAL"))
891             field = DBUS_HEADER_FIELD_REPLY_SERIAL;
892           else if (_dbus_string_starts_with_c_str (&line, "DESTINATION"))
893             field = DBUS_HEADER_FIELD_DESTINATION;
894           else if (_dbus_string_starts_with_c_str (&line, "SENDER"))
895             field = DBUS_HEADER_FIELD_SENDER;
896           else if (_dbus_string_starts_with_c_str (&line, "SIGNATURE"))
897             field = DBUS_HEADER_FIELD_SIGNATURE;
898           else if (_dbus_string_starts_with_c_str (&line, "UNKNOWN"))
899             field = 22; /* random unknown header field */
900           else
901             {
902               _dbus_warn ("%s is not a valid header field name\n",
903                           _dbus_string_get_const_data (&line));
904               goto parse_failed;
905             }
906
907           if (!_dbus_string_append_byte (dest, field))
908             {
909               _dbus_warn ("could not append header field name byte\n");
910               goto parse_failed;
911             }
912         }
913       else if (_dbus_string_starts_with_c_str (&line,
914                                                "TYPE"))
915         {
916           int code;
917           
918           _dbus_string_delete_first_word (&line);          
919
920           if (_dbus_string_starts_with_c_str (&line, "INVALID"))
921             code = DBUS_TYPE_INVALID;
922           else if (_dbus_string_starts_with_c_str (&line, "NIL"))
923             code = DBUS_TYPE_NIL;
924           else if ((code = lookup_basic_type (&line, NULL)) != DBUS_TYPE_INVALID)
925             ;
926           else if (_dbus_string_starts_with_c_str (&line, "STRING"))
927             code = DBUS_TYPE_STRING;
928           else if (_dbus_string_starts_with_c_str (&line, "OBJECT_PATH"))
929             code = DBUS_TYPE_OBJECT_PATH;
930           else if (_dbus_string_starts_with_c_str (&line, "CUSTOM"))
931             code = DBUS_TYPE_CUSTOM;
932           else if (_dbus_string_starts_with_c_str (&line, "ARRAY"))
933             code = DBUS_TYPE_ARRAY;
934           else if (_dbus_string_starts_with_c_str (&line, "DICT"))
935             code = DBUS_TYPE_DICT;
936           else
937             {
938               _dbus_warn ("%s is not a valid type name\n", _dbus_string_get_const_data (&line));
939               goto parse_failed;
940             }
941
942           if (!_dbus_string_append_byte (dest, code))
943             {
944               _dbus_warn ("could not append typecode byte\n");
945               goto parse_failed;
946             }
947         }
948       else if (_dbus_string_starts_with_c_str (&line,
949                                                "STRING_ARRAY"))
950         {
951           SAVE_FOR_UNALIGN (dest, 4);
952           int i, len, allocated;
953           char **values;
954           char *val;
955           DBusString val_str;
956           unsigned char b;
957
958           allocated = 4;
959           values = dbus_new (char *, allocated);
960           if (!values)
961             {
962               _dbus_warn ("could not allocate memory for STRING_ARRAY\n");
963               goto parse_failed;
964             }
965           
966           len = 0;
967           
968           _dbus_string_delete_first_word (&line);
969           _dbus_string_skip_blank (&line, 0, &i);
970           b = _dbus_string_get_byte (&line, i++);
971
972           if (b != '{')
973             goto parse_failed;
974
975           _dbus_string_init (&val_str);
976           while (i < _dbus_string_get_length (&line))
977             {
978               _dbus_string_skip_blank (&line, i, &i);
979
980               if (!append_quoted_string (&val_str, &line, i, &i))
981                 {
982                   _dbus_warn ("could not parse quoted string for STRING_ARRAY\n");
983                   goto parse_failed;
984                 }
985               i++;
986
987               if (!_dbus_string_steal_data (&val_str, &val))
988                 {
989                   _dbus_warn ("could not allocate memory for STRING_ARRAY string\n");
990                   goto parse_failed;
991                 }
992               
993               values[len++] = val;
994               if (len == allocated)
995                 {
996                   allocated *= 2;
997                   values = dbus_realloc (values, allocated * sizeof (char *));
998                   if (!values)
999                     {
1000                       _dbus_warn ("could not allocate memory for STRING_ARRAY\n");
1001                       goto parse_failed;
1002                     }
1003                 }
1004               
1005               _dbus_string_skip_blank (&line, i, &i);
1006               
1007               b = _dbus_string_get_byte (&line, i++);
1008
1009               if (b == '}')
1010                 break;
1011               else if (b != ',')
1012                 {
1013                   _dbus_warn ("missing comma when parsing STRING_ARRAY\n");
1014                   goto parse_failed;
1015                 }
1016             }
1017           _dbus_string_free (&val_str);
1018           
1019           if (!_dbus_marshal_string_array (dest, endian, (const char **)values, len))
1020             {
1021               _dbus_warn ("failed to append STRING_ARRAY\n");
1022               goto parse_failed;
1023             }
1024
1025           values[len] = NULL;
1026           dbus_free_string_array (values);
1027           
1028           PERFORM_UNALIGN (dest);
1029         }
1030       else if (_dbus_string_starts_with_c_str (&line,
1031                                                "STRING"))
1032         {
1033           SAVE_FOR_UNALIGN (dest, 4);
1034           int size_offset;
1035           int old_len;
1036           
1037           _dbus_string_delete_first_word (&line);
1038
1039           size_offset = _dbus_string_get_length (dest);
1040           size_offset = _DBUS_ALIGN_VALUE (size_offset, 4);
1041           if (!_dbus_marshal_uint32 (dest, endian, 0))
1042             {
1043               _dbus_warn ("Failed to append string size\n");
1044               goto parse_failed;
1045             }
1046
1047           old_len = _dbus_string_get_length (dest);
1048           if (!append_quoted_string (dest, &line, 0, NULL))
1049             {
1050               _dbus_warn ("Failed to append quoted string\n");
1051               goto parse_failed;
1052             }
1053
1054           _dbus_marshal_set_uint32 (dest, endian, size_offset,
1055                                     /* subtract 1 for nul */
1056                                     _dbus_string_get_length (dest) - old_len - 1);
1057           
1058           PERFORM_UNALIGN (dest);
1059         }
1060       else if ((type = lookup_basic_type (&line, &is_array)) != DBUS_TYPE_INVALID)
1061         {
1062           if (is_array)
1063             {
1064               if (!parse_basic_array (&line, type, dest, &unalign, endian))
1065                 goto parse_failed;
1066             }
1067           else
1068             {
1069               if (!parse_basic_type (&line, type, dest, &unalign, endian))
1070                 goto parse_failed;
1071             }
1072         }
1073       else if (_dbus_string_starts_with_c_str (&line,
1074                                               "OBJECT_PATH"))
1075         {
1076           SAVE_FOR_UNALIGN (dest, 4);
1077           int size_offset;
1078           int old_len;
1079           
1080           _dbus_string_delete_first_word (&line);
1081           
1082           size_offset = _dbus_string_get_length (dest);
1083           size_offset = _DBUS_ALIGN_VALUE (size_offset, 4);
1084           if (!_dbus_marshal_uint32 (dest, endian, 0))
1085             {
1086               _dbus_warn ("Failed to append string size\n");
1087               goto parse_failed;
1088             }
1089
1090           old_len = _dbus_string_get_length (dest);
1091           if (!append_quoted_string (dest, &line, 0, NULL))
1092             {
1093               _dbus_warn ("Failed to append quoted string\n");
1094               goto parse_failed;
1095             }
1096
1097           _dbus_marshal_set_uint32 (dest, endian, size_offset,
1098                                     /* subtract 1 for nul */
1099                                     _dbus_string_get_length (dest) - old_len - 1);
1100           
1101           PERFORM_UNALIGN (dest);
1102         }      
1103       else
1104         goto parse_failed;
1105       
1106       if (!just_set_unalign && unalign)
1107         {
1108           _dbus_warn ("UNALIGN prior to something that isn't aligned\n");
1109           goto parse_failed;
1110         }
1111
1112       goto next_iteration; /* skip parse_failed */
1113       
1114     parse_failed:
1115       {
1116         _dbus_warn ("couldn't process line %d \"%s\"\n",
1117                     line_no, _dbus_string_get_const_data (&line));
1118         goto out;
1119       }
1120     }
1121
1122   _dbus_hash_iter_init (length_hash, &iter);
1123   while (_dbus_hash_iter_next (&iter))
1124     {
1125       SavedLength *sl = _dbus_hash_iter_get_value (&iter);
1126       const char *s;
1127
1128       s = _dbus_string_get_const_data (&sl->name);
1129       
1130       if (sl->length < 0)
1131         {
1132           _dbus_warn ("Used LENGTH %s but never did END_LENGTH\n",
1133                       s);
1134           goto out;
1135         }
1136       else if (sl->offset < 0)
1137         {
1138           _dbus_warn ("Did END_LENGTH %s but never used LENGTH\n",
1139                       s);
1140           goto out;
1141         }
1142       else
1143         {
1144           if (sl->start < 0)
1145             sl->start = 0;
1146           
1147           _dbus_verbose ("Filling in length %s endian = %d offset = %d start = %d length = %d\n",
1148                          s, sl->endian, sl->offset, sl->start, sl->length);
1149           _dbus_marshal_set_int32 (dest,
1150                                    sl->endian,
1151                                    sl->offset,
1152                                    sl->length - sl->start);
1153         }
1154
1155       _dbus_hash_iter_remove_entry (&iter);
1156     }
1157   
1158   retval = TRUE;
1159
1160   _dbus_verbose_bytes_of_string (dest, 0, _dbus_string_get_length (dest));
1161   
1162  out:
1163   if (length_hash != NULL)
1164     _dbus_hash_table_unref (length_hash);
1165   
1166   _dbus_string_free (&file);
1167   _dbus_string_free (&line);
1168   return retval;
1169 }
1170
1171 /** @} */
1172 #endif /* DBUS_BUILD_TESTS */