2003-08-01 Havoc Pennington <hp@pobox.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 1.2
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 typedef struct
44 {
45   DBusString name;
46   int start;  /**< Calculate length since here */
47   int length; /**< length to write */
48   int offset; /**< where to write it into the data */
49   int endian; /**< endianness to write with */
50 } SavedLength;
51
52 static void
53 free_saved_length (void *data)
54 {
55   SavedLength *sl = data;
56
57   if (sl == NULL)
58     return; /* all hash free functions have to accept NULL */
59   
60   _dbus_string_free (&sl->name);
61   dbus_free (sl);
62 }
63
64 static SavedLength*
65 ensure_saved_length (DBusHashTable    *hash,
66                      const DBusString *name)
67 {
68   SavedLength *sl;
69   const char *s;
70   
71   s = _dbus_string_get_const_data (name);
72
73   sl = _dbus_hash_table_lookup_string (hash, s);
74   if (sl != NULL)
75     return sl;
76   
77   sl = dbus_new0 (SavedLength, 1);
78
79   if (!_dbus_string_init (&sl->name))
80     {
81       dbus_free (sl);
82       return NULL;
83     }
84
85   if (!_dbus_string_copy (name, 0, &sl->name, 0))
86     goto failed;
87
88   s = _dbus_string_get_const_data (&sl->name);
89
90   if (!_dbus_hash_table_insert_string (hash, (char*)s, sl))
91     goto failed;
92
93   sl->start = -1;
94   sl->length = -1;
95   sl->offset = -1;
96   sl->endian = -1;
97   
98   return sl;
99   
100  failed:
101   free_saved_length (sl);
102   return NULL;
103 }
104
105 static dbus_bool_t
106 save_start (DBusHashTable    *hash,
107             const DBusString *name,
108             int               start)
109 {
110   SavedLength *sl;
111
112   sl = ensure_saved_length (hash, name);
113
114   if (sl == NULL)
115     return FALSE;
116   else if (sl->start >= 0)
117     {
118       _dbus_warn ("Same START_LENGTH given twice\n");
119       return FALSE;
120     }
121   else
122     sl->start = start;
123
124   return TRUE;
125 }
126
127 static dbus_bool_t
128 save_length (DBusHashTable    *hash,
129              const DBusString *name,
130              int               length)
131 {
132   SavedLength *sl;
133
134   sl = ensure_saved_length (hash, name);
135
136   if (sl == NULL)
137     return FALSE;
138   else if (sl->length >= 0)
139     {
140       _dbus_warn ("Same END_LENGTH given twice\n");
141       return FALSE;
142     }
143   else
144     sl->length = length;
145
146   return TRUE;
147 }
148
149 static dbus_bool_t
150 save_offset (DBusHashTable    *hash,
151              const DBusString *name,
152              int               offset,
153              int               endian)
154 {
155   SavedLength *sl;
156
157   sl = ensure_saved_length (hash, name);
158
159   if (sl == NULL)
160     return FALSE;
161   else if (sl->offset >= 0)
162     {
163       _dbus_warn ("Same LENGTH given twice\n");
164       return FALSE;
165     }
166   else
167     {
168       sl->offset = offset;
169       sl->endian = endian;
170     }
171
172   return TRUE;
173 }
174
175 /** Saves the segment to delete in order to unalign the next item */
176 #define SAVE_FOR_UNALIGN(str, boundary)                                 \
177   int align_pad_start = _dbus_string_get_length (str);                  \
178   int align_pad_end = _DBUS_ALIGN_VALUE (align_pad_start, (boundary))
179
180 /** Deletes the alignment padding */
181 #define PERFORM_UNALIGN(str)                                    \
182   if (unalign)                                                  \
183     {                                                           \
184       _dbus_string_delete ((str), align_pad_start,              \
185                            align_pad_end - align_pad_start);    \
186       unalign = FALSE;                                          \
187     }
188
189
190 static dbus_bool_t
191 append_quoted_string (DBusString       *dest,
192                       const DBusString *quoted,
193                       int               start_pos,
194                       int              *new_pos)
195 {
196   dbus_bool_t in_quotes = FALSE;
197   int i;
198
199   /* FIXME: We might want to add escaping in case we want to put '
200    * characters in our strings.
201    */
202   
203   i = start_pos;
204   while (i < _dbus_string_get_length (quoted))
205     {
206       unsigned char b;
207
208       b = _dbus_string_get_byte (quoted, i);
209       
210       if (in_quotes)
211         {
212           if (b == '\'')
213             break;
214           else
215             {
216               if (!_dbus_string_append_byte (dest, b))
217                 return FALSE;
218             }
219         }
220       else
221         {
222           if (b == '\'')
223             in_quotes = TRUE;
224           else if (b == ' ' || b == '\n' || b == '\t')
225             break; /* end on whitespace if not quoted */
226           else
227             {
228               if (!_dbus_string_append_byte (dest, b))
229                 return FALSE;
230             }
231         }
232       
233       ++i;
234     }
235
236   if (new_pos)
237     *new_pos = i;
238   
239   if (!_dbus_string_append_byte (dest, '\0'))
240     return FALSE;
241   return TRUE;
242 }
243
244 static dbus_bool_t
245 append_saved_length (DBusString       *dest,
246                      DBusHashTable    *length_hash,
247                      const DBusString *name,
248                      int               offset,
249                      int               endian)
250 {
251   if (!save_offset (length_hash, name,
252                     offset, endian))
253     {
254       _dbus_warn ("failed to save offset to LENGTH\n");
255       return FALSE;
256     }
257   
258   if (!_dbus_marshal_uint32 (dest, endian,
259                              -1))
260     {
261       _dbus_warn ("failed to append a length\n");
262       return FALSE;
263     }
264
265   return TRUE;
266 }
267
268 /**
269  * Reads the given filename, which should be in "message description
270  * language" (look at some examples), and builds up the message data
271  * from it.  The message data may be invalid, or valid.
272  *
273  * The parser isn't very strict, it's just a hack for test programs.
274  * 
275  * The file format is:
276  * @code
277  *   VALID_HEADER normal header; byte order, padding, header len, body len, serial
278  *   BIG_ENDIAN switch to big endian
279  *   LITTLE_ENDIAN switch to little endian
280  *   OPPOSITE_ENDIAN switch to opposite endian
281  *   ALIGN <N> aligns to the given value
282  *   UNALIGN skips alignment for the next marshal
283  *   BYTE <N> inserts the given integer in [0,255] or char in 'a' format
284  *   START_LENGTH <name> marks the start of a length to measure
285  *   END_LENGTH <name> records the length since START_LENGTH under the given name
286  *                     (or if no START_LENGTH, absolute length)
287  *   LENGTH <name> inserts the saved length of the same name
288  *   CHOP <N> chops last N bytes off the data
289  *   FIELD_NAME <abcd> inserts 4-byte field name
290  *   TYPE <typename> inserts a typecode byte 
291  * @endcode
292  * 
293  * Following commands insert aligned data unless
294  * preceded by "UNALIGN":
295  * @code
296  *   INT32 <N> marshals an INT32
297  *   UINT32 <N> marshals a UINT32
298  *   INT64 <N> marshals an INT64
299  *   UINT64 <N> marshals a UINT64
300  *   DOUBLE <N> marshals a double
301  *   STRING 'Foo' marshals a string
302  *   BYTE_ARRAY { 'a', 3, 4, 5, 6} marshals a BYTE array
303  *   BOOLEAN_ARRAY { false, true, false} marshals a BOOLEAN array
304  *   INT32_ARRAY { 3, 4, 5, 6} marshals an INT32 array
305  *   UINT32_ARRAY { 3, 4, 5, 6} marshals an UINT32 array
306  *   DOUBLE_ARRAY { 1.0, 2.0, 3.0, 4.0} marshals a DOUBLE array  
307  *   STRING_ARRAY { "foo", "bar", "gazonk"} marshals a STRING array  
308  * @endcode
309  *
310  * @todo add support for array types INT32_ARRAY { 3, 4, 5, 6 }
311  * and so forth.
312  * 
313  * @param dest the string to append the message data to
314  * @param filename the filename to load
315  * @returns #TRUE on success
316  */
317 dbus_bool_t
318 _dbus_message_data_load (DBusString       *dest,
319                          const DBusString *filename)
320 {
321   DBusString file;
322   DBusError error;
323   DBusString line;
324   dbus_bool_t retval;
325   int line_no;
326   dbus_bool_t unalign;
327   DBusHashTable *length_hash;
328   int endian;
329   DBusHashIter iter;
330   
331   retval = FALSE;
332   length_hash = NULL;
333   
334   if (!_dbus_string_init (&file))
335     return FALSE;
336
337   if (!_dbus_string_init (&line))
338     {
339       _dbus_string_free (&file);
340       return FALSE;
341     }
342
343   _dbus_verbose ("Loading %s\n", _dbus_string_get_const_data (filename));
344
345   dbus_error_init (&error);
346   if (!_dbus_file_get_contents (&file, filename, &error))
347     {
348       _dbus_warn ("Getting contents of %s failed: %s\n",
349                   _dbus_string_get_const_data (filename), error.message);
350       dbus_error_free (&error);
351       goto out;
352     }
353
354   length_hash = _dbus_hash_table_new (DBUS_HASH_STRING,
355                                       NULL,
356                                       free_saved_length);
357   if (length_hash == NULL)
358     goto out;
359   
360   endian = DBUS_COMPILER_BYTE_ORDER;
361   unalign = FALSE;
362   line_no = 0;
363  next_iteration:
364   while (_dbus_string_pop_line (&file, &line))
365     {
366       dbus_bool_t just_set_unalign;
367
368       just_set_unalign = FALSE;
369       line_no += 1;
370
371       _dbus_string_delete_leading_blanks (&line);
372
373       if (_dbus_string_get_length (&line) == 0)
374         {
375           /* empty line */
376           goto next_iteration;
377         }
378       else if (_dbus_string_starts_with_c_str (&line,
379                                                "#"))
380         {
381           /* Ignore this comment */
382           goto next_iteration;
383         }
384       else if (_dbus_string_starts_with_c_str (&line,
385                                                "VALID_HEADER"))
386         {
387           int i;
388           DBusString name;
389           
390           if (!_dbus_string_append_byte (dest, endian))
391             {
392               _dbus_warn ("could not append endianness\n");
393               goto parse_failed;
394             }
395
396           if (!_dbus_string_append_byte (dest, DBUS_MESSAGE_TYPE_METHOD_CALL))
397             {
398               _dbus_warn ("could not append message type\n");
399               goto parse_failed;
400             }
401           
402           i = 0;
403           while (i < 2)
404             {
405               if (!_dbus_string_append_byte (dest, '\0'))
406                 {
407                   _dbus_warn ("could not append nul pad\n");
408                   goto parse_failed;
409                 }
410               ++i;
411             }
412
413           _dbus_string_init_const (&name, "Header");
414           if (!append_saved_length (dest, length_hash,
415                                     &name, _dbus_string_get_length (dest),
416                                     endian))
417             goto parse_failed;
418
419           _dbus_string_init_const (&name, "Body");
420           if (!append_saved_length (dest, length_hash,
421                                     &name, _dbus_string_get_length (dest),
422                                     endian))
423             goto parse_failed;
424           
425           /* client serial */
426           if (!_dbus_marshal_int32 (dest, endian, 1))
427             {
428               _dbus_warn ("couldn't append client serial\n");
429               goto parse_failed;
430             }
431         }
432       else if (_dbus_string_starts_with_c_str (&line,
433                                                "BIG_ENDIAN"))
434         {
435           endian = DBUS_BIG_ENDIAN;
436         }
437       else if (_dbus_string_starts_with_c_str (&line,
438                                                "LITTLE_ENDIAN"))
439         {
440           endian = DBUS_LITTLE_ENDIAN;
441         }
442       else if (_dbus_string_starts_with_c_str (&line,
443                                                "OPPOSITE_ENDIAN"))
444         {
445           if (endian == DBUS_BIG_ENDIAN)
446             endian = DBUS_LITTLE_ENDIAN;
447           else
448             endian = DBUS_BIG_ENDIAN;
449         }
450       else if (_dbus_string_starts_with_c_str (&line,
451                                                "ALIGN"))
452         {
453           long val;
454           int end;
455           int orig_len;
456           
457           _dbus_string_delete_first_word (&line);
458
459           if (!_dbus_string_parse_int (&line, 0, &val, &end))
460             {
461               _dbus_warn ("Failed to parse integer\n");
462               goto parse_failed;
463             }
464
465           if (val > 8)
466             {
467               _dbus_warn ("Aligning to %ld boundary is crack\n",
468                           val);
469               goto parse_failed;
470             }
471
472           orig_len = _dbus_string_get_length (dest);
473           
474           if (!_dbus_string_align_length (dest, val))
475             goto parse_failed;
476
477           if (_dbus_string_parse_int (&line, end, &val, NULL))
478             {
479               /* If there's an optional second int argument,
480                * fill in align padding with that value
481                */
482               if (val < 0 || val > 255)
483                 {
484                   _dbus_warn ("can't fill align padding with %ld, must be a byte value\n", val);
485                   goto parse_failed;
486                 }
487
488               end = orig_len;
489               while (end < _dbus_string_get_length (dest))
490                 {
491                   _dbus_string_set_byte (dest, end, val);
492                   ++end;
493                 }
494             }
495         }
496       else if (_dbus_string_starts_with_c_str (&line, "UNALIGN"))
497         {
498           unalign = TRUE;
499           just_set_unalign = TRUE;
500         }
501       else if (_dbus_string_starts_with_c_str (&line, "CHOP"))
502         {
503           long val;
504
505           /* FIXME if you CHOP the offset for a LENGTH
506            * command, we segfault.
507            */
508           
509           _dbus_string_delete_first_word (&line);
510
511           if (!_dbus_string_parse_int (&line, 0, &val, NULL))
512             {
513               _dbus_warn ("Failed to parse integer to chop\n");
514               goto parse_failed;
515             }
516
517           if (val > _dbus_string_get_length (dest))
518             {
519               _dbus_warn ("Trying to chop %ld bytes but we only have %d\n",
520                           val,
521                           _dbus_string_get_length (dest));
522               goto parse_failed;
523             }
524           
525           _dbus_string_shorten (dest, val);
526         }
527       else if (_dbus_string_starts_with_c_str (&line,
528                                                "START_LENGTH"))
529         {
530           _dbus_string_delete_first_word (&line);
531
532           if (!save_start (length_hash, &line,
533                            _dbus_string_get_length (dest)))
534             {
535               _dbus_warn ("failed to save length start\n");
536               goto parse_failed;
537             }
538         }
539       else if (_dbus_string_starts_with_c_str (&line,
540                                                "END_LENGTH"))
541         {
542           _dbus_string_delete_first_word (&line);
543
544           if (!save_length (length_hash, &line,
545                             _dbus_string_get_length (dest)))
546             {
547               _dbus_warn ("failed to save length end\n");
548               goto parse_failed;
549             }
550         }
551       else if (_dbus_string_starts_with_c_str (&line,
552                                                "LENGTH"))
553         {
554           SAVE_FOR_UNALIGN (dest, 4);
555           
556           _dbus_string_delete_first_word (&line);
557
558           if (!append_saved_length (dest, length_hash,
559                                     &line,
560                                     unalign ? align_pad_start : align_pad_end,
561                                     endian))
562             {
563               _dbus_warn ("failed to add LENGTH\n");
564               goto parse_failed;
565             }
566
567           PERFORM_UNALIGN (dest);
568         }
569       else if (_dbus_string_starts_with_c_str (&line,
570                                                "FIELD_NAME"))
571         {
572           _dbus_string_delete_first_word (&line);
573
574           if (_dbus_string_get_length (&line) != 4)
575             {
576               _dbus_warn ("Field name must be four characters not \"%s\"\n",
577                           _dbus_string_get_const_data (&line));
578               goto parse_failed;
579             }
580
581           if (unalign)
582             unalign = FALSE;
583           else
584             _dbus_string_align_length (dest, 4);
585           
586           if (!_dbus_string_copy (&line, 0, dest,
587                                   _dbus_string_get_length (dest)))
588             goto parse_failed;
589         }
590       else if (_dbus_string_starts_with_c_str (&line,
591                                                "TYPE"))
592         {
593           int code;
594           
595           _dbus_string_delete_first_word (&line);          
596
597           if (_dbus_string_starts_with_c_str (&line, "INVALID"))
598             code = DBUS_TYPE_INVALID;
599           else if (_dbus_string_starts_with_c_str (&line, "NIL"))
600             code = DBUS_TYPE_NIL;
601           else if (_dbus_string_starts_with_c_str (&line, "BYTE"))
602             code = DBUS_TYPE_BYTE;
603           else if (_dbus_string_starts_with_c_str (&line, "BOOLEAN"))
604             code = DBUS_TYPE_BOOLEAN;
605           else if (_dbus_string_starts_with_c_str (&line, "INT32"))
606             code = DBUS_TYPE_INT32;
607           else if (_dbus_string_starts_with_c_str (&line, "UINT32"))
608             code = DBUS_TYPE_UINT32;
609           else if (_dbus_string_starts_with_c_str (&line, "DOUBLE"))
610             code = DBUS_TYPE_DOUBLE;
611           else if (_dbus_string_starts_with_c_str (&line, "STRING"))
612             code = DBUS_TYPE_STRING;
613           else if (_dbus_string_starts_with_c_str (&line, "NAMED"))
614             code = DBUS_TYPE_NAMED;
615           else if (_dbus_string_starts_with_c_str (&line, "ARRAY"))
616             code = DBUS_TYPE_ARRAY;
617           else if (_dbus_string_starts_with_c_str (&line, "DICT"))
618             code = DBUS_TYPE_DICT;
619           else
620             {
621               _dbus_warn ("%s is not a valid type name\n", _dbus_string_get_const_data (&line));
622               goto parse_failed;
623             }
624
625           if (!_dbus_string_append_byte (dest, code))
626             {
627               _dbus_warn ("could not append typecode byte\n");
628               goto parse_failed;
629             }
630         }
631       else if (_dbus_string_starts_with_c_str (&line,
632                                                "BYTE_ARRAY"))
633         {
634           SAVE_FOR_UNALIGN (dest, 4);
635           int i, len, allocated;
636           unsigned char *values;
637           unsigned char b;
638           long val;
639
640           allocated = 4;
641           values = dbus_new (unsigned char, allocated);
642           if (!values)
643             {
644               _dbus_warn ("could not allocate memory for BYTE_ARRAY\n");
645               goto parse_failed;
646             }
647
648           len = 0;
649           
650           _dbus_string_delete_first_word (&line);
651           _dbus_string_skip_blank (&line, 0, &i);
652           b = _dbus_string_get_byte (&line, i++);
653           
654           if (b != '{')
655             goto parse_failed;
656
657           while (i < _dbus_string_get_length (&line))
658             {
659               _dbus_string_skip_blank (&line, i, &i);         
660
661               if (_dbus_string_get_byte (&line, i) == '\'' &&
662                   _dbus_string_get_length (&line) >= i + 4 &&
663                   _dbus_string_get_byte (&line, i + 1) == '\\' &&
664                   _dbus_string_get_byte (&line, i + 2) == '\'' &&
665                   _dbus_string_get_byte (&line, i + 3) == '\'')
666                 {
667                   val = '\'';
668                   i += 4;
669                 }
670               else if (_dbus_string_get_byte (&line, i) == '\'' &&
671                        _dbus_string_get_length (&line) >= i + 3 &&
672                        _dbus_string_get_byte (&line, i + 2) == '\'')
673                 {
674                   val = _dbus_string_get_byte (&line, i + 1);
675                   i += 3;
676                 }
677               else
678                 {
679                   if (!_dbus_string_parse_int (&line, i, &val, &i))
680                     {
681                       _dbus_warn ("Failed to parse integer for BYTE_ARRAY\n");
682                       goto parse_failed;
683                     }
684
685                   if (val < 0 || val > 255)
686                     {
687                       _dbus_warn ("A byte must be in range 0-255 not %ld\n",
688                                   val);
689                       goto parse_failed;
690                     }
691                 }
692
693               values[len++] = val;
694               if (len == allocated)
695                 {
696                   allocated *= 2;
697                   values = dbus_realloc (values, allocated * sizeof (unsigned char));
698                   if (!values)
699                     {
700                       _dbus_warn ("could not allocate memory for BYTE_ARRAY\n");
701                       goto parse_failed;
702                     }
703                 }
704               
705               _dbus_string_skip_blank (&line, i, &i);
706               
707               b = _dbus_string_get_byte (&line, i++);
708
709               if (b == '}')
710                 break;
711               else if (b != ',')
712                 goto parse_failed;
713             }
714
715           if (!_dbus_marshal_int32 (dest, endian, len) ||
716               !_dbus_string_append_len (dest, values, len))
717             {
718               _dbus_warn ("failed to append BYTE_ARRAY\n");
719               goto parse_failed;
720             }
721           dbus_free (values);
722           
723           PERFORM_UNALIGN (dest);
724         }
725       else if (_dbus_string_starts_with_c_str (&line,
726                                                "BOOLEAN_ARRAY"))
727         {
728           SAVE_FOR_UNALIGN (dest, 4);
729           int i, len, allocated;
730           unsigned char *values;
731           unsigned char b, val;
732
733           allocated = 4;
734           values = dbus_new (unsigned char, allocated);
735           if (!values)
736             {
737               _dbus_warn ("could not allocate memory for BOOLEAN_ARRAY\n");
738               goto parse_failed;
739             }
740
741           len = 0;
742           
743           _dbus_string_delete_first_word (&line);
744           _dbus_string_skip_blank (&line, 0, &i);
745           b = _dbus_string_get_byte (&line, i++);
746           
747           if (b != '{')
748             goto parse_failed;
749
750           while (i < _dbus_string_get_length (&line))
751             {
752               _dbus_string_skip_blank (&line, i, &i);         
753               
754               if (_dbus_string_find_to (&line, i, i + 5,
755                                         "false", NULL))
756                 {
757                   i += 5;
758                   val = TRUE;
759                 }
760               else if (_dbus_string_find_to (&line, i, i + 4,
761                                              "true", NULL))
762                 {
763                   i += 4;
764                   val = FALSE;
765                 }
766               else
767                 {
768                   _dbus_warn ("could not parse BOOLEAN_ARRAY\n");
769                   goto parse_failed;
770                 }
771
772               values[len++] = val;
773               if (len == allocated)
774                 {
775                   allocated *= 2;
776                   values = dbus_realloc (values, allocated * sizeof (unsigned char));
777                   if (!values)
778                     {
779                       _dbus_warn ("could not allocate memory for BOOLEAN_ARRAY\n");
780                       goto parse_failed;
781                     }
782                 }
783               
784               _dbus_string_skip_blank (&line, i, &i);
785               
786               b = _dbus_string_get_byte (&line, i++);
787
788               if (b == '}')
789                 break;
790               else if (b != ',')
791                 goto parse_failed;
792             }
793
794           if (!_dbus_marshal_int32 (dest, endian, len) ||
795               !_dbus_string_append_len (dest, values, len))
796             {
797               _dbus_warn ("failed to append BOOLEAN_ARRAY\n");
798               goto parse_failed;
799             }
800           dbus_free (values);
801           
802           PERFORM_UNALIGN (dest);
803         }
804       else if (_dbus_string_starts_with_c_str (&line,
805                                                "INT32_ARRAY"))
806         {
807           SAVE_FOR_UNALIGN (dest, 4);
808           int i, len, allocated;
809           dbus_int32_t *values;
810           long val;
811           unsigned char b;
812
813           allocated = 4;
814           values = dbus_new (dbus_int32_t, allocated);
815           if (!values)
816             {
817               _dbus_warn ("could not allocate memory for INT32_ARRAY\n");
818               goto parse_failed;
819             }
820           
821           len = 0;
822           
823           _dbus_string_delete_first_word (&line);
824           _dbus_string_skip_blank (&line, 0, &i);
825           b = _dbus_string_get_byte (&line, i++);
826
827           if (b != '{')
828             goto parse_failed;
829
830           while (i < _dbus_string_get_length (&line))
831             {
832               _dbus_string_skip_blank (&line, i, &i);
833
834               if (!_dbus_string_parse_int (&line, i, &val, &i))
835                 {
836                   _dbus_warn ("could not parse integer for INT32_ARRAY\n");
837                   goto parse_failed;
838                 }
839
840               values[len++] = val;
841               if (len == allocated)
842                 {
843                   allocated *= 2;
844                   values = dbus_realloc (values, allocated * sizeof (dbus_int32_t));
845                   if (!values)
846                     {
847                       _dbus_warn ("could not allocate memory for INT32_ARRAY\n");
848                       goto parse_failed;
849                     }
850                 }
851               
852               _dbus_string_skip_blank (&line, i, &i);
853               
854               b = _dbus_string_get_byte (&line, i++);
855
856               if (b == '}')
857                 break;
858               else if (b != ',')
859                 goto parse_failed;
860             }
861
862           if (!_dbus_marshal_int32_array (dest, endian, values, len))
863             {
864               _dbus_warn ("failed to append INT32_ARRAY\n");
865               goto parse_failed;
866             }
867           dbus_free (values);
868           
869           PERFORM_UNALIGN (dest);
870         }
871       else if (_dbus_string_starts_with_c_str (&line,
872                                                "UINT32_ARRAY"))
873         {
874           SAVE_FOR_UNALIGN (dest, 4);
875           int i, len, allocated;
876           dbus_uint32_t *values;
877           long val;
878           unsigned char b;
879
880           allocated = 4;
881           values = dbus_new (dbus_uint32_t, allocated);
882           if (!values)
883             {
884               _dbus_warn ("could not allocate memory for UINT32_ARRAY\n");
885               goto parse_failed;
886             }
887           
888           len = 0;
889           
890           _dbus_string_delete_first_word (&line);
891           _dbus_string_skip_blank (&line, 0, &i);
892           b = _dbus_string_get_byte (&line, i++);
893
894           if (b != '{')
895             goto parse_failed;
896
897           while (i < _dbus_string_get_length (&line))
898             {
899               _dbus_string_skip_blank (&line, i, &i);
900
901               if (!_dbus_string_parse_int (&line, i, &val, &i))
902                 {
903                   _dbus_warn ("could not parse integer for UINT32_ARRAY\n");
904                   goto parse_failed;
905                 }
906
907               values[len++] = val;
908               if (len == allocated)
909                 {
910                   allocated *= 2;
911                   values = dbus_realloc (values, allocated * sizeof (dbus_uint32_t));
912                   if (!values)
913                     {
914                       _dbus_warn ("could not allocate memory for UINT32_ARRAY\n");
915                       goto parse_failed;
916                     }
917                 }
918               
919               _dbus_string_skip_blank (&line, i, &i);
920               
921               b = _dbus_string_get_byte (&line, i++);
922
923               if (b == '}')
924                 break;
925               else if (b != ',')
926                 goto parse_failed;
927             }
928
929           if (!_dbus_marshal_uint32_array (dest, endian, values, len))
930             {
931               _dbus_warn ("failed to append UINT32_ARRAY\n");
932               goto parse_failed;
933             }
934           dbus_free (values);
935           
936           PERFORM_UNALIGN (dest);
937         }
938       else if (_dbus_string_starts_with_c_str (&line,
939                                                "DOUBLE_ARRAY"))
940         {
941           SAVE_FOR_UNALIGN (dest, 8);
942           int i, len, allocated;
943           double *values;
944           double val;
945           unsigned char b;
946
947           allocated = 4;
948           values = dbus_new (double, allocated);
949           if (!values)
950             {
951               _dbus_warn ("could not allocate memory for DOUBLE_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           while (i < _dbus_string_get_length (&line))
965             {
966               _dbus_string_skip_blank (&line, i, &i);
967
968               if (!_dbus_string_parse_double (&line, i, &val, &i))
969                 {
970                   _dbus_warn ("could not parse double for DOUBLE_ARRAY\n");
971                   goto parse_failed;
972                 }
973
974               values[len++] = val;
975               if (len == allocated)
976                 {
977                   allocated *= 2;
978                   values = dbus_realloc (values, allocated * sizeof (double));
979                   if (!values)
980                     {
981                       _dbus_warn ("could not allocate memory for DOUBLE_ARRAY\n");
982                       goto parse_failed;
983                     }
984                 }
985               
986               _dbus_string_skip_blank (&line, i, &i);
987               
988               b = _dbus_string_get_byte (&line, i++);
989
990               if (b == '}')
991                 break;
992               else if (b != ',')
993                 goto parse_failed;
994             }
995
996           if (!_dbus_marshal_double_array (dest, endian, values, len))
997             {
998               _dbus_warn ("failed to append DOUBLE_ARRAY\n");
999               goto parse_failed;
1000             }
1001           dbus_free (values);
1002           
1003           PERFORM_UNALIGN (dest);
1004         }
1005       else if (_dbus_string_starts_with_c_str (&line,
1006                                                "STRING_ARRAY"))
1007         {
1008           SAVE_FOR_UNALIGN (dest, 4);
1009           int i, len, allocated;
1010           char **values;
1011           char *val;
1012           DBusString val_str;
1013           unsigned char b;
1014
1015           allocated = 4;
1016           values = dbus_new (char *, allocated);
1017           if (!values)
1018             {
1019               _dbus_warn ("could not allocate memory for STRING_ARRAY\n");
1020               goto parse_failed;
1021             }
1022           
1023           len = 0;
1024           
1025           _dbus_string_delete_first_word (&line);
1026           _dbus_string_skip_blank (&line, 0, &i);
1027           b = _dbus_string_get_byte (&line, i++);
1028
1029           if (b != '{')
1030             goto parse_failed;
1031
1032           _dbus_string_init (&val_str);
1033           while (i < _dbus_string_get_length (&line))
1034             {
1035               _dbus_string_skip_blank (&line, i, &i);
1036
1037               if (!append_quoted_string (&val_str, &line, i, &i))
1038                 {
1039                   _dbus_warn ("could not parse quoted string for STRING_ARRAY\n");
1040                   goto parse_failed;
1041                 }
1042               i++;
1043
1044               if (!_dbus_string_steal_data (&val_str, &val))
1045                 {
1046                   _dbus_warn ("could not allocate memory for STRING_ARRAY string\n");
1047                   goto parse_failed;
1048                 }
1049               
1050               values[len++] = val;
1051               if (len == allocated)
1052                 {
1053                   allocated *= 2;
1054                   values = dbus_realloc (values, allocated * sizeof (char *));
1055                   if (!values)
1056                     {
1057                       _dbus_warn ("could not allocate memory for STRING_ARRAY\n");
1058                       goto parse_failed;
1059                     }
1060                 }
1061               
1062               _dbus_string_skip_blank (&line, i, &i);
1063               
1064               b = _dbus_string_get_byte (&line, i++);
1065
1066               if (b == '}')
1067                 break;
1068               else if (b != ',')
1069                 {
1070                   _dbus_warn ("missing comma when parsing STRING_ARRAY\n");
1071                   goto parse_failed;
1072                 }
1073             }
1074           _dbus_string_free (&val_str);
1075           
1076           if (!_dbus_marshal_string_array (dest, endian, (const char **)values, len))
1077             {
1078               _dbus_warn ("failed to append STRING_ARRAY\n");
1079               goto parse_failed;
1080             }
1081
1082           values[len] = NULL;
1083           dbus_free_string_array (values);
1084           
1085           PERFORM_UNALIGN (dest);
1086         }
1087       else if (_dbus_string_starts_with_c_str (&line, "BYTE"))
1088         {
1089           unsigned char the_byte;
1090           
1091           _dbus_string_delete_first_word (&line);
1092
1093           if (_dbus_string_equal_c_str (&line, "'\\''"))
1094             the_byte = '\'';
1095           else if (_dbus_string_get_byte (&line, 0) == '\'' &&
1096                    _dbus_string_get_length (&line) >= 3 &&
1097                    _dbus_string_get_byte (&line, 2) == '\'')
1098             the_byte = _dbus_string_get_byte (&line, 1);
1099           else
1100             {
1101               long val;
1102               if (!_dbus_string_parse_int (&line, 0, &val, NULL))
1103                 {
1104                   _dbus_warn ("Failed to parse integer for BYTE\n");
1105                   goto parse_failed;
1106                 }
1107
1108               if (val > 255)
1109                 {
1110                   _dbus_warn ("A byte must be in range 0-255 not %ld\n",
1111                                  val);
1112                   goto parse_failed;
1113                 }
1114               the_byte = (unsigned char) val;
1115             }
1116
1117           _dbus_string_append_byte (dest, the_byte);
1118         }
1119       else if (_dbus_string_starts_with_c_str (&line,
1120                                                "BOOLEAN"))
1121         {
1122           unsigned char val;
1123
1124           _dbus_string_delete_first_word (&line);
1125
1126           if (_dbus_string_starts_with_c_str (&line, "true"))
1127             val = TRUE;
1128           else if (_dbus_string_starts_with_c_str (&line, "false"))
1129             val = FALSE;
1130           else
1131             {
1132               _dbus_warn ("could not parse BOOLEAN\n");
1133               goto parse_failed;
1134             }
1135           if (!_dbus_string_append_byte (dest, val))
1136             {
1137               _dbus_warn ("failed to append BOOLEAN\n");
1138               goto parse_failed;
1139             }
1140         }
1141       
1142       else if (_dbus_string_starts_with_c_str (&line,
1143                                                "INT32"))
1144         {
1145           SAVE_FOR_UNALIGN (dest, 4);
1146           long val;
1147           
1148           _dbus_string_delete_first_word (&line);
1149
1150           if (!_dbus_string_parse_int (&line, 0, &val, NULL))
1151             {
1152               _dbus_warn ("could not parse integer for INT32\n");
1153               goto parse_failed;
1154             }
1155           
1156           if (!_dbus_marshal_int32 (dest, endian,
1157                                     val))
1158             {
1159               _dbus_warn ("failed to append INT32\n");
1160               goto parse_failed;
1161             }
1162
1163           PERFORM_UNALIGN (dest);
1164         }
1165       else if (_dbus_string_starts_with_c_str (&line,
1166                                                "UINT32"))
1167         {
1168           SAVE_FOR_UNALIGN (dest, 4);
1169           unsigned long val;
1170           
1171           _dbus_string_delete_first_word (&line);
1172
1173           if (!_dbus_string_parse_uint (&line, 0, &val, NULL))
1174             goto parse_failed;
1175           
1176           if (!_dbus_marshal_uint32 (dest, endian,
1177                                      val))
1178             {
1179               _dbus_warn ("failed to append UINT32\n");
1180               goto parse_failed;
1181             }
1182
1183           PERFORM_UNALIGN (dest);
1184         }
1185       else if (_dbus_string_starts_with_c_str (&line,
1186                                                "DOUBLE"))
1187         {
1188           SAVE_FOR_UNALIGN (dest, 8);
1189           double val;
1190           
1191           _dbus_string_delete_first_word (&line);
1192
1193           if (!_dbus_string_parse_double (&line, 0, &val, NULL))
1194             goto parse_failed;
1195           
1196           if (!_dbus_marshal_double (dest, endian,
1197                                      val))
1198             {
1199               _dbus_warn ("failed to append DOUBLE\n");
1200               goto parse_failed;
1201             }
1202
1203           PERFORM_UNALIGN (dest);
1204         }
1205       else if (_dbus_string_starts_with_c_str (&line,
1206                                                "STRING"))
1207         {
1208           SAVE_FOR_UNALIGN (dest, 4);
1209           int size_offset;
1210           int old_len;
1211           
1212           _dbus_string_delete_first_word (&line);
1213
1214           size_offset = _dbus_string_get_length (dest);
1215           size_offset = _DBUS_ALIGN_VALUE (size_offset, 4);
1216           if (!_dbus_marshal_uint32 (dest, endian, 0))
1217             {
1218               _dbus_warn ("Failed to append string size\n");
1219               goto parse_failed;
1220             }
1221
1222           old_len = _dbus_string_get_length (dest);
1223           if (!append_quoted_string (dest, &line, 0, NULL))
1224             {
1225               _dbus_warn ("Failed to append quoted string\n");
1226               goto parse_failed;
1227             }
1228
1229           _dbus_marshal_set_uint32 (dest, endian, size_offset,
1230                                     /* subtract 1 for nul */
1231                                     _dbus_string_get_length (dest) - old_len - 1);
1232           
1233           PERFORM_UNALIGN (dest);
1234         }
1235       else
1236         goto parse_failed;
1237       
1238       if (!just_set_unalign && unalign)
1239         {
1240           _dbus_warn ("UNALIGN prior to something that isn't aligned\n");
1241           goto parse_failed;
1242         }
1243
1244       goto next_iteration; /* skip parse_failed */
1245       
1246     parse_failed:
1247       {
1248         _dbus_warn ("couldn't process line %d \"%s\"\n",
1249                     line_no, _dbus_string_get_const_data (&line));
1250         goto out;
1251       }
1252     }
1253
1254   _dbus_hash_iter_init (length_hash, &iter);
1255   while (_dbus_hash_iter_next (&iter))
1256     {
1257       SavedLength *sl = _dbus_hash_iter_get_value (&iter);
1258       const char *s;
1259
1260       s = _dbus_string_get_const_data (&sl->name);
1261       
1262       if (sl->length < 0)
1263         {
1264           _dbus_warn ("Used LENGTH %s but never did END_LENGTH\n",
1265                       s);
1266           goto out;
1267         }
1268       else if (sl->offset < 0)
1269         {
1270           _dbus_warn ("Did END_LENGTH %s but never used LENGTH\n",
1271                       s);
1272           goto out;
1273         }
1274       else
1275         {
1276           if (sl->start < 0)
1277             sl->start = 0;
1278           
1279           _dbus_verbose ("Filling in length %s endian = %d offset = %d start = %d length = %d\n",
1280                          s, sl->endian, sl->offset, sl->start, sl->length);
1281           _dbus_marshal_set_int32 (dest,
1282                                    sl->endian,
1283                                    sl->offset,
1284                                    sl->length - sl->start);
1285         }
1286
1287       _dbus_hash_iter_remove_entry (&iter);
1288     }
1289   
1290   retval = TRUE;
1291
1292   _dbus_verbose_bytes_of_string (dest, 0, _dbus_string_get_length (dest));
1293   
1294  out:
1295   if (length_hash != NULL)
1296     _dbus_hash_table_unref (length_hash);
1297   
1298   _dbus_string_free (&file);
1299   _dbus_string_free (&line);
1300   return retval;
1301 }
1302
1303 /** @} */
1304 #endif /* DBUS_BUILD_TESTS */