2005-01-29 Havoc Pennington <hp@redhat.com>
[platform/upstream/dbus.git] / glib / dbus-gparser.c
1 /* -*- mode: C; c-file-style: "gnu" -*- */
2 /* dbus-gparser.c parse DBus description files
3  *
4  * Copyright (C) 2003, 2005  Red Hat, Inc.
5  *
6  * Licensed under the Academic Free License version 2.1
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 "dbus-gparser.h"
24 #include "dbus-gidl.h"
25 #include <string.h>
26
27 #include <libintl.h>
28 #define _(x) gettext ((x))
29 #define N_(x) x
30
31 #ifndef DOXYGEN_SHOULD_SKIP_THIS
32
33 #define ELEMENT_IS(name) (strcmp (element_name, (name)) == 0)
34
35 typedef struct
36 {
37   const char  *name;
38   const char **retloc;
39 } LocateAttr;
40
41 static gboolean
42 locate_attributes (const char  *element_name,
43                    const char **attribute_names,
44                    const char **attribute_values,
45                    GError     **error,
46                    const char  *first_attribute_name,
47                    const char **first_attribute_retloc,
48                    ...)
49 {
50   va_list args;
51   const char *name;
52   const char **retloc;
53   int n_attrs;
54 #define MAX_ATTRS 24
55   LocateAttr attrs[MAX_ATTRS];
56   gboolean retval;
57   int i;
58
59   g_return_val_if_fail (first_attribute_name != NULL, FALSE);
60   g_return_val_if_fail (first_attribute_retloc != NULL, FALSE);
61
62   retval = TRUE;
63
64   n_attrs = 1;
65   attrs[0].name = first_attribute_name;
66   attrs[0].retloc = first_attribute_retloc;
67   *first_attribute_retloc = NULL;
68   
69   va_start (args, first_attribute_retloc);
70
71   name = va_arg (args, const char*);
72   retloc = va_arg (args, const char**);
73
74   while (name != NULL)
75     {
76       g_return_val_if_fail (retloc != NULL, FALSE);
77
78       g_assert (n_attrs < MAX_ATTRS);
79       
80       attrs[n_attrs].name = name;
81       attrs[n_attrs].retloc = retloc;
82       n_attrs += 1;
83       *retloc = NULL;      
84
85       name = va_arg (args, const char*);
86       retloc = va_arg (args, const char**);
87     }
88
89   va_end (args);
90
91   if (!retval)
92     return retval;
93
94   i = 0;
95   while (attribute_names[i])
96     {
97       int j;
98       gboolean found;
99
100       found = FALSE;
101       j = 0;
102       while (j < n_attrs)
103         {
104           if (strcmp (attrs[j].name, attribute_names[i]) == 0)
105             {
106               retloc = attrs[j].retloc;
107
108               if (*retloc != NULL)
109                 {
110                   g_set_error (error,
111                                G_MARKUP_ERROR,
112                                G_MARKUP_ERROR_PARSE,
113                                _("Attribute \"%s\" repeated twice on the same <%s> element"),
114                                attrs[j].name, element_name);
115                   retval = FALSE;
116                   goto out;
117                 }
118
119               *retloc = attribute_values[i];
120               found = TRUE;
121             }
122
123           ++j;
124         }
125
126       if (!found)
127         {
128           g_set_error (error,
129                        G_MARKUP_ERROR,
130                        G_MARKUP_ERROR_PARSE,
131                        _("Attribute \"%s\" is invalid on <%s> element in this context"),
132                        attribute_names[i], element_name);
133           retval = FALSE;
134           goto out;
135         }
136
137       ++i;
138     }
139
140  out:
141   return retval;
142 }
143
144 static gboolean
145 check_no_attributes (const char  *element_name,
146                      const char **attribute_names,
147                      const char **attribute_values,
148                      GError     **error)
149 {
150   if (attribute_names[0] != NULL)
151     {
152       g_set_error (error,
153                    G_MARKUP_ERROR,
154                    G_MARKUP_ERROR_PARSE,
155                    _("Attribute \"%s\" is invalid on <%s> element in this context"),
156                    attribute_names[0], element_name);
157       return FALSE;
158     }
159
160   return TRUE;
161 }
162
163 struct Parser
164 {
165   int refcount;
166
167   NodeInfo *result; /* Filled in when we pop the last node */
168   GSList *node_stack;
169   InterfaceInfo *interface;
170   MethodInfo *method;
171   SignalInfo *signal;
172   PropertyInfo *property;
173   ArgInfo *arg;
174 };
175
176 Parser*
177 parser_new (void)
178 {
179   Parser *parser;
180
181   parser = g_new0 (Parser, 1);
182
183   parser->refcount = 1;
184
185   return parser;
186 }
187
188 Parser *
189 parser_ref (Parser *parser)
190 {
191   parser->refcount += 1;
192
193   return parser;
194 }
195
196 void
197 parser_unref (Parser *parser)
198 {
199   parser->refcount -= 1;
200   if (parser->refcount == 0)
201     {
202       if (parser->result)
203         node_info_unref (parser->result);
204
205       g_free (parser);
206     }
207 }
208
209 gboolean
210 parser_check_doctype (Parser      *parser,
211                       const char  *doctype,
212                       GError     **error)
213 {
214   g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
215   
216   if (strcmp (doctype, "node") != 0)
217     {
218       g_set_error (error,
219                    G_MARKUP_ERROR,
220                    G_MARKUP_ERROR_PARSE,
221                    "D-BUS description file has the wrong document type %s, use node or interface",
222                    doctype);
223       return FALSE;
224     }
225   else
226     return TRUE;
227 }
228
229 static gboolean
230 parse_node (Parser      *parser,
231             const char  *element_name,
232             const char **attribute_names,
233             const char **attribute_values,
234             GError     **error)
235 {
236   const char *name;
237   NodeInfo *node;
238   
239   if (parser->interface ||
240       parser->method ||
241       parser->signal ||
242       parser->property ||
243       parser->arg)
244     {
245       g_set_error (error, G_MARKUP_ERROR,
246                    G_MARKUP_ERROR_PARSE,
247                    _("Can't put <%s> element here"),
248                    element_name);
249       return FALSE;      
250     }
251
252   name = NULL;
253   if (!locate_attributes (element_name, attribute_names,
254                           attribute_values, error,
255                           "name", &name,
256                           NULL))
257     return FALSE;
258
259   /* Only the root node can have no name */
260   if (parser->node_stack != NULL && name == NULL)
261     {
262       g_set_error (error, G_MARKUP_ERROR,
263                    G_MARKUP_ERROR_PARSE,
264                    _("\"%s\" attribute required on <%s> element "),
265                    "name", element_name);
266       return FALSE;
267     }
268
269   /* Root element name must be absolute */
270   if (parser->node_stack == NULL && name && *name != '/')
271     {
272       g_set_error (error, G_MARKUP_ERROR,
273                    G_MARKUP_ERROR_PARSE,
274                    _("\"%s\" attribute on <%s> element must be an absolute object path, \"%s\" not OK"),
275                    "name", element_name, name);
276       return FALSE;
277     }
278
279   /* Other element names must not be absolute */
280   if (parser->node_stack != NULL && name && *name == '/')
281     {
282       g_set_error (error, G_MARKUP_ERROR,
283                    G_MARKUP_ERROR_PARSE,
284                    _("\"%s\" attribute on <%s> element must not be an absolute object path, \"%s\" starts with /"),
285                    "name", element_name, name);
286       return FALSE;
287     }
288   
289   node = node_info_new (name);
290
291   if (parser->node_stack != NULL)
292     {
293       node_info_add_node (parser->node_stack->data,
294                           node);
295     }
296   
297   parser->node_stack = g_slist_prepend (parser->node_stack,
298                                         node);
299   
300   return TRUE;
301 }
302
303 static gboolean
304 parse_interface (Parser      *parser,
305                  const char  *element_name,
306                  const char **attribute_names,
307                  const char **attribute_values,
308                  GError     **error)
309 {
310   const char *name;
311   InterfaceInfo *iface;
312   NodeInfo *top;
313   
314   if (parser->interface ||
315       parser->method ||
316       parser->signal ||
317       parser->property ||
318       parser->arg ||
319       (parser->node_stack == NULL))
320     {
321       g_set_error (error, G_MARKUP_ERROR,
322                    G_MARKUP_ERROR_PARSE,
323                    _("Can't put <%s> element here"),
324                    element_name);
325       return FALSE;      
326     }
327
328   name = NULL;
329   if (!locate_attributes (element_name, attribute_names,
330                           attribute_values, error,
331                           "name", &name,
332                           NULL))
333     return FALSE;
334
335   if (name == NULL)
336     {
337       g_set_error (error, G_MARKUP_ERROR,
338                    G_MARKUP_ERROR_PARSE,
339                    _("\"%s\" attribute required on <%s> element "),
340                    "name", element_name);
341       return FALSE;
342     }
343
344   top = parser->node_stack->data;
345   
346   iface = interface_info_new (name);
347   node_info_add_interface (top, iface);
348   interface_info_unref (iface);
349
350   parser->interface = iface;
351   
352   return TRUE;
353 }
354
355 static gboolean
356 parse_method (Parser      *parser,
357               const char  *element_name,
358               const char **attribute_names,
359               const char **attribute_values,
360               GError     **error)
361 {
362   const char *name;
363   MethodInfo *method;
364   NodeInfo *top;
365   
366   if (parser->interface == NULL ||
367       parser->node_stack == NULL ||
368       parser->method ||
369       parser->signal ||
370       parser->property ||
371       parser->arg)
372     {
373       g_set_error (error, G_MARKUP_ERROR,
374                    G_MARKUP_ERROR_PARSE,
375                    _("Can't put <%s> element here"),
376                    element_name);
377       return FALSE;      
378     }
379
380   name = NULL;
381   if (!locate_attributes (element_name, attribute_names,
382                           attribute_values, error,
383                           "name", &name,
384                           NULL))
385     return FALSE;
386
387   if (name == NULL)
388     {
389       g_set_error (error, G_MARKUP_ERROR,
390                    G_MARKUP_ERROR_PARSE,
391                    _("\"%s\" attribute required on <%s> element "),
392                    "name", element_name);
393       return FALSE;
394     }
395
396   top = parser->node_stack->data;
397   
398   method = method_info_new (name);
399   interface_info_add_method (parser->interface, method);
400   method_info_unref (method);
401
402   parser->method = method;
403   
404   return TRUE;
405 }
406
407 static gboolean
408 parse_signal (Parser      *parser,
409               const char  *element_name,
410               const char **attribute_names,
411               const char **attribute_values,
412               GError     **error)
413 {
414   const char *name;
415   SignalInfo *signal;
416   NodeInfo *top;
417   
418   if (parser->interface == NULL ||
419       parser->node_stack == NULL ||
420       parser->signal ||
421       parser->method ||
422       parser->property ||
423       parser->arg)
424     {
425       g_set_error (error, G_MARKUP_ERROR,
426                    G_MARKUP_ERROR_PARSE,
427                    _("Can't put <%s> element here"),
428                    element_name);
429       return FALSE;      
430     }
431
432   name = NULL;
433   if (!locate_attributes (element_name, attribute_names,
434                           attribute_values, error,
435                           "name", &name,
436                           NULL))
437     return FALSE;
438
439   if (name == NULL)
440     {
441       g_set_error (error, G_MARKUP_ERROR,
442                    G_MARKUP_ERROR_PARSE,
443                    _("\"%s\" attribute required on <%s> element "),
444                    "name", element_name);
445       return FALSE;
446     }
447
448   top = parser->node_stack->data;
449   
450   signal = signal_info_new (name);
451   interface_info_add_signal (parser->interface, signal);
452   signal_info_unref (signal);
453
454   parser->signal = signal;
455   
456   return TRUE;
457 }
458
459 static int
460 basic_type_from_string (const char *str)
461 {
462   if (strcmp (str, "string") == 0)
463     return DBUS_TYPE_STRING;
464   else if (strcmp (str, "int16") == 0)
465     return DBUS_TYPE_INT16;
466   else if (strcmp (str, "uint16") == 0)
467     return DBUS_TYPE_UINT16;
468   else if (strcmp (str, "int32") == 0)
469     return DBUS_TYPE_INT32;
470   else if (strcmp (str, "uint32") == 0)
471     return DBUS_TYPE_UINT32;
472   else if (strcmp (str, "int64") == 0)
473     return DBUS_TYPE_INT64;
474   else if (strcmp (str, "uint64") == 0)
475     return DBUS_TYPE_UINT64;
476   else if (strcmp (str, "double") == 0)
477     return DBUS_TYPE_DOUBLE;
478   else if (strcmp (str, "byte") == 0)
479     return DBUS_TYPE_BYTE;
480   else if (strcmp (str, "boolean") == 0)
481     return DBUS_TYPE_BOOLEAN;
482   else if (strcmp (str, "byte") == 0)
483     return DBUS_TYPE_BYTE;
484   else if (strcmp (str, "object") == 0)
485     return DBUS_TYPE_OBJECT_PATH;
486   else
487     return DBUS_TYPE_INVALID;
488 }
489
490 /* FIXME we have to allow type signatures, not just basic types
491  */
492 static int
493 type_from_string (const char *str,
494                   const char *element_name,
495                   GError    **error)
496 {
497   int t;
498   
499   t = basic_type_from_string (str);
500
501   if (t == DBUS_TYPE_INVALID)
502     {
503       g_set_error (error, G_MARKUP_ERROR,
504                    G_MARKUP_ERROR_PARSE,
505                    _("Type \"%s\" not understood on <%s> element "),
506                    str, element_name);
507     }
508
509   return t;
510 }
511
512 static gboolean
513 parse_property (Parser      *parser,
514                 const char  *element_name,
515                 const char **attribute_names,
516                 const char **attribute_values,
517                 GError     **error)
518 {
519   const char *name;
520   const char *access;
521   const char *type;
522   PropertyInfo *property;
523   NodeInfo *top;
524   PropertyAccessFlags access_flags;
525   int t;
526   
527   if (parser->interface == NULL ||
528       parser->node_stack == NULL ||
529       parser->signal ||
530       parser->method ||
531       parser->property ||
532       parser->arg)
533     {
534       g_set_error (error, G_MARKUP_ERROR,
535                    G_MARKUP_ERROR_PARSE,
536                    _("Can't put <%s> element here"),
537                    element_name);
538       return FALSE;      
539     }
540
541   name = NULL;
542   if (!locate_attributes (element_name, attribute_names,
543                           attribute_values, error,
544                           "name", &name,
545                           "access", &access,
546                           "type", &type,
547                           NULL))
548     return FALSE;
549
550   if (name == NULL)
551     {
552       g_set_error (error, G_MARKUP_ERROR,
553                    G_MARKUP_ERROR_PARSE,
554                    _("\"%s\" attribute required on <%s> element "),
555                    "name", element_name);
556       return FALSE;
557     }
558
559   if (access == NULL)
560     {
561       g_set_error (error, G_MARKUP_ERROR,
562                    G_MARKUP_ERROR_PARSE,
563                    _("\"%s\" attribute required on <%s> element "),
564                    "access", element_name);
565       return FALSE;
566     }
567
568   if (type == NULL)
569     {
570       g_set_error (error, G_MARKUP_ERROR,
571                    G_MARKUP_ERROR_PARSE,
572                    _("\"%s\" attribute required on <%s> element "),
573                    "type", element_name);
574       return FALSE;
575     }
576
577   t = type_from_string (type, element_name, error);
578   if (t == DBUS_TYPE_INVALID)
579     return FALSE;
580
581   access_flags = 0;
582   if (strcmp (access, "readwrite") == 0)
583     access_flags = PROPERTY_READ | PROPERTY_WRITE;
584   else if (strcmp (access, "read") == 0)
585     access_flags = PROPERTY_READ;
586   else if (strcmp (access, "write") == 0)
587     access_flags = PROPERTY_WRITE;
588   else
589     {
590       g_set_error (error, G_MARKUP_ERROR,
591                    G_MARKUP_ERROR_PARSE,
592                    _("access=\"%s\" must have value readwrite, read, or write on %s\n"),
593                    access, element_name);
594       return FALSE;
595     }
596   
597   top = parser->node_stack->data;
598   
599   property = property_info_new (name, t, access_flags);
600   interface_info_add_property (parser->interface, property);
601   property_info_unref (property);
602
603   parser->property = property;
604   
605   return TRUE;
606 }
607
608 static gboolean
609 parse_arg (Parser      *parser,
610            const char  *element_name,
611            const char **attribute_names,
612            const char **attribute_values,
613            GError     **error)
614 {
615   const char *name;
616   const char *type;
617   const char *direction;
618   ArgDirection dir;
619   int t;
620   ArgInfo *arg;
621   
622   if (!(parser->method || parser->signal) ||
623       parser->node_stack == NULL ||
624       parser->property ||
625       parser->arg)
626     {
627       g_set_error (error, G_MARKUP_ERROR,
628                    G_MARKUP_ERROR_PARSE,
629                    _("Can't put <%s> element here"),
630                    element_name);
631       return FALSE;      
632     }
633
634   name = NULL;
635   if (!locate_attributes (element_name, attribute_names,
636                           attribute_values, error,
637                           "name", &name,
638                           "type", &type,
639                           "direction", &direction,
640                           NULL))
641     return FALSE;
642
643   /* name can be null for args */
644   
645   if (type == NULL)
646     {
647       g_set_error (error, G_MARKUP_ERROR,
648                    G_MARKUP_ERROR_PARSE,
649                    _("\"%s\" attribute required on <%s> element "),
650                    "type", element_name);
651       return FALSE;
652     }
653
654   if (direction == NULL)
655     {
656       /* methods default to in, signal to out */
657       if (parser->method)
658         direction = "in";
659       else if (parser->signal)
660         direction = "out";
661       else
662         g_assert_not_reached ();
663     }
664
665   dir = ARG_INVALID;
666   
667   if (strcmp (direction, "in") == 0)
668     dir = ARG_IN;
669   else if (strcmp (direction, "out") == 0)
670     dir = ARG_OUT;
671   
672   if (dir == ARG_INVALID ||
673       (parser->signal && dir == ARG_IN))
674     {
675       if (parser->signal)
676         g_set_error (error, G_MARKUP_ERROR,
677                      G_MARKUP_ERROR_PARSE,
678                      _("Signals must have direction=\"out\" (just omit the direction attribute)"));
679       else
680         g_set_error (error, G_MARKUP_ERROR,
681                      G_MARKUP_ERROR_PARSE,
682                      _("\"%s\" attribute on <%s> has value \"in\" or \"out\""),
683                      "direction", element_name);
684       return FALSE;
685     }
686
687   t = type_from_string (type, element_name, error);
688   if (t == DBUS_TYPE_INVALID)
689     return FALSE;
690   
691   arg = arg_info_new (name, dir, t);
692   if (parser->method)
693     method_info_add_arg (parser->method, arg);
694   else if (parser->signal)
695     signal_info_add_arg (parser->signal, arg);
696   else
697     g_assert_not_reached ();
698
699   arg_info_unref (arg);
700
701   parser->arg = arg;
702   
703   return TRUE;
704 }
705
706 gboolean
707 parser_start_element (Parser      *parser,
708                       const char  *element_name,
709                       const char **attribute_names,
710                       const char **attribute_values,
711                       GError     **error)
712 {
713   g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
714
715   if (ELEMENT_IS ("node"))
716     {
717       if (!parse_node (parser, element_name, attribute_names,
718                        attribute_values, error))
719         return FALSE;
720     }
721   else if (ELEMENT_IS ("interface"))
722     {
723       if (!parse_interface (parser, element_name, attribute_names,
724                             attribute_values, error))
725         return FALSE;
726     }
727   else if (ELEMENT_IS ("method"))
728     {
729       if (!parse_method (parser, element_name, attribute_names,
730                          attribute_values, error))
731         return FALSE;
732     }
733   else if (ELEMENT_IS ("signal"))
734     {
735       if (!parse_signal (parser, element_name, attribute_names,
736                          attribute_values, error))
737         return FALSE;
738     }
739   else if (ELEMENT_IS ("property"))
740     {
741       if (!parse_property (parser, element_name, attribute_names,
742                            attribute_values, error))
743         return FALSE;
744     }
745   else if (ELEMENT_IS ("arg"))
746     {
747       if (!parse_arg (parser, element_name, attribute_names,
748                       attribute_values, error))
749         return FALSE;
750     }
751   else
752     {
753       g_set_error (error, G_MARKUP_ERROR,
754                    G_MARKUP_ERROR_PARSE,
755                    _("Element <%s> not recognized"),
756                    element_name);
757     }
758   
759   return TRUE;
760 }
761
762 gboolean
763 parser_end_element (Parser      *parser,
764                     const char  *element_name,
765                     GError     **error)
766 {
767   g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
768
769   if (ELEMENT_IS ("interface"))
770     {
771       parser->interface = NULL;
772     }
773   else if (ELEMENT_IS ("method"))
774     {
775       parser->method = NULL;
776     }
777   else if (ELEMENT_IS ("signal"))
778     {
779       parser->signal = NULL;
780     }
781   else if (ELEMENT_IS ("property"))
782     {
783       parser->property = NULL;
784     }
785   else if (ELEMENT_IS ("arg"))
786     {
787       parser->arg = NULL;
788     }
789   else if (ELEMENT_IS ("node"))
790     {
791       NodeInfo *top;
792
793       g_assert (parser->node_stack != NULL);
794       top = parser->node_stack->data;
795
796       parser->node_stack = g_slist_remove (parser->node_stack,
797                                            top);
798
799       if (parser->node_stack == NULL)
800         parser->result = top; /* We are done, store the result */      
801     }
802   else
803     g_assert_not_reached (); /* should have had an error on start_element */
804   
805   return TRUE;
806 }
807
808 gboolean
809 parser_content (Parser      *parser,
810                 const char  *content,
811                 int          len,
812                 GError     **error)
813 {
814   g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
815
816   /* FIXME check that it's all whitespace */
817   
818   return TRUE;
819 }
820
821 gboolean
822 parser_finished (Parser      *parser,
823                  GError     **error)
824 {
825   g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
826
827   return TRUE;
828 }
829
830 NodeInfo*
831 parser_get_nodes (Parser *parser)
832 {
833   return parser->result;
834 }
835
836 #endif /* DOXYGEN_SHOULD_SKIP_THIS */