2003-03-12 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 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   _dbus_string_get_const_data (name, &s);
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, _DBUS_INT_MAX))
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   _dbus_string_get_const_data (&sl->name, &s);
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  *   DOUBLE <N> marshals a double
299  *   STRING 'Foo' marshals a string
300  *   INT32_ARRAY { 3, 4, 5, 6} marshals an INT32 array
301  *   UINT32_ARRAY { 3, 4, 5, 6} marshals an UINT32 array
302  *   DOUBLE_ARRAY { 1.0, 2.0, 3.0, 4.0} marshals a DOUBLE array  
303  * @endcode
304  *
305  * @todo add support for array types INT32_ARRAY { 3, 4, 5, 6 }
306  * and so forth.
307  * 
308  * @param dest the string to append the message data to
309  * @param filename the filename to load
310  * @returns #TRUE on success
311  */
312 dbus_bool_t
313 _dbus_message_data_load (DBusString       *dest,
314                          const DBusString *filename)
315 {
316   DBusString file;
317   DBusError error;
318   DBusString line;
319   dbus_bool_t retval;
320   int line_no;
321   dbus_bool_t unalign;
322   DBusHashTable *length_hash;
323   int endian;
324   DBusHashIter iter;
325   
326   retval = FALSE;
327   length_hash = NULL;
328   
329   if (!_dbus_string_init (&file, _DBUS_INT_MAX))
330     return FALSE;
331
332   if (!_dbus_string_init (&line, _DBUS_INT_MAX))
333     {
334       _dbus_string_free (&file);
335       return FALSE;
336     }
337
338   {
339     const char *s;
340     _dbus_string_get_const_data (filename, &s);
341     _dbus_verbose ("Loading %s\n", s);
342   }
343
344   dbus_error_init (&error);
345   if (!_dbus_file_get_contents (&file, filename, &error))
346     {
347       const char *s;
348       _dbus_string_get_const_data (filename, &s);
349       _dbus_warn ("Getting contents of %s failed: %s\n",
350                   s, error.message);
351       dbus_error_free (&error);
352       goto out;
353     }
354
355   length_hash = _dbus_hash_table_new (DBUS_HASH_STRING,
356                                       NULL,
357                                       free_saved_length);
358   if (length_hash == NULL)
359     goto out;
360   
361   endian = DBUS_COMPILER_BYTE_ORDER;
362   unalign = FALSE;
363   line_no = 0;
364  next_iteration:
365   while (_dbus_string_pop_line (&file, &line))
366     {
367       dbus_bool_t just_set_unalign;
368
369       just_set_unalign = FALSE;
370       line_no += 1;
371
372       _dbus_string_delete_leading_blanks (&line);
373
374       if (_dbus_string_get_length (&line) == 0)
375         {
376           /* empty line */
377           goto next_iteration;
378         }
379       else if (_dbus_string_starts_with_c_str (&line,
380                                                "#"))
381         {
382           /* Ignore this comment */
383           goto next_iteration;
384         }
385       else if (_dbus_string_starts_with_c_str (&line,
386                                                "VALID_HEADER"))
387         {
388           int i;
389           DBusString name;
390           
391           if (!_dbus_string_append_byte (dest, endian))
392             {
393               _dbus_warn ("could not append endianness\n");
394               goto parse_failed;
395             }
396
397           i = 0;
398           while (i < 3)
399             {
400               if (!_dbus_string_append_byte (dest, '\0'))
401                 {
402                   _dbus_warn ("could not append nul pad\n");
403                   goto parse_failed;
404                 }
405               ++i;
406             }
407
408           _dbus_string_init_const (&name, "Header");
409           if (!append_saved_length (dest, length_hash,
410                                     &name, _dbus_string_get_length (dest),
411                                     endian))
412             goto parse_failed;
413
414           _dbus_string_init_const (&name, "Body");
415           if (!append_saved_length (dest, length_hash,
416                                     &name, _dbus_string_get_length (dest),
417                                     endian))
418             goto parse_failed;
419           
420           /* client serial */
421           if (!_dbus_marshal_int32 (dest, endian, 1))
422             {
423               _dbus_warn ("couldn't append client serial\n");
424               goto parse_failed;
425             }
426         }
427       else if (_dbus_string_starts_with_c_str (&line,
428                                                "BIG_ENDIAN"))
429         {
430           endian = DBUS_BIG_ENDIAN;
431         }
432       else if (_dbus_string_starts_with_c_str (&line,
433                                                "LITTLE_ENDIAN"))
434         {
435           endian = DBUS_LITTLE_ENDIAN;
436         }
437       else if (_dbus_string_starts_with_c_str (&line,
438                                                "OPPOSITE_ENDIAN"))
439         {
440           if (endian == DBUS_BIG_ENDIAN)
441             endian = DBUS_LITTLE_ENDIAN;
442           else
443             endian = DBUS_BIG_ENDIAN;
444         }
445       else if (_dbus_string_starts_with_c_str (&line,
446                                                "ALIGN"))
447         {
448           long val;
449           int end;
450           int orig_len;
451           
452           _dbus_string_delete_first_word (&line);
453
454           if (!_dbus_string_parse_int (&line, 0, &val, &end))
455             {
456               _dbus_warn ("Failed to parse integer\n");
457               goto parse_failed;
458             }
459
460           if (val > 8)
461             {
462               _dbus_warn ("Aligning to %ld boundary is crack\n",
463                           val);
464               goto parse_failed;
465             }
466
467           orig_len = _dbus_string_get_length (dest);
468           
469           if (!_dbus_string_align_length (dest, val))
470             goto parse_failed;
471
472           if (_dbus_string_parse_int (&line, end, &val, NULL))
473             {
474               /* If there's an optional second int argument,
475                * fill in align padding with that value
476                */
477               if (val < 0 || val > 255)
478                 {
479                   _dbus_warn ("can't fill align padding with %ld, must be a byte value\n", val);
480                   goto parse_failed;
481                 }
482
483               end = orig_len;
484               while (end < _dbus_string_get_length (dest))
485                 {
486                   _dbus_string_set_byte (dest, end, val);
487                   ++end;
488                 }
489             }
490         }
491       else if (_dbus_string_starts_with_c_str (&line, "UNALIGN"))
492         {
493           unalign = TRUE;
494           just_set_unalign = TRUE;
495         }
496       else if (_dbus_string_starts_with_c_str (&line, "CHOP"))
497         {
498           long val;
499
500           /* FIXME if you CHOP the offset for a LENGTH
501            * command, we segfault.
502            */
503           
504           _dbus_string_delete_first_word (&line);
505
506           if (!_dbus_string_parse_int (&line, 0, &val, NULL))
507             {
508               _dbus_warn ("Failed to parse integer to chop\n");
509               goto parse_failed;
510             }
511
512           if (val > _dbus_string_get_length (dest))
513             {
514               _dbus_warn ("Trying to chop %ld bytes but we only have %d\n",
515                           val,
516                           _dbus_string_get_length (dest));
517               goto parse_failed;
518             }
519           
520           _dbus_string_shorten (dest, val);
521         }
522       else if (_dbus_string_starts_with_c_str (&line, "BYTE"))
523         {
524           unsigned char the_byte;
525           
526           _dbus_string_delete_first_word (&line);
527
528           if (_dbus_string_equal_c_str (&line, "'\\''"))
529             the_byte = '\'';
530           else if (_dbus_string_get_byte (&line, 0) == '\'' &&
531                    _dbus_string_get_length (&line) >= 3 &&
532                    _dbus_string_get_byte (&line, 2) == '\'')
533             the_byte = _dbus_string_get_byte (&line, 1);
534           else
535             {
536               long val;
537               if (!_dbus_string_parse_int (&line, 0, &val, NULL))
538                 {
539                   _dbus_warn ("Failed to parse integer for BYTE\n");
540                   goto parse_failed;
541                 }
542
543               if (val > 255)
544                 {
545                   _dbus_warn ("A byte must be in range 0-255 not %ld\n",
546                                  val);
547                   goto parse_failed;
548                 }
549               the_byte = (unsigned char) val;
550             }
551
552           _dbus_string_append_byte (dest, the_byte);
553         }
554       else if (_dbus_string_starts_with_c_str (&line,
555                                                "START_LENGTH"))
556         {
557           _dbus_string_delete_first_word (&line);
558
559           if (!save_start (length_hash, &line,
560                            _dbus_string_get_length (dest)))
561             {
562               _dbus_warn ("failed to save length start\n");
563               goto parse_failed;
564             }
565         }
566       else if (_dbus_string_starts_with_c_str (&line,
567                                                "END_LENGTH"))
568         {
569           _dbus_string_delete_first_word (&line);
570
571           if (!save_length (length_hash, &line,
572                             _dbus_string_get_length (dest)))
573             {
574               _dbus_warn ("failed to save length end\n");
575               goto parse_failed;
576             }
577         }
578       else if (_dbus_string_starts_with_c_str (&line,
579                                                "LENGTH"))
580         {
581           SAVE_FOR_UNALIGN (dest, 4);
582           
583           _dbus_string_delete_first_word (&line);
584
585           if (!append_saved_length (dest, length_hash,
586                                     &line,
587                                     unalign ? align_pad_start : align_pad_end,
588                                     endian))
589             {
590               _dbus_warn ("failed to add LENGTH\n");
591               goto parse_failed;
592             }
593
594           PERFORM_UNALIGN (dest);
595         }
596       else if (_dbus_string_starts_with_c_str (&line,
597                                                "FIELD_NAME"))
598         {
599           _dbus_string_delete_first_word (&line);
600
601           if (_dbus_string_get_length (&line) != 4)
602             {
603               const char *s;
604               _dbus_string_get_const_data (&line, &s);
605               _dbus_warn ("Field name must be four characters not \"%s\"\n",
606                              s);
607               goto parse_failed;
608             }
609
610           if (unalign)
611             unalign = FALSE;
612           else
613             _dbus_string_align_length (dest, 4);
614           
615           if (!_dbus_string_copy (&line, 0, dest,
616                                   _dbus_string_get_length (dest)))
617             goto parse_failed;
618         }
619       else if (_dbus_string_starts_with_c_str (&line,
620                                                "TYPE"))
621         {
622           int code;
623           
624           _dbus_string_delete_first_word (&line);          
625
626           if (_dbus_string_starts_with_c_str (&line, "INVALID"))
627             code = DBUS_TYPE_INVALID;
628           else if (_dbus_string_starts_with_c_str (&line, "NIL"))
629             code = DBUS_TYPE_NIL;
630           else if (_dbus_string_starts_with_c_str (&line, "BOOLEAN_ARRAY"))
631             code = DBUS_TYPE_BOOLEAN_ARRAY;
632           else if (_dbus_string_starts_with_c_str (&line, "INT32_ARRAY"))
633             code = DBUS_TYPE_INT32_ARRAY;
634           else if (_dbus_string_starts_with_c_str (&line, "UINT32_ARRAY"))
635             code = DBUS_TYPE_UINT32_ARRAY;
636           else if (_dbus_string_starts_with_c_str (&line, "DOUBLE_ARRAY"))
637             code = DBUS_TYPE_DOUBLE_ARRAY;
638           else if (_dbus_string_starts_with_c_str (&line, "BYTE_ARRAY"))
639             code = DBUS_TYPE_BYTE_ARRAY;
640           else if (_dbus_string_starts_with_c_str (&line, "STRING_ARRAY"))
641             code = DBUS_TYPE_STRING_ARRAY;
642           else if (_dbus_string_starts_with_c_str (&line, "BOOLEAN"))
643             code = DBUS_TYPE_BOOLEAN;
644           else if (_dbus_string_starts_with_c_str (&line, "INT32"))
645             code = DBUS_TYPE_INT32;
646           else if (_dbus_string_starts_with_c_str (&line, "UINT32"))
647             code = DBUS_TYPE_UINT32;
648           else if (_dbus_string_starts_with_c_str (&line, "DOUBLE"))
649             code = DBUS_TYPE_DOUBLE;
650           else if (_dbus_string_starts_with_c_str (&line, "STRING"))
651             code = DBUS_TYPE_STRING;
652           else if (_dbus_string_starts_with_c_str (&line, "DICT"))
653             code = DBUS_TYPE_DICT;
654           else
655             {
656               const char *s;
657               _dbus_string_get_const_data (&line, &s);
658               _dbus_warn ("%s is not a valid type name\n", s);
659               goto parse_failed;
660             }
661
662           if (!_dbus_string_append_byte (dest, code))
663             {
664               _dbus_warn ("could not append typecode byte\n");
665               goto parse_failed;
666             }
667         }
668       else if (_dbus_string_starts_with_c_str (&line,
669                                                "BOOLEAN_ARRAY"))
670         {
671           SAVE_FOR_UNALIGN (dest, 4);
672           int i, len, allocated;
673           unsigned char *values;
674           unsigned char b, val;
675
676           allocated = 4;
677           values = dbus_new (unsigned char, allocated);
678           if (!values)
679             {
680               _dbus_warn ("could not allocate memory for BOOLEAN_ARRAY\n");
681               goto parse_failed;
682             }
683
684           len = 0;
685           
686           _dbus_string_delete_first_word (&line);
687           _dbus_string_skip_blank (&line, 0, &i);
688           b = _dbus_string_get_byte (&line, i++);
689           
690           if (b != '{')
691             goto parse_failed;
692
693           while (i < _dbus_string_get_length (&line))
694             {
695               _dbus_string_skip_blank (&line, i, &i);         
696               
697               if (_dbus_string_find_to (&line, i, i + 5,
698                                         "false", NULL))
699                 {
700                   i += 5;
701                   val = TRUE;
702                 }
703               else if (_dbus_string_find_to (&line, i, i + 4,
704                                              "true", NULL))
705                 {
706                   i += 4;
707                   val = FALSE;
708                 }
709               else
710                 {
711                   _dbus_warn ("could not parse BOOLEAN_ARRAY\n");
712                   goto parse_failed;
713                 }
714
715               values[len++] = val;
716               if (len == allocated)
717                 {
718                   allocated *= 2;
719                   values = dbus_realloc (values, allocated * sizeof (unsigned char));
720                   if (!values)
721                     {
722                       _dbus_warn ("could not allocate memory for BOOLEAN_ARRAY\n");
723                       goto parse_failed;
724                     }
725                 }
726               
727               _dbus_string_skip_blank (&line, i, &i);
728               
729               b = _dbus_string_get_byte (&line, i++);
730
731               if (b == '}')
732                 break;
733               else if (b != ',')
734                 goto parse_failed;
735             }
736
737           if (!_dbus_marshal_int32 (dest, endian, len) ||
738               !_dbus_string_append_len (dest, values, len))
739             {
740               _dbus_warn ("failed to append BOOLEAN_ARRAY\n");
741               goto parse_failed;
742             }
743           dbus_free (values);
744           
745           PERFORM_UNALIGN (dest);
746         }
747       else if (_dbus_string_starts_with_c_str (&line,
748                                                "INT32_ARRAY"))
749         {
750           SAVE_FOR_UNALIGN (dest, 4);
751           int i, len, allocated;
752           dbus_int32_t *values;
753           long val;
754           unsigned char b;
755
756           allocated = 4;
757           values = dbus_new (dbus_int32_t, allocated);
758           if (!values)
759             {
760               _dbus_warn ("could not allocate memory for INT32_ARRAY\n");
761               goto parse_failed;
762             }
763           
764           len = 0;
765           
766           _dbus_string_delete_first_word (&line);
767           _dbus_string_skip_blank (&line, 0, &i);
768           b = _dbus_string_get_byte (&line, i++);
769
770           if (b != '{')
771             goto parse_failed;
772
773           while (i < _dbus_string_get_length (&line))
774             {
775               _dbus_string_skip_blank (&line, i, &i);
776
777               if (!_dbus_string_parse_int (&line, i, &val, &i))
778                 {
779                   _dbus_warn ("could not parse integer for INT32_ARRAY\n");
780                   goto parse_failed;
781                 }
782
783               values[len++] = val;
784               if (len == allocated)
785                 {
786                   allocated *= 2;
787                   values = dbus_realloc (values, allocated * sizeof (dbus_int32_t));
788                   if (!values)
789                     {
790                       _dbus_warn ("could not allocate memory for INT32_ARRAY\n");
791                       goto parse_failed;
792                     }
793                 }
794               
795               _dbus_string_skip_blank (&line, i, &i);
796               
797               b = _dbus_string_get_byte (&line, i++);
798
799               if (b == '}')
800                 break;
801               else if (b != ',')
802                 goto parse_failed;
803             }
804
805           if (!_dbus_marshal_int32_array (dest, endian, values, len))
806             {
807               _dbus_warn ("failed to append INT32_ARRAY\n");
808               goto parse_failed;
809             }
810           dbus_free (values);
811           
812           PERFORM_UNALIGN (dest);
813         }
814       else if (_dbus_string_starts_with_c_str (&line,
815                                                "UINT32_ARRAY"))
816         {
817           SAVE_FOR_UNALIGN (dest, 4);
818           int i, len, allocated;
819           dbus_uint32_t *values;
820           long val;
821           unsigned char b;
822
823           allocated = 4;
824           values = dbus_new (dbus_uint32_t, allocated);
825           if (!values)
826             {
827               _dbus_warn ("could not allocate memory for UINT32_ARRAY\n");
828               goto parse_failed;
829             }
830           
831           len = 0;
832           
833           _dbus_string_delete_first_word (&line);
834           _dbus_string_skip_blank (&line, 0, &i);
835           b = _dbus_string_get_byte (&line, i++);
836
837           if (b != '{')
838             goto parse_failed;
839
840           while (i < _dbus_string_get_length (&line))
841             {
842               _dbus_string_skip_blank (&line, i, &i);
843
844               if (!_dbus_string_parse_int (&line, i, &val, &i))
845                 {
846                   _dbus_warn ("could not parse integer for UINT32_ARRAY\n");
847                   goto parse_failed;
848                 }
849
850               values[len++] = val;
851               if (len == allocated)
852                 {
853                   allocated *= 2;
854                   values = dbus_realloc (values, allocated * sizeof (dbus_uint32_t));
855                   if (!values)
856                     {
857                       _dbus_warn ("could not allocate memory for UINT32_ARRAY\n");
858                       goto parse_failed;
859                     }
860                 }
861               
862               _dbus_string_skip_blank (&line, i, &i);
863               
864               b = _dbus_string_get_byte (&line, i++);
865
866               if (b == '}')
867                 break;
868               else if (b != ',')
869                 goto parse_failed;
870             }
871
872           if (!_dbus_marshal_uint32_array (dest, endian, values, len))
873             {
874               _dbus_warn ("failed to append UINT32_ARRAY\n");
875               goto parse_failed;
876             }
877           dbus_free (values);
878           
879           PERFORM_UNALIGN (dest);
880         }
881       else if (_dbus_string_starts_with_c_str (&line,
882                                                "DOUBLE_ARRAY"))
883         {
884           SAVE_FOR_UNALIGN (dest, 8);
885           int i, len, allocated;
886           double *values;
887           double val;
888           unsigned char b;
889
890           allocated = 4;
891           values = dbus_new (double, allocated);
892           if (!values)
893             {
894               _dbus_warn ("could not allocate memory for DOUBLE_ARRAY\n");
895               goto parse_failed;
896             }
897           
898           len = 0;
899           
900           _dbus_string_delete_first_word (&line);
901           _dbus_string_skip_blank (&line, 0, &i);
902           b = _dbus_string_get_byte (&line, i++);
903
904           if (b != '{')
905             goto parse_failed;
906
907           while (i < _dbus_string_get_length (&line))
908             {
909               _dbus_string_skip_blank (&line, i, &i);
910
911               if (!_dbus_string_parse_double (&line, i, &val, &i))
912                 {
913                   _dbus_warn ("could not parse double for DOUBLE_ARRAY\n");
914                   goto parse_failed;
915                 }
916
917               values[len++] = val;
918               if (len == allocated)
919                 {
920                   allocated *= 2;
921                   values = dbus_realloc (values, allocated * sizeof (double));
922                   if (!values)
923                     {
924                       _dbus_warn ("could not allocate memory for DOUBLE_ARRAY\n");
925                       goto parse_failed;
926                     }
927                 }
928               
929               _dbus_string_skip_blank (&line, i, &i);
930               
931               b = _dbus_string_get_byte (&line, i++);
932
933               if (b == '}')
934                 break;
935               else if (b != ',')
936                 goto parse_failed;
937             }
938
939           if (!_dbus_marshal_double_array (dest, endian, values, len))
940             {
941               _dbus_warn ("failed to append DOUBLE_ARRAY\n");
942               goto parse_failed;
943             }
944           dbus_free (values);
945           
946           PERFORM_UNALIGN (dest);
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 DOUBLE_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, _DBUS_INT_MAX);
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                                                "BOOLEAN"))
1032         {
1033           unsigned char val;
1034
1035           _dbus_string_delete_first_word (&line);
1036
1037           if (_dbus_string_starts_with_c_str (&line, "true"))
1038             val = TRUE;
1039           else if (_dbus_string_starts_with_c_str (&line, "false"))
1040             val = FALSE;
1041           else
1042             {
1043               _dbus_warn ("could not parse BOOLEAN\n");
1044               goto parse_failed;
1045             }
1046           if (!_dbus_string_append_byte (dest, val))
1047             {
1048               _dbus_warn ("failed to append BOOLEAN\n");
1049               goto parse_failed;
1050             }
1051         }
1052       
1053       else if (_dbus_string_starts_with_c_str (&line,
1054                                                "INT32"))
1055         {
1056           SAVE_FOR_UNALIGN (dest, 4);
1057           long val;
1058           
1059           _dbus_string_delete_first_word (&line);
1060
1061           if (!_dbus_string_parse_int (&line, 0, &val, NULL))
1062             {
1063               _dbus_warn ("could not parse integer for INT32\n");
1064               goto parse_failed;
1065             }
1066           
1067           if (!_dbus_marshal_int32 (dest, endian,
1068                                     val))
1069             {
1070               _dbus_warn ("failed to append INT32\n");
1071               goto parse_failed;
1072             }
1073
1074           PERFORM_UNALIGN (dest);
1075         }
1076       else if (_dbus_string_starts_with_c_str (&line,
1077                                                "UINT32"))
1078         {
1079           SAVE_FOR_UNALIGN (dest, 4);
1080           long val;
1081           
1082           _dbus_string_delete_first_word (&line);
1083
1084           /* FIXME should have _dbus_string_parse_uint32 */
1085           if (!_dbus_string_parse_int (&line, 0, &val, NULL))
1086             goto parse_failed;
1087           
1088           if (!_dbus_marshal_uint32 (dest, endian,
1089                                      val))
1090             {
1091               _dbus_warn ("failed to append UINT32\n");
1092               goto parse_failed;
1093             }
1094
1095           PERFORM_UNALIGN (dest);
1096         }
1097       else if (_dbus_string_starts_with_c_str (&line,
1098                                                "DOUBLE"))
1099         {
1100           SAVE_FOR_UNALIGN (dest, 8);
1101           double val;
1102           
1103           _dbus_string_delete_first_word (&line);
1104
1105           if (!_dbus_string_parse_double (&line, 0, &val, NULL))
1106             goto parse_failed;
1107           
1108           if (!_dbus_marshal_double (dest, endian,
1109                                      val))
1110             {
1111               _dbus_warn ("failed to append DOUBLE\n");
1112               goto parse_failed;
1113             }
1114
1115           PERFORM_UNALIGN (dest);
1116         }
1117       else if (_dbus_string_starts_with_c_str (&line,
1118                                                "STRING"))
1119         {
1120           SAVE_FOR_UNALIGN (dest, 4);
1121           int size_offset;
1122           int old_len;
1123           
1124           _dbus_string_delete_first_word (&line);
1125
1126           size_offset = _dbus_string_get_length (dest);
1127           size_offset = _DBUS_ALIGN_VALUE (size_offset, 4);
1128           if (!_dbus_marshal_uint32 (dest, endian, 0))
1129             {
1130               _dbus_warn ("Failed to append string size\n");
1131               goto parse_failed;
1132             }
1133
1134           old_len = _dbus_string_get_length (dest);
1135           if (!append_quoted_string (dest, &line, 0, NULL))
1136             {
1137               _dbus_warn ("Failed to append quoted string\n");
1138               goto parse_failed;
1139             }
1140
1141           _dbus_marshal_set_uint32 (dest, endian, size_offset,
1142                                     /* subtract 1 for nul */
1143                                     _dbus_string_get_length (dest) - old_len - 1);
1144           
1145           PERFORM_UNALIGN (dest);
1146         }
1147       else
1148         goto parse_failed;
1149       
1150       if (!just_set_unalign && unalign)
1151         {
1152           _dbus_warn ("UNALIGN prior to something that isn't aligned\n");
1153           goto parse_failed;
1154         }
1155
1156       goto next_iteration; /* skip parse_failed */
1157       
1158     parse_failed:
1159       {
1160         const char *s;
1161         _dbus_string_get_const_data (&line, &s);
1162         _dbus_warn ("couldn't process line %d \"%s\"\n",
1163                     line_no, s);
1164         goto out;
1165       }
1166     }
1167
1168   _dbus_hash_iter_init (length_hash, &iter);
1169   while (_dbus_hash_iter_next (&iter))
1170     {
1171       SavedLength *sl = _dbus_hash_iter_get_value (&iter);
1172       const char *s;
1173
1174       _dbus_string_get_const_data (&sl->name, &s);
1175       
1176       if (sl->length < 0)
1177         {
1178           _dbus_warn ("Used LENGTH %s but never did END_LENGTH\n",
1179                       s);
1180           goto out;
1181         }
1182       else if (sl->offset < 0)
1183         {
1184           _dbus_warn ("Did END_LENGTH %s but never used LENGTH\n",
1185                       s);
1186           goto out;
1187         }
1188       else
1189         {
1190           if (sl->start < 0)
1191             sl->start = 0;
1192           
1193           _dbus_verbose ("Filling in length %s endian = %d offset = %d start = %d length = %d\n",
1194                          s, sl->endian, sl->offset, sl->start, sl->length);
1195           _dbus_marshal_set_int32 (dest,
1196                                    sl->endian,
1197                                    sl->offset,
1198                                    sl->length - sl->start);
1199         }
1200
1201       _dbus_hash_iter_remove_entry (&iter);
1202     }
1203   
1204   retval = TRUE;
1205   
1206  out:
1207   if (length_hash != NULL)
1208     _dbus_hash_table_unref (length_hash);
1209   
1210   _dbus_string_free (&file);
1211   _dbus_string_free (&line);
1212   return retval;
1213 }
1214
1215 /** @} */
1216 #endif /* DBUS_BUILD_TESTS */