2003-02-16 Anders Carlsson <andersca@codefactory.se>
[platform/upstream/dbus.git] / dbus / dbus-message-builder.c
1 /* -*- mode: C; c-file-style: "gnu" -*- */
2 /* dbus-message-builder.c Build messages from text files for testing (internal to D-BUS implementation)
3  * 
4  * Copyright (C) 2003 Red Hat, Inc.
5  *
6  * Licensed under the Academic Free License version 1.2
7  * 
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  * 
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
21  *
22  */
23 #include <config.h>
24
25 #ifdef DBUS_BUILD_TESTS
26
27 #include "dbus-message-builder.h"
28 #include "dbus-hash.h"
29 #include "dbus-internals.h"
30 #include "dbus-marshal.h"
31
32 /**
33  * @defgroup DBusMessageBuilder code for loading test message data
34  * @ingroup  DBusInternals
35  * @brief code for loading up test data for unit tests
36  *
37  * The code in here is used for unit testing, it loads
38  * up message data from a description in a file.
39  *
40  * @{
41  */
42
43 typedef struct
44 {
45   DBusString name;
46   int start;  /**< Calculate length since here */
47   int length; /**< length to write */
48   int offset; /**< where to write it into the data */
49   int endian; /**< endianness to write with */
50 } SavedLength;
51
52 static void
53 free_saved_length (void *data)
54 {
55   SavedLength *sl = data;
56
57   if (sl == NULL)
58     return; /* all hash free functions have to accept NULL */
59   
60   _dbus_string_free (&sl->name);
61   dbus_free (sl);
62 }
63
64 static SavedLength*
65 ensure_saved_length (DBusHashTable    *hash,
66                      const DBusString *name)
67 {
68   SavedLength *sl;
69   const char *s;
70
71   _dbus_string_get_const_data (name, &s);
72
73   sl = _dbus_hash_table_lookup_string (hash, s);
74   if (sl != NULL)
75     return sl;
76   
77   sl = dbus_new0 (SavedLength, 1);
78
79   if (!_dbus_string_init (&sl->name, _DBUS_INT_MAX))
80     {
81       dbus_free (sl);
82       return NULL;
83     }
84
85   if (!_dbus_string_copy (name, 0, &sl->name, 0))
86     goto failed;
87
88   _dbus_string_get_const_data (&sl->name, &s);
89
90   if (!_dbus_hash_table_insert_string (hash, (char*)s, sl))
91     goto failed;
92
93   sl->start = -1;
94   sl->length = -1;
95   sl->offset = -1;
96   sl->endian = -1;
97   
98   return sl;
99   
100  failed:
101   free_saved_length (sl);
102   return NULL;
103 }
104
105 static dbus_bool_t
106 save_start (DBusHashTable    *hash,
107             const DBusString *name,
108             int               start)
109 {
110   SavedLength *sl;
111
112   sl = ensure_saved_length (hash, name);
113
114   if (sl == NULL)
115     return FALSE;
116   else if (sl->start >= 0)
117     {
118       _dbus_warn ("Same START_LENGTH given twice\n");
119       return FALSE;
120     }
121   else
122     sl->start = start;
123
124   return TRUE;
125 }
126
127 static dbus_bool_t
128 save_length (DBusHashTable    *hash,
129              const DBusString *name,
130              int               length)
131 {
132   SavedLength *sl;
133
134   sl = ensure_saved_length (hash, name);
135
136   if (sl == NULL)
137     return FALSE;
138   else if (sl->length >= 0)
139     {
140       _dbus_warn ("Same END_LENGTH given twice\n");
141       return FALSE;
142     }
143   else
144     sl->length = length;
145
146   return TRUE;
147 }
148
149 static dbus_bool_t
150 save_offset (DBusHashTable    *hash,
151              const DBusString *name,
152              int               offset,
153              int               endian)
154 {
155   SavedLength *sl;
156
157   sl = ensure_saved_length (hash, name);
158
159   if (sl == NULL)
160     return FALSE;
161   else if (sl->offset >= 0)
162     {
163       _dbus_warn ("Same LENGTH given twice\n");
164       return FALSE;
165     }
166   else
167     {
168       sl->offset = offset;
169       sl->endian = endian;
170     }
171
172   return TRUE;
173 }
174
175 /** Saves the segment to delete in order to unalign the next item */
176 #define SAVE_FOR_UNALIGN(str, boundary)                                 \
177   int align_pad_start = _dbus_string_get_length (str);                  \
178   int align_pad_end = _DBUS_ALIGN_VALUE (align_pad_start, (boundary))
179
180 /** Deletes the alignment padding */
181 #define PERFORM_UNALIGN(str)                                    \
182   if (unalign)                                                  \
183     {                                                           \
184       _dbus_string_delete ((str), align_pad_start,              \
185                            align_pad_end - align_pad_start);    \
186       unalign = FALSE;                                          \
187     }
188
189
190 static dbus_bool_t
191 append_quoted_string (DBusString       *dest,
192                       const DBusString *quoted)
193 {
194   dbus_bool_t in_quotes = FALSE;
195   int i;
196
197   i = 0;
198   while (i < _dbus_string_get_length (quoted))
199     {
200       unsigned char b;
201
202       b = _dbus_string_get_byte (quoted, i);
203       
204       if (in_quotes)
205         {
206           if (b == '\'')
207             in_quotes = FALSE;
208           else
209             {
210               if (!_dbus_string_append_byte (dest, b))
211                 return FALSE;
212             }
213         }
214       else
215         {
216           if (b == '\'')
217             in_quotes = TRUE;
218           else if (b == ' ' || b == '\n' || b == '\t')
219             break; /* end on whitespace if not quoted */
220           else
221             {
222               if (!_dbus_string_append_byte (dest, b))
223                 return FALSE;
224             }
225         }
226       
227       ++i;
228     }
229
230   if (!_dbus_string_append_byte (dest, '\0'))
231     return FALSE;
232   return TRUE;
233 }
234
235 static dbus_bool_t
236 append_saved_length (DBusString       *dest,
237                      DBusHashTable    *length_hash,
238                      const DBusString *name,
239                      int               offset,
240                      int               endian)
241 {
242   if (!save_offset (length_hash, name,
243                     offset, endian))
244     {
245       _dbus_warn ("failed to save offset to LENGTH\n");
246       return FALSE;
247     }
248   
249   if (!_dbus_marshal_uint32 (dest, endian,
250                              -1))
251     {
252       _dbus_warn ("failed to append a length\n");
253       return FALSE;
254     }
255
256   return TRUE;
257 }
258
259 /**
260  * Reads the given filename, which should be in "message description
261  * language" (look at some examples), and builds up the message data
262  * from it.  The message data may be invalid, or valid.
263  *
264  * The parser isn't very strict, it's just a hack for test programs.
265  * 
266  * The file format is:
267  * @code
268  *   VALID_HEADER normal header; byte order, padding, header len, body len, serial
269  *   BIG_ENDIAN switch to big endian
270  *   LITTLE_ENDIAN switch to little endian
271  *   OPPOSITE_ENDIAN switch to opposite endian
272  *   ALIGN <N> aligns to the given value
273  *   UNALIGN skips alignment for the next marshal
274  *   BYTE <N> inserts the given integer in [0,255] or char in 'a' format
275  *   START_LENGTH <name> marks the start of a length to measure
276  *   END_LENGTH <name> records the length since START_LENGTH under the given name
277  *                     (or if no START_LENGTH, absolute length)
278  *   LENGTH <name> inserts the saved length of the same name
279  *   CHOP <N> chops last N bytes off the data
280  *   FIELD_NAME <abcd> inserts 4-byte field name
281  *   TYPE <typename> inserts a typecode byte 
282  * @endcode
283  * 
284  * Following commands insert aligned data unless
285  * preceded by "UNALIGN":
286  * @code
287  *   INT32 <N> marshals an INT32
288  *   UINT32 <N> marshals a UINT32
289  *   DOUBLE <N> marshals a double
290  *   STRING 'Foo' marshals a string
291  * @endcode
292  *
293  * @todo add support for array types INT32_ARRAY { 3, 4, 5, 6 }
294  * and so forth.
295  * 
296  * @param dest the string to append the message data to
297  * @param filename the filename to load
298  * @returns #TRUE on success
299  */
300 dbus_bool_t
301 _dbus_message_data_load (DBusString       *dest,
302                          const DBusString *filename)
303 {
304   DBusString file;
305   DBusResultCode result;
306   DBusString line;
307   dbus_bool_t retval;
308   int line_no;
309   dbus_bool_t unalign;
310   DBusHashTable *length_hash;
311   int endian;
312   DBusHashIter iter;
313   
314   retval = FALSE;
315   length_hash = NULL;
316   
317   if (!_dbus_string_init (&file, _DBUS_INT_MAX))
318     return FALSE;
319
320   if (!_dbus_string_init (&line, _DBUS_INT_MAX))
321     {
322       _dbus_string_free (&file);
323       return FALSE;
324     }
325   
326   if ((result = _dbus_file_get_contents (&file, filename)) != DBUS_RESULT_SUCCESS)
327     {
328       const char *s;
329       _dbus_string_get_const_data (filename, &s);
330       _dbus_warn ("Getting contents of %s failed: %s\n",
331                   s, dbus_result_to_string (result));
332                      
333       goto out;
334     }
335
336   length_hash = _dbus_hash_table_new (DBUS_HASH_STRING,
337                                       NULL,
338                                       free_saved_length);
339   if (length_hash == NULL)
340     goto out;
341   
342   endian = DBUS_COMPILER_BYTE_ORDER;
343   unalign = FALSE;
344   line_no = 0;
345  next_iteration:
346   while (_dbus_string_pop_line (&file, &line))
347     {
348       dbus_bool_t just_set_unalign;
349
350       just_set_unalign = FALSE;
351       line_no += 1;
352
353       _dbus_string_delete_leading_blanks (&line);
354
355       if (_dbus_string_get_length (&line) == 0)
356         {
357           /* empty line */
358           goto next_iteration;
359         }
360       else if (_dbus_string_starts_with_c_str (&line,
361                                                "#"))
362         {
363           /* Ignore this comment */
364           goto next_iteration;
365         }
366       else if (_dbus_string_starts_with_c_str (&line,
367                                                "VALID_HEADER"))
368         {
369           int i;
370           DBusString name;
371           
372           if (!_dbus_string_append_byte (dest, endian))
373             {
374               _dbus_warn ("could not append endianness\n");
375               goto parse_failed;
376             }
377
378           i = 0;
379           while (i < 3)
380             {
381               if (!_dbus_string_append_byte (dest, '\0'))
382                 {
383                   _dbus_warn ("could not append nul pad\n");
384                   goto parse_failed;
385                 }
386               ++i;
387             }
388
389           _dbus_string_init_const (&name, "Header");
390           if (!append_saved_length (dest, length_hash,
391                                     &name, _dbus_string_get_length (dest),
392                                     endian))
393             goto parse_failed;
394
395           _dbus_string_init_const (&name, "Body");
396           if (!append_saved_length (dest, length_hash,
397                                     &name, _dbus_string_get_length (dest),
398                                     endian))
399             goto parse_failed;
400           
401           /* client serial */
402           if (!_dbus_marshal_int32 (dest, endian, 1))
403             {
404               _dbus_warn ("couldn't append client serial\n");
405               goto parse_failed;
406             }
407         }
408       else if (_dbus_string_starts_with_c_str (&line,
409                                                "BIG_ENDIAN"))
410         {
411           endian = DBUS_BIG_ENDIAN;
412         }
413       else if (_dbus_string_starts_with_c_str (&line,
414                                                "LITTLE_ENDIAN"))
415         {
416           endian = DBUS_LITTLE_ENDIAN;
417         }
418       else if (_dbus_string_starts_with_c_str (&line,
419                                                "OPPOSITE_ENDIAN"))
420         {
421           if (endian == DBUS_BIG_ENDIAN)
422             endian = DBUS_LITTLE_ENDIAN;
423           else
424             endian = DBUS_BIG_ENDIAN;
425         }
426       else if (_dbus_string_starts_with_c_str (&line,
427                                                "ALIGN"))
428         {
429           long val;
430
431           _dbus_string_delete_first_word (&line);
432
433           if (!_dbus_string_parse_int (&line, 0, &val, NULL))
434             {
435               _dbus_warn ("Failed to parse integer\n");
436               goto parse_failed;
437             }
438
439           if (val > 16)
440             {
441               _dbus_warn ("Aligning to %ld boundary is crack\n",
442                           val);
443               goto parse_failed;
444             }
445           
446           if (!_dbus_string_align_length (dest, val))
447             goto parse_failed;
448         }
449       else if (_dbus_string_starts_with_c_str (&line, "UNALIGN"))
450         {
451           unalign = TRUE;
452           just_set_unalign = TRUE;
453         }
454       else if (_dbus_string_starts_with_c_str (&line, "CHOP"))
455         {
456           long val;
457
458           /* FIXME if you CHOP the offset for a LENGTH
459            * command, we segfault.
460            */
461           
462           _dbus_string_delete_first_word (&line);
463
464           if (!_dbus_string_parse_int (&line, 0, &val, NULL))
465             {
466               _dbus_warn ("Failed to parse integer to chop\n");
467               goto parse_failed;
468             }
469
470           if (val > _dbus_string_get_length (dest))
471             {
472               _dbus_warn ("Trying to chop %ld bytes but we only have %d\n",
473                           val,
474                           _dbus_string_get_length (dest));
475               goto parse_failed;
476             }
477           
478           _dbus_string_shorten (dest, val);
479         }
480       else if (_dbus_string_starts_with_c_str (&line, "BYTE"))
481         {
482           unsigned char the_byte;
483           
484           _dbus_string_delete_first_word (&line);
485
486           if (_dbus_string_equal_c_str (&line, "'\\''"))
487             the_byte = '\'';
488           else if (_dbus_string_get_byte (&line, 0) == '\'' &&
489                    _dbus_string_get_length (&line) >= 3 &&
490                    _dbus_string_get_byte (&line, 2) == '\'')
491             the_byte = _dbus_string_get_byte (&line, 1);
492           else
493             {
494               long val;
495               if (!_dbus_string_parse_int (&line, 0, &val, NULL))
496                 {
497                   _dbus_warn ("Failed to parse integer for BYTE\n");
498                   goto parse_failed;
499                 }
500
501               if (val > 255)
502                 {
503                   _dbus_warn ("A byte must be in range 0-255 not %ld\n",
504                                  val);
505                   goto parse_failed;
506                 }
507               the_byte = (unsigned char) val;
508             }
509
510           _dbus_string_append_byte (dest, the_byte);
511         }
512       else if (_dbus_string_starts_with_c_str (&line,
513                                                "START_LENGTH"))
514         {
515           _dbus_string_delete_first_word (&line);
516
517           if (!save_start (length_hash, &line,
518                            _dbus_string_get_length (dest)))
519             {
520               _dbus_warn ("failed to save length start\n");
521               goto parse_failed;
522             }
523         }
524       else if (_dbus_string_starts_with_c_str (&line,
525                                                "END_LENGTH"))
526         {
527           _dbus_string_delete_first_word (&line);
528
529           if (!save_length (length_hash, &line,
530                             _dbus_string_get_length (dest)))
531             {
532               _dbus_warn ("failed to save length end\n");
533               goto parse_failed;
534             }
535         }
536       else if (_dbus_string_starts_with_c_str (&line,
537                                                "LENGTH"))
538         {
539           SAVE_FOR_UNALIGN (dest, 4);
540           
541           _dbus_string_delete_first_word (&line);
542
543           if (!append_saved_length (dest, length_hash,
544                                     &line,
545                                     unalign ? align_pad_start : align_pad_end,
546                                     endian))
547             {
548               _dbus_warn ("failed to add LENGTH\n");
549               goto parse_failed;
550             }
551
552           PERFORM_UNALIGN (dest);
553         }
554       else if (_dbus_string_starts_with_c_str (&line,
555                                                "FIELD_NAME"))
556         {
557           _dbus_string_delete_first_word (&line);
558
559           if (_dbus_string_get_length (&line) != 4)
560             {
561               const char *s;
562               _dbus_string_get_const_data (&line, &s);
563               _dbus_warn ("Field name must be four characters not \"%s\"\n",
564                              s);
565               goto parse_failed;
566             }
567
568           if (unalign)
569             unalign = FALSE;
570           else
571             _dbus_string_align_length (dest, 4);
572           
573           if (!_dbus_string_copy (&line, 0, dest,
574                                   _dbus_string_get_length (dest)))
575             goto parse_failed;
576         }
577       else if (_dbus_string_starts_with_c_str (&line,
578                                                "TYPE"))
579         {
580           int code;
581           
582           _dbus_string_delete_first_word (&line);          
583
584           if (_dbus_string_starts_with_c_str (&line, "INVALID"))
585             code = DBUS_TYPE_INVALID;
586           else if (_dbus_string_starts_with_c_str (&line, "NIL"))
587             code = DBUS_TYPE_NIL;
588           else if (_dbus_string_starts_with_c_str (&line, "INT32"))
589             code = DBUS_TYPE_INT32;
590           else if (_dbus_string_starts_with_c_str (&line, "UINT32"))
591             code = DBUS_TYPE_UINT32;
592           else if (_dbus_string_starts_with_c_str (&line, "DOUBLE"))
593             code = DBUS_TYPE_DOUBLE;
594           else if (_dbus_string_starts_with_c_str (&line, "STRING"))
595             code = DBUS_TYPE_STRING;
596           else if (_dbus_string_starts_with_c_str (&line, "INT32_ARRAY"))
597             code = DBUS_TYPE_INT32_ARRAY;
598           else if (_dbus_string_starts_with_c_str (&line, "UINT32_ARRAY"))
599             code = DBUS_TYPE_UINT32_ARRAY;
600           else if (_dbus_string_starts_with_c_str (&line, "DOUBLE_ARRAY"))
601             code = DBUS_TYPE_DOUBLE_ARRAY;
602           else if (_dbus_string_starts_with_c_str (&line, "BYTE_ARRAY"))
603             code = DBUS_TYPE_BYTE_ARRAY;
604           else if (_dbus_string_starts_with_c_str (&line, "STRING_ARRAY"))
605             code = DBUS_TYPE_STRING_ARRAY;
606           else
607             {
608               const char *s;
609               _dbus_string_get_const_data (&line, &s);
610               _dbus_warn ("%s is not a valid type name\n", s);
611               goto parse_failed;
612             }
613
614           if (!_dbus_string_append_byte (dest, code))
615             {
616               _dbus_warn ("could not append typecode byte\n");
617               goto parse_failed;
618             }
619         }
620       else if (_dbus_string_starts_with_c_str (&line,
621                                                "INT32"))
622         {
623           SAVE_FOR_UNALIGN (dest, 4);
624           long val;
625           
626           _dbus_string_delete_first_word (&line);
627
628           if (!_dbus_string_parse_int (&line, 0, &val, NULL))
629             {
630               _dbus_warn ("could not parse integer for INT32\n");
631               goto parse_failed;
632             }
633           
634           if (!_dbus_marshal_int32 (dest, endian,
635                                     val))
636             {
637               _dbus_warn ("failed to append INT32\n");
638               goto parse_failed;
639             }
640
641           PERFORM_UNALIGN (dest);
642         }
643       else if (_dbus_string_starts_with_c_str (&line,
644                                                "UINT32"))
645         {
646           SAVE_FOR_UNALIGN (dest, 4);
647           long val;
648           
649           _dbus_string_delete_first_word (&line);
650
651           /* FIXME should have _dbus_string_parse_uint32 */
652           if (!_dbus_string_parse_int (&line, 0, &val, NULL))
653             goto parse_failed;
654           
655           if (!_dbus_marshal_uint32 (dest, endian,
656                                      val))
657             {
658               _dbus_warn ("failed to append UINT32\n");
659               goto parse_failed;
660             }
661
662           PERFORM_UNALIGN (dest);
663         }
664       else if (_dbus_string_starts_with_c_str (&line,
665                                                "DOUBLE"))
666         {
667           SAVE_FOR_UNALIGN (dest, 8);
668           double val;
669           
670           _dbus_string_delete_first_word (&line);
671
672           if (!_dbus_string_parse_double (&line, 0, &val, NULL))
673             goto parse_failed;
674           
675           if (!_dbus_marshal_double (dest, endian,
676                                      val))
677             {
678               _dbus_warn ("failed to append DOUBLE\n");
679               goto parse_failed;
680             }
681
682           PERFORM_UNALIGN (dest);
683         }
684       else if (_dbus_string_starts_with_c_str (&line,
685                                                "STRING"))
686         {
687           SAVE_FOR_UNALIGN (dest, 4);
688           int size_offset;
689           int old_len;
690           
691           _dbus_string_delete_first_word (&line);
692
693           size_offset = _dbus_string_get_length (dest);
694           size_offset = _DBUS_ALIGN_VALUE (size_offset, 4);
695           if (!_dbus_marshal_uint32 (dest, endian, 0))
696             {
697               _dbus_warn ("Failed to append string size\n");
698               goto parse_failed;
699             }
700
701           old_len = _dbus_string_get_length (dest);
702           if (!append_quoted_string (dest, &line))
703             {
704               _dbus_warn ("Failed to append quoted string\n");
705               goto parse_failed;
706             }
707
708           _dbus_marshal_set_uint32 (dest, endian, size_offset,
709                                     /* subtract 1 for nul */
710                                     _dbus_string_get_length (dest) - old_len - 1);
711           
712           PERFORM_UNALIGN (dest);
713         }
714       else
715         goto parse_failed;
716       
717       if (!just_set_unalign && unalign)
718         {
719           _dbus_warn ("UNALIGN prior to something that isn't aligned\n");
720           goto parse_failed;
721         }
722
723       goto next_iteration; /* skip parse_failed */
724       
725     parse_failed:
726       {
727         const char *s;
728         _dbus_string_get_const_data (&line, &s);
729         _dbus_warn ("couldn't process line %d \"%s\"\n",
730                     line_no, s);
731         goto out;
732       }
733     }
734
735   _dbus_hash_iter_init (length_hash, &iter);
736   while (_dbus_hash_iter_next (&iter))
737     {
738       SavedLength *sl = _dbus_hash_iter_get_value (&iter);
739       const char *s;
740
741       _dbus_string_get_const_data (&sl->name, &s);
742       
743       if (sl->length < 0)
744         {
745           _dbus_warn ("Used LENGTH %s but never did END_LENGTH\n",
746                       s);
747           goto out;
748         }
749       else if (sl->offset < 0)
750         {
751           _dbus_warn ("Did END_LENGTH %s but never used LENGTH\n",
752                       s);
753           goto out;
754         }
755       else
756         {
757           if (sl->start < 0)
758             sl->start = 0;
759           
760           _dbus_verbose ("Filling in length %s endian = %d offset = %d start = %d length = %d\n",
761                          s, sl->endian, sl->offset, sl->start, sl->length);
762           _dbus_marshal_set_int32 (dest,
763                                    sl->endian,
764                                    sl->offset,
765                                    sl->length - sl->start);
766         }
767
768       _dbus_hash_iter_remove_entry (&iter);
769     }
770   
771   retval = TRUE;
772   
773  out:
774   if (length_hash != NULL)
775     _dbus_hash_table_unref (length_hash);
776   
777   _dbus_string_free (&file);
778   _dbus_string_free (&line);
779   return retval;
780 }
781
782 /** @} */
783 #endif /* DBUS_BUILD_TESTS */