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