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