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