2003-03-31 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   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  *   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))
330     return FALSE;
331
332   if (!_dbus_string_init (&line))
333     {
334       _dbus_string_free (&file);
335       return FALSE;
336     }
337
338   _dbus_verbose ("Loading %s\n", _dbus_string_get_const_data (filename));
339
340   dbus_error_init (&error);
341   if (!_dbus_file_get_contents (&file, filename, &error))
342     {
343       _dbus_warn ("Getting contents of %s failed: %s\n",
344                   _dbus_string_get_const_data (filename), error.message);
345       dbus_error_free (&error);
346       goto out;
347     }
348
349   length_hash = _dbus_hash_table_new (DBUS_HASH_STRING,
350                                       NULL,
351                                       free_saved_length);
352   if (length_hash == NULL)
353     goto out;
354   
355   endian = DBUS_COMPILER_BYTE_ORDER;
356   unalign = FALSE;
357   line_no = 0;
358  next_iteration:
359   while (_dbus_string_pop_line (&file, &line))
360     {
361       dbus_bool_t just_set_unalign;
362
363       just_set_unalign = FALSE;
364       line_no += 1;
365
366       _dbus_string_delete_leading_blanks (&line);
367
368       if (_dbus_string_get_length (&line) == 0)
369         {
370           /* empty line */
371           goto next_iteration;
372         }
373       else if (_dbus_string_starts_with_c_str (&line,
374                                                "#"))
375         {
376           /* Ignore this comment */
377           goto next_iteration;
378         }
379       else if (_dbus_string_starts_with_c_str (&line,
380                                                "VALID_HEADER"))
381         {
382           int i;
383           DBusString name;
384           
385           if (!_dbus_string_append_byte (dest, endian))
386             {
387               _dbus_warn ("could not append endianness\n");
388               goto parse_failed;
389             }
390
391           i = 0;
392           while (i < 3)
393             {
394               if (!_dbus_string_append_byte (dest, '\0'))
395                 {
396                   _dbus_warn ("could not append nul pad\n");
397                   goto parse_failed;
398                 }
399               ++i;
400             }
401
402           _dbus_string_init_const (&name, "Header");
403           if (!append_saved_length (dest, length_hash,
404                                     &name, _dbus_string_get_length (dest),
405                                     endian))
406             goto parse_failed;
407
408           _dbus_string_init_const (&name, "Body");
409           if (!append_saved_length (dest, length_hash,
410                                     &name, _dbus_string_get_length (dest),
411                                     endian))
412             goto parse_failed;
413           
414           /* client serial */
415           if (!_dbus_marshal_int32 (dest, endian, 1))
416             {
417               _dbus_warn ("couldn't append client serial\n");
418               goto parse_failed;
419             }
420         }
421       else if (_dbus_string_starts_with_c_str (&line,
422                                                "BIG_ENDIAN"))
423         {
424           endian = DBUS_BIG_ENDIAN;
425         }
426       else if (_dbus_string_starts_with_c_str (&line,
427                                                "LITTLE_ENDIAN"))
428         {
429           endian = DBUS_LITTLE_ENDIAN;
430         }
431       else if (_dbus_string_starts_with_c_str (&line,
432                                                "OPPOSITE_ENDIAN"))
433         {
434           if (endian == DBUS_BIG_ENDIAN)
435             endian = DBUS_LITTLE_ENDIAN;
436           else
437             endian = DBUS_BIG_ENDIAN;
438         }
439       else if (_dbus_string_starts_with_c_str (&line,
440                                                "ALIGN"))
441         {
442           long val;
443           int end;
444           int orig_len;
445           
446           _dbus_string_delete_first_word (&line);
447
448           if (!_dbus_string_parse_int (&line, 0, &val, &end))
449             {
450               _dbus_warn ("Failed to parse integer\n");
451               goto parse_failed;
452             }
453
454           if (val > 8)
455             {
456               _dbus_warn ("Aligning to %ld boundary is crack\n",
457                           val);
458               goto parse_failed;
459             }
460
461           orig_len = _dbus_string_get_length (dest);
462           
463           if (!_dbus_string_align_length (dest, val))
464             goto parse_failed;
465
466           if (_dbus_string_parse_int (&line, end, &val, NULL))
467             {
468               /* If there's an optional second int argument,
469                * fill in align padding with that value
470                */
471               if (val < 0 || val > 255)
472                 {
473                   _dbus_warn ("can't fill align padding with %ld, must be a byte value\n", val);
474                   goto parse_failed;
475                 }
476
477               end = orig_len;
478               while (end < _dbus_string_get_length (dest))
479                 {
480                   _dbus_string_set_byte (dest, end, val);
481                   ++end;
482                 }
483             }
484         }
485       else if (_dbus_string_starts_with_c_str (&line, "UNALIGN"))
486         {
487           unalign = TRUE;
488           just_set_unalign = TRUE;
489         }
490       else if (_dbus_string_starts_with_c_str (&line, "CHOP"))
491         {
492           long val;
493
494           /* FIXME if you CHOP the offset for a LENGTH
495            * command, we segfault.
496            */
497           
498           _dbus_string_delete_first_word (&line);
499
500           if (!_dbus_string_parse_int (&line, 0, &val, NULL))
501             {
502               _dbus_warn ("Failed to parse integer to chop\n");
503               goto parse_failed;
504             }
505
506           if (val > _dbus_string_get_length (dest))
507             {
508               _dbus_warn ("Trying to chop %ld bytes but we only have %d\n",
509                           val,
510                           _dbus_string_get_length (dest));
511               goto parse_failed;
512             }
513           
514           _dbus_string_shorten (dest, val);
515         }
516       else if (_dbus_string_starts_with_c_str (&line, "BYTE"))
517         {
518           unsigned char the_byte;
519           
520           _dbus_string_delete_first_word (&line);
521
522           if (_dbus_string_equal_c_str (&line, "'\\''"))
523             the_byte = '\'';
524           else if (_dbus_string_get_byte (&line, 0) == '\'' &&
525                    _dbus_string_get_length (&line) >= 3 &&
526                    _dbus_string_get_byte (&line, 2) == '\'')
527             the_byte = _dbus_string_get_byte (&line, 1);
528           else
529             {
530               long val;
531               if (!_dbus_string_parse_int (&line, 0, &val, NULL))
532                 {
533                   _dbus_warn ("Failed to parse integer for BYTE\n");
534                   goto parse_failed;
535                 }
536
537               if (val > 255)
538                 {
539                   _dbus_warn ("A byte must be in range 0-255 not %ld\n",
540                                  val);
541                   goto parse_failed;
542                 }
543               the_byte = (unsigned char) val;
544             }
545
546           _dbus_string_append_byte (dest, the_byte);
547         }
548       else if (_dbus_string_starts_with_c_str (&line,
549                                                "START_LENGTH"))
550         {
551           _dbus_string_delete_first_word (&line);
552
553           if (!save_start (length_hash, &line,
554                            _dbus_string_get_length (dest)))
555             {
556               _dbus_warn ("failed to save length start\n");
557               goto parse_failed;
558             }
559         }
560       else if (_dbus_string_starts_with_c_str (&line,
561                                                "END_LENGTH"))
562         {
563           _dbus_string_delete_first_word (&line);
564
565           if (!save_length (length_hash, &line,
566                             _dbus_string_get_length (dest)))
567             {
568               _dbus_warn ("failed to save length end\n");
569               goto parse_failed;
570             }
571         }
572       else if (_dbus_string_starts_with_c_str (&line,
573                                                "LENGTH"))
574         {
575           SAVE_FOR_UNALIGN (dest, 4);
576           
577           _dbus_string_delete_first_word (&line);
578
579           if (!append_saved_length (dest, length_hash,
580                                     &line,
581                                     unalign ? align_pad_start : align_pad_end,
582                                     endian))
583             {
584               _dbus_warn ("failed to add LENGTH\n");
585               goto parse_failed;
586             }
587
588           PERFORM_UNALIGN (dest);
589         }
590       else if (_dbus_string_starts_with_c_str (&line,
591                                                "FIELD_NAME"))
592         {
593           _dbus_string_delete_first_word (&line);
594
595           if (_dbus_string_get_length (&line) != 4)
596             {
597               _dbus_warn ("Field name must be four characters not \"%s\"\n",
598                           _dbus_string_get_const_data (&line));
599               goto parse_failed;
600             }
601
602           if (unalign)
603             unalign = FALSE;
604           else
605             _dbus_string_align_length (dest, 4);
606           
607           if (!_dbus_string_copy (&line, 0, dest,
608                                   _dbus_string_get_length (dest)))
609             goto parse_failed;
610         }
611       else if (_dbus_string_starts_with_c_str (&line,
612                                                "TYPE"))
613         {
614           int code;
615           
616           _dbus_string_delete_first_word (&line);          
617
618           if (_dbus_string_starts_with_c_str (&line, "INVALID"))
619             code = DBUS_TYPE_INVALID;
620           else if (_dbus_string_starts_with_c_str (&line, "NIL"))
621             code = DBUS_TYPE_NIL;
622           else if (_dbus_string_starts_with_c_str (&line, "BOOLEAN_ARRAY"))
623             code = DBUS_TYPE_BOOLEAN_ARRAY;
624           else if (_dbus_string_starts_with_c_str (&line, "INT32_ARRAY"))
625             code = DBUS_TYPE_INT32_ARRAY;
626           else if (_dbus_string_starts_with_c_str (&line, "UINT32_ARRAY"))
627             code = DBUS_TYPE_UINT32_ARRAY;
628           else if (_dbus_string_starts_with_c_str (&line, "DOUBLE_ARRAY"))
629             code = DBUS_TYPE_DOUBLE_ARRAY;
630           else if (_dbus_string_starts_with_c_str (&line, "BYTE_ARRAY"))
631             code = DBUS_TYPE_BYTE_ARRAY;
632           else if (_dbus_string_starts_with_c_str (&line, "STRING_ARRAY"))
633             code = DBUS_TYPE_STRING_ARRAY;
634           else if (_dbus_string_starts_with_c_str (&line, "BOOLEAN"))
635             code = DBUS_TYPE_BOOLEAN;
636           else if (_dbus_string_starts_with_c_str (&line, "INT32"))
637             code = DBUS_TYPE_INT32;
638           else if (_dbus_string_starts_with_c_str (&line, "UINT32"))
639             code = DBUS_TYPE_UINT32;
640           else if (_dbus_string_starts_with_c_str (&line, "DOUBLE"))
641             code = DBUS_TYPE_DOUBLE;
642           else if (_dbus_string_starts_with_c_str (&line, "STRING"))
643             code = DBUS_TYPE_STRING;
644           else if (_dbus_string_starts_with_c_str (&line, "DICT"))
645             code = DBUS_TYPE_DICT;
646           else
647             {
648               _dbus_warn ("%s is not a valid type name\n", _dbus_string_get_const_data (&line));
649               goto parse_failed;
650             }
651
652           if (!_dbus_string_append_byte (dest, code))
653             {
654               _dbus_warn ("could not append typecode byte\n");
655               goto parse_failed;
656             }
657         }
658       else if (_dbus_string_starts_with_c_str (&line,
659                                                "BOOLEAN_ARRAY"))
660         {
661           SAVE_FOR_UNALIGN (dest, 4);
662           int i, len, allocated;
663           unsigned char *values;
664           unsigned char b, val;
665
666           allocated = 4;
667           values = dbus_new (unsigned char, allocated);
668           if (!values)
669             {
670               _dbus_warn ("could not allocate memory for BOOLEAN_ARRAY\n");
671               goto parse_failed;
672             }
673
674           len = 0;
675           
676           _dbus_string_delete_first_word (&line);
677           _dbus_string_skip_blank (&line, 0, &i);
678           b = _dbus_string_get_byte (&line, i++);
679           
680           if (b != '{')
681             goto parse_failed;
682
683           while (i < _dbus_string_get_length (&line))
684             {
685               _dbus_string_skip_blank (&line, i, &i);         
686               
687               if (_dbus_string_find_to (&line, i, i + 5,
688                                         "false", NULL))
689                 {
690                   i += 5;
691                   val = TRUE;
692                 }
693               else if (_dbus_string_find_to (&line, i, i + 4,
694                                              "true", NULL))
695                 {
696                   i += 4;
697                   val = FALSE;
698                 }
699               else
700                 {
701                   _dbus_warn ("could not parse BOOLEAN_ARRAY\n");
702                   goto parse_failed;
703                 }
704
705               values[len++] = val;
706               if (len == allocated)
707                 {
708                   allocated *= 2;
709                   values = dbus_realloc (values, allocated * sizeof (unsigned char));
710                   if (!values)
711                     {
712                       _dbus_warn ("could not allocate memory for BOOLEAN_ARRAY\n");
713                       goto parse_failed;
714                     }
715                 }
716               
717               _dbus_string_skip_blank (&line, i, &i);
718               
719               b = _dbus_string_get_byte (&line, i++);
720
721               if (b == '}')
722                 break;
723               else if (b != ',')
724                 goto parse_failed;
725             }
726
727           if (!_dbus_marshal_int32 (dest, endian, len) ||
728               !_dbus_string_append_len (dest, values, len))
729             {
730               _dbus_warn ("failed to append BOOLEAN_ARRAY\n");
731               goto parse_failed;
732             }
733           dbus_free (values);
734           
735           PERFORM_UNALIGN (dest);
736         }
737       else if (_dbus_string_starts_with_c_str (&line,
738                                                "INT32_ARRAY"))
739         {
740           SAVE_FOR_UNALIGN (dest, 4);
741           int i, len, allocated;
742           dbus_int32_t *values;
743           long val;
744           unsigned char b;
745
746           allocated = 4;
747           values = dbus_new (dbus_int32_t, allocated);
748           if (!values)
749             {
750               _dbus_warn ("could not allocate memory for INT32_ARRAY\n");
751               goto parse_failed;
752             }
753           
754           len = 0;
755           
756           _dbus_string_delete_first_word (&line);
757           _dbus_string_skip_blank (&line, 0, &i);
758           b = _dbus_string_get_byte (&line, i++);
759
760           if (b != '{')
761             goto parse_failed;
762
763           while (i < _dbus_string_get_length (&line))
764             {
765               _dbus_string_skip_blank (&line, i, &i);
766
767               if (!_dbus_string_parse_int (&line, i, &val, &i))
768                 {
769                   _dbus_warn ("could not parse integer for INT32_ARRAY\n");
770                   goto parse_failed;
771                 }
772
773               values[len++] = val;
774               if (len == allocated)
775                 {
776                   allocated *= 2;
777                   values = dbus_realloc (values, allocated * sizeof (dbus_int32_t));
778                   if (!values)
779                     {
780                       _dbus_warn ("could not allocate memory for INT32_ARRAY\n");
781                       goto parse_failed;
782                     }
783                 }
784               
785               _dbus_string_skip_blank (&line, i, &i);
786               
787               b = _dbus_string_get_byte (&line, i++);
788
789               if (b == '}')
790                 break;
791               else if (b != ',')
792                 goto parse_failed;
793             }
794
795           if (!_dbus_marshal_int32_array (dest, endian, values, len))
796             {
797               _dbus_warn ("failed to append INT32_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                                                "UINT32_ARRAY"))
806         {
807           SAVE_FOR_UNALIGN (dest, 4);
808           int i, len, allocated;
809           dbus_uint32_t *values;
810           long val;
811           unsigned char b;
812
813           allocated = 4;
814           values = dbus_new (dbus_uint32_t, allocated);
815           if (!values)
816             {
817               _dbus_warn ("could not allocate memory for UINT32_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 UINT32_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_uint32_t));
845                   if (!values)
846                     {
847                       _dbus_warn ("could not allocate memory for UINT32_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_uint32_array (dest, endian, values, len))
863             {
864               _dbus_warn ("failed to append UINT32_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                                                "DOUBLE_ARRAY"))
873         {
874           SAVE_FOR_UNALIGN (dest, 8);
875           int i, len, allocated;
876           double *values;
877           double val;
878           unsigned char b;
879
880           allocated = 4;
881           values = dbus_new (double, allocated);
882           if (!values)
883             {
884               _dbus_warn ("could not allocate memory for DOUBLE_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_double (&line, i, &val, &i))
902                 {
903                   _dbus_warn ("could not parse double for DOUBLE_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 (double));
912                   if (!values)
913                     {
914                       _dbus_warn ("could not allocate memory for DOUBLE_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_double_array (dest, endian, values, len))
930             {
931               _dbus_warn ("failed to append DOUBLE_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                                                "STRING_ARRAY"))
940         {
941           SAVE_FOR_UNALIGN (dest, 4);
942           int i, len, allocated;
943           char **values;
944           char *val;
945           DBusString val_str;
946           unsigned char b;
947
948           allocated = 4;
949           values = dbus_new (char *, allocated);
950           if (!values)
951             {
952               _dbus_warn ("could not allocate memory for DOUBLE_ARRAY\n");
953               goto parse_failed;
954             }
955           
956           len = 0;
957           
958           _dbus_string_delete_first_word (&line);
959           _dbus_string_skip_blank (&line, 0, &i);
960           b = _dbus_string_get_byte (&line, i++);
961
962           if (b != '{')
963             goto parse_failed;
964
965           _dbus_string_init (&val_str);
966           while (i < _dbus_string_get_length (&line))
967             {
968               _dbus_string_skip_blank (&line, i, &i);
969
970               if (!append_quoted_string (&val_str, &line, i, &i))
971                 {
972                   _dbus_warn ("could not parse quoted string for STRING_ARRAY\n");
973                   goto parse_failed;
974                 }
975               i++;
976
977               if (!_dbus_string_steal_data (&val_str, &val))
978                 {
979                   _dbus_warn ("could not allocate memory for STRING_ARRAY string\n");
980                   goto parse_failed;
981                 }
982               
983               values[len++] = val;
984               if (len == allocated)
985                 {
986                   allocated *= 2;
987                   values = dbus_realloc (values, allocated * sizeof (char *));
988                   if (!values)
989                     {
990                       _dbus_warn ("could not allocate memory for STRING_ARRAY\n");
991                       goto parse_failed;
992                     }
993                 }
994               
995               _dbus_string_skip_blank (&line, i, &i);
996               
997               b = _dbus_string_get_byte (&line, i++);
998
999               if (b == '}')
1000                 break;
1001               else if (b != ',')
1002                 {
1003                   _dbus_warn ("missing comma when parsing STRING_ARRAY\n");
1004                   goto parse_failed;
1005                 }
1006             }
1007           _dbus_string_free (&val_str);
1008           
1009           if (!_dbus_marshal_string_array (dest, endian, (const char **)values, len))
1010             {
1011               _dbus_warn ("failed to append STRING_ARRAY\n");
1012               goto parse_failed;
1013             }
1014
1015           values[len] = NULL;
1016           dbus_free_string_array (values);
1017           
1018           PERFORM_UNALIGN (dest);
1019         }
1020       else if (_dbus_string_starts_with_c_str (&line,
1021                                                "BOOLEAN"))
1022         {
1023           unsigned char val;
1024
1025           _dbus_string_delete_first_word (&line);
1026
1027           if (_dbus_string_starts_with_c_str (&line, "true"))
1028             val = TRUE;
1029           else if (_dbus_string_starts_with_c_str (&line, "false"))
1030             val = FALSE;
1031           else
1032             {
1033               _dbus_warn ("could not parse BOOLEAN\n");
1034               goto parse_failed;
1035             }
1036           if (!_dbus_string_append_byte (dest, val))
1037             {
1038               _dbus_warn ("failed to append BOOLEAN\n");
1039               goto parse_failed;
1040             }
1041         }
1042       
1043       else if (_dbus_string_starts_with_c_str (&line,
1044                                                "INT32"))
1045         {
1046           SAVE_FOR_UNALIGN (dest, 4);
1047           long val;
1048           
1049           _dbus_string_delete_first_word (&line);
1050
1051           if (!_dbus_string_parse_int (&line, 0, &val, NULL))
1052             {
1053               _dbus_warn ("could not parse integer for INT32\n");
1054               goto parse_failed;
1055             }
1056           
1057           if (!_dbus_marshal_int32 (dest, endian,
1058                                     val))
1059             {
1060               _dbus_warn ("failed to append INT32\n");
1061               goto parse_failed;
1062             }
1063
1064           PERFORM_UNALIGN (dest);
1065         }
1066       else if (_dbus_string_starts_with_c_str (&line,
1067                                                "UINT32"))
1068         {
1069           SAVE_FOR_UNALIGN (dest, 4);
1070           long val;
1071           
1072           _dbus_string_delete_first_word (&line);
1073
1074           /* FIXME should have _dbus_string_parse_uint32 */
1075           if (!_dbus_string_parse_int (&line, 0, &val, NULL))
1076             goto parse_failed;
1077           
1078           if (!_dbus_marshal_uint32 (dest, endian,
1079                                      val))
1080             {
1081               _dbus_warn ("failed to append UINT32\n");
1082               goto parse_failed;
1083             }
1084
1085           PERFORM_UNALIGN (dest);
1086         }
1087       else if (_dbus_string_starts_with_c_str (&line,
1088                                                "DOUBLE"))
1089         {
1090           SAVE_FOR_UNALIGN (dest, 8);
1091           double val;
1092           
1093           _dbus_string_delete_first_word (&line);
1094
1095           if (!_dbus_string_parse_double (&line, 0, &val, NULL))
1096             goto parse_failed;
1097           
1098           if (!_dbus_marshal_double (dest, endian,
1099                                      val))
1100             {
1101               _dbus_warn ("failed to append DOUBLE\n");
1102               goto parse_failed;
1103             }
1104
1105           PERFORM_UNALIGN (dest);
1106         }
1107       else if (_dbus_string_starts_with_c_str (&line,
1108                                                "STRING"))
1109         {
1110           SAVE_FOR_UNALIGN (dest, 4);
1111           int size_offset;
1112           int old_len;
1113           
1114           _dbus_string_delete_first_word (&line);
1115
1116           size_offset = _dbus_string_get_length (dest);
1117           size_offset = _DBUS_ALIGN_VALUE (size_offset, 4);
1118           if (!_dbus_marshal_uint32 (dest, endian, 0))
1119             {
1120               _dbus_warn ("Failed to append string size\n");
1121               goto parse_failed;
1122             }
1123
1124           old_len = _dbus_string_get_length (dest);
1125           if (!append_quoted_string (dest, &line, 0, NULL))
1126             {
1127               _dbus_warn ("Failed to append quoted string\n");
1128               goto parse_failed;
1129             }
1130
1131           _dbus_marshal_set_uint32 (dest, endian, size_offset,
1132                                     /* subtract 1 for nul */
1133                                     _dbus_string_get_length (dest) - old_len - 1);
1134           
1135           PERFORM_UNALIGN (dest);
1136         }
1137       else
1138         goto parse_failed;
1139       
1140       if (!just_set_unalign && unalign)
1141         {
1142           _dbus_warn ("UNALIGN prior to something that isn't aligned\n");
1143           goto parse_failed;
1144         }
1145
1146       goto next_iteration; /* skip parse_failed */
1147       
1148     parse_failed:
1149       {
1150         _dbus_warn ("couldn't process line %d \"%s\"\n",
1151                     line_no, _dbus_string_get_const_data (&line));
1152         goto out;
1153       }
1154     }
1155
1156   _dbus_hash_iter_init (length_hash, &iter);
1157   while (_dbus_hash_iter_next (&iter))
1158     {
1159       SavedLength *sl = _dbus_hash_iter_get_value (&iter);
1160       const char *s;
1161
1162       s = _dbus_string_get_const_data (&sl->name);
1163       
1164       if (sl->length < 0)
1165         {
1166           _dbus_warn ("Used LENGTH %s but never did END_LENGTH\n",
1167                       s);
1168           goto out;
1169         }
1170       else if (sl->offset < 0)
1171         {
1172           _dbus_warn ("Did END_LENGTH %s but never used LENGTH\n",
1173                       s);
1174           goto out;
1175         }
1176       else
1177         {
1178           if (sl->start < 0)
1179             sl->start = 0;
1180           
1181           _dbus_verbose ("Filling in length %s endian = %d offset = %d start = %d length = %d\n",
1182                          s, sl->endian, sl->offset, sl->start, sl->length);
1183           _dbus_marshal_set_int32 (dest,
1184                                    sl->endian,
1185                                    sl->offset,
1186                                    sl->length - sl->start);
1187         }
1188
1189       _dbus_hash_iter_remove_entry (&iter);
1190     }
1191   
1192   retval = TRUE;
1193   
1194  out:
1195   if (length_hash != NULL)
1196     _dbus_hash_table_unref (length_hash);
1197   
1198   _dbus_string_free (&file);
1199   _dbus_string_free (&line);
1200   return retval;
1201 }
1202
1203 /** @} */
1204 #endif /* DBUS_BUILD_TESTS */