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