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