2005-02-17 Colin Walters <walters@verbum.org>
[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   const char *c_name;
312   InterfaceInfo *iface;
313   NodeInfo *top;
314   
315   if (parser->interface ||
316       parser->method ||
317       parser->signal ||
318       parser->property ||
319       parser->arg ||
320       (parser->node_stack == NULL))
321     {
322       g_set_error (error, G_MARKUP_ERROR,
323                    G_MARKUP_ERROR_PARSE,
324                    _("Can't put <%s> element here"),
325                    element_name);
326       return FALSE;      
327     }
328
329   name = NULL;
330   if (!locate_attributes (element_name, attribute_names,
331                           attribute_values, error,
332                           "name", &name,
333                           "c_name", &c_name,
334                           NULL))
335     return FALSE;
336
337   if (name == NULL)
338     {
339       g_set_error (error, G_MARKUP_ERROR,
340                    G_MARKUP_ERROR_PARSE,
341                    _("\"%s\" attribute required on <%s> element "),
342                    "name", element_name);
343       return FALSE;
344     }
345
346   top = parser->node_stack->data;
347   
348   iface = interface_info_new (name);
349   if (c_name)
350     interface_info_set_binding_name (iface, "C", c_name);
351   node_info_add_interface (top, iface);
352   interface_info_unref (iface);
353
354   parser->interface = iface;
355   
356   return TRUE;
357 }
358
359 static gboolean
360 parse_method (Parser      *parser,
361               const char  *element_name,
362               const char **attribute_names,
363               const char **attribute_values,
364               GError     **error)
365 {
366   const char *name;
367   const char *c_name;
368   MethodInfo *method;
369   NodeInfo *top;
370   
371   if (parser->interface == NULL ||
372       parser->node_stack == NULL ||
373       parser->method ||
374       parser->signal ||
375       parser->property ||
376       parser->arg)
377     {
378       g_set_error (error, G_MARKUP_ERROR,
379                    G_MARKUP_ERROR_PARSE,
380                    _("Can't put <%s> element here"),
381                    element_name);
382       return FALSE;      
383     }
384
385   name = NULL;
386   if (!locate_attributes (element_name, attribute_names,
387                           attribute_values, error,
388                           "name", &name,
389                           "c_name", &c_name,
390                           NULL))
391     return FALSE;
392
393   if (name == NULL)
394     {
395       g_set_error (error, G_MARKUP_ERROR,
396                    G_MARKUP_ERROR_PARSE,
397                    _("\"%s\" attribute required on <%s> element "),
398                    "name", element_name);
399       return FALSE;
400     }
401
402   top = parser->node_stack->data;
403   
404   method = method_info_new (name);
405   if (c_name)
406     method_info_set_binding_name (method, "C", c_name);
407   interface_info_add_method (parser->interface, method);
408   method_info_unref (method);
409
410   parser->method = method;
411   
412   return TRUE;
413 }
414
415 static gboolean
416 parse_signal (Parser      *parser,
417               const char  *element_name,
418               const char **attribute_names,
419               const char **attribute_values,
420               GError     **error)
421 {
422   const char *name;
423   SignalInfo *signal;
424   NodeInfo *top;
425   
426   if (parser->interface == NULL ||
427       parser->node_stack == NULL ||
428       parser->signal ||
429       parser->method ||
430       parser->property ||
431       parser->arg)
432     {
433       g_set_error (error, G_MARKUP_ERROR,
434                    G_MARKUP_ERROR_PARSE,
435                    _("Can't put <%s> element here"),
436                    element_name);
437       return FALSE;      
438     }
439
440   name = NULL;
441   if (!locate_attributes (element_name, attribute_names,
442                           attribute_values, error,
443                           "name", &name,
444                           NULL))
445     return FALSE;
446
447   if (name == NULL)
448     {
449       g_set_error (error, G_MARKUP_ERROR,
450                    G_MARKUP_ERROR_PARSE,
451                    _("\"%s\" attribute required on <%s> element "),
452                    "name", element_name);
453       return FALSE;
454     }
455
456   top = parser->node_stack->data;
457   
458   signal = signal_info_new (name);
459   interface_info_add_signal (parser->interface, signal);
460   signal_info_unref (signal);
461
462   parser->signal = signal;
463   
464   return TRUE;
465 }
466
467 static int
468 basic_type_from_string (const char *str)
469 {
470   if (strcmp (str, "string") == 0)
471     return DBUS_TYPE_STRING;
472   else if (strcmp (str, "int16") == 0)
473     return DBUS_TYPE_INT16;
474   else if (strcmp (str, "uint16") == 0)
475     return DBUS_TYPE_UINT16;
476   else if (strcmp (str, "int32") == 0)
477     return DBUS_TYPE_INT32;
478   else if (strcmp (str, "uint32") == 0)
479     return DBUS_TYPE_UINT32;
480   else if (strcmp (str, "int64") == 0)
481     return DBUS_TYPE_INT64;
482   else if (strcmp (str, "uint64") == 0)
483     return DBUS_TYPE_UINT64;
484   else if (strcmp (str, "double") == 0)
485     return DBUS_TYPE_DOUBLE;
486   else if (strcmp (str, "byte") == 0)
487     return DBUS_TYPE_BYTE;
488   else if (strcmp (str, "boolean") == 0)
489     return DBUS_TYPE_BOOLEAN;
490   else if (strcmp (str, "byte") == 0)
491     return DBUS_TYPE_BYTE;
492   else if (strcmp (str, "object") == 0)
493     return DBUS_TYPE_OBJECT_PATH;
494   else if (strcmp (str, "variant") == 0)
495     return DBUS_TYPE_VARIANT;
496   else
497     return DBUS_TYPE_INVALID;
498 }
499
500 /* FIXME we have to allow type signatures, not just basic types
501  */
502 static int
503 type_from_string (const char *str,
504                   const char *element_name,
505                   GError    **error)
506 {
507   int t;
508   
509   t = basic_type_from_string (str);
510
511   if (t == DBUS_TYPE_INVALID)
512     {
513       g_set_error (error, G_MARKUP_ERROR,
514                    G_MARKUP_ERROR_PARSE,
515                    _("Type \"%s\" not understood on <%s> element "),
516                    str, element_name);
517     }
518
519   return t;
520 }
521
522 static gboolean
523 parse_property (Parser      *parser,
524                 const char  *element_name,
525                 const char **attribute_names,
526                 const char **attribute_values,
527                 GError     **error)
528 {
529   const char *name;
530   const char *access;
531   const char *type;
532   PropertyInfo *property;
533   NodeInfo *top;
534   PropertyAccessFlags access_flags;
535   int t;
536   
537   if (parser->interface == NULL ||
538       parser->node_stack == NULL ||
539       parser->signal ||
540       parser->method ||
541       parser->property ||
542       parser->arg)
543     {
544       g_set_error (error, G_MARKUP_ERROR,
545                    G_MARKUP_ERROR_PARSE,
546                    _("Can't put <%s> element here"),
547                    element_name);
548       return FALSE;      
549     }
550
551   name = NULL;
552   if (!locate_attributes (element_name, attribute_names,
553                           attribute_values, error,
554                           "name", &name,
555                           "access", &access,
556                           "type", &type,
557                           NULL))
558     return FALSE;
559
560   if (name == NULL)
561     {
562       g_set_error (error, G_MARKUP_ERROR,
563                    G_MARKUP_ERROR_PARSE,
564                    _("\"%s\" attribute required on <%s> element "),
565                    "name", element_name);
566       return FALSE;
567     }
568
569   if (access == NULL)
570     {
571       g_set_error (error, G_MARKUP_ERROR,
572                    G_MARKUP_ERROR_PARSE,
573                    _("\"%s\" attribute required on <%s> element "),
574                    "access", element_name);
575       return FALSE;
576     }
577
578   if (type == NULL)
579     {
580       g_set_error (error, G_MARKUP_ERROR,
581                    G_MARKUP_ERROR_PARSE,
582                    _("\"%s\" attribute required on <%s> element "),
583                    "type", element_name);
584       return FALSE;
585     }
586
587   t = type_from_string (type, element_name, error);
588   if (t == DBUS_TYPE_INVALID)
589     return FALSE;
590
591   access_flags = 0;
592   if (strcmp (access, "readwrite") == 0)
593     access_flags = PROPERTY_READ | PROPERTY_WRITE;
594   else if (strcmp (access, "read") == 0)
595     access_flags = PROPERTY_READ;
596   else if (strcmp (access, "write") == 0)
597     access_flags = PROPERTY_WRITE;
598   else
599     {
600       g_set_error (error, G_MARKUP_ERROR,
601                    G_MARKUP_ERROR_PARSE,
602                    _("access=\"%s\" must have value readwrite, read, or write on %s\n"),
603                    access, element_name);
604       return FALSE;
605     }
606   
607   top = parser->node_stack->data;
608   
609   property = property_info_new (name, t, access_flags);
610   interface_info_add_property (parser->interface, property);
611   property_info_unref (property);
612
613   parser->property = property;
614   
615   return TRUE;
616 }
617
618 static gboolean
619 parse_arg (Parser      *parser,
620            const char  *element_name,
621            const char **attribute_names,
622            const char **attribute_values,
623            GError     **error)
624 {
625   const char *name;
626   const char *type;
627   const char *direction;
628   ArgDirection dir;
629   int t;
630   ArgInfo *arg;
631   char *generated_name;
632   
633   if (!(parser->method || parser->signal) ||
634       parser->node_stack == NULL ||
635       parser->property ||
636       parser->arg)
637     {
638       g_set_error (error, G_MARKUP_ERROR,
639                    G_MARKUP_ERROR_PARSE,
640                    _("Can't put <%s> element here"),
641                    element_name);
642       return FALSE;      
643     }
644
645   name = NULL;
646   if (!locate_attributes (element_name, attribute_names,
647                           attribute_values, error,
648                           "name", &name,
649                           "type", &type,
650                           "direction", &direction,
651                           NULL))
652     return FALSE;
653
654   /* name can be null for args */
655   
656   if (type == NULL)
657     {
658       g_set_error (error, G_MARKUP_ERROR,
659                    G_MARKUP_ERROR_PARSE,
660                    _("\"%s\" attribute required on <%s> element "),
661                    "type", element_name);
662       return FALSE;
663     }
664
665   if (direction == NULL)
666     {
667       /* methods default to in, signal to out */
668       if (parser->method)
669         direction = "in";
670       else if (parser->signal)
671         direction = "out";
672       else
673         g_assert_not_reached ();
674     }
675
676   dir = ARG_INVALID;
677   
678   if (strcmp (direction, "in") == 0)
679     dir = ARG_IN;
680   else if (strcmp (direction, "out") == 0)
681     dir = ARG_OUT;
682   
683   if (dir == ARG_INVALID ||
684       (parser->signal && dir == ARG_IN))
685     {
686       if (parser->signal)
687         g_set_error (error, G_MARKUP_ERROR,
688                      G_MARKUP_ERROR_PARSE,
689                      _("Signals must have direction=\"out\" (just omit the direction attribute)"));
690       else
691         g_set_error (error, G_MARKUP_ERROR,
692                      G_MARKUP_ERROR_PARSE,
693                      _("\"%s\" attribute on <%s> has value \"in\" or \"out\""),
694                      "direction", element_name);
695       return FALSE;
696     }
697
698   t = type_from_string (type, element_name, error);
699   if (t == DBUS_TYPE_INVALID)
700     return FALSE;
701
702   generated_name = NULL;
703   if (name == NULL)
704     generated_name = g_strdup_printf ("arg%d",
705                                       parser->method ?
706                                       method_info_get_n_args (parser->method) :
707                                       signal_info_get_n_args (parser->signal));
708                                       
709   
710   arg = arg_info_new (name ? name : generated_name, dir, t);
711   if (parser->method)
712     method_info_add_arg (parser->method, arg);
713   else if (parser->signal)
714     signal_info_add_arg (parser->signal, arg);
715   else
716     g_assert_not_reached ();
717
718   g_free (generated_name);
719   
720   arg_info_unref (arg);
721
722   parser->arg = arg;
723   
724   return TRUE;
725 }
726
727 gboolean
728 parser_start_element (Parser      *parser,
729                       const char  *element_name,
730                       const char **attribute_names,
731                       const char **attribute_values,
732                       GError     **error)
733 {
734   g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
735
736   if (ELEMENT_IS ("node"))
737     {
738       if (!parse_node (parser, element_name, attribute_names,
739                        attribute_values, error))
740         return FALSE;
741     }
742   else if (ELEMENT_IS ("interface"))
743     {
744       if (!parse_interface (parser, element_name, attribute_names,
745                             attribute_values, error))
746         return FALSE;
747     }
748   else if (ELEMENT_IS ("method"))
749     {
750       if (!parse_method (parser, element_name, attribute_names,
751                          attribute_values, error))
752         return FALSE;
753     }
754   else if (ELEMENT_IS ("signal"))
755     {
756       if (!parse_signal (parser, element_name, attribute_names,
757                          attribute_values, error))
758         return FALSE;
759     }
760   else if (ELEMENT_IS ("property"))
761     {
762       if (!parse_property (parser, element_name, attribute_names,
763                            attribute_values, error))
764         return FALSE;
765     }
766   else if (ELEMENT_IS ("arg"))
767     {
768       if (!parse_arg (parser, element_name, attribute_names,
769                       attribute_values, error))
770         return FALSE;
771     }
772   else
773     {
774       g_set_error (error, G_MARKUP_ERROR,
775                    G_MARKUP_ERROR_PARSE,
776                    _("Element <%s> not recognized"),
777                    element_name);
778     }
779   
780   return TRUE;
781 }
782
783 gboolean
784 parser_end_element (Parser      *parser,
785                     const char  *element_name,
786                     GError     **error)
787 {
788   g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
789
790   if (ELEMENT_IS ("interface"))
791     {
792       parser->interface = NULL;
793     }
794   else if (ELEMENT_IS ("method"))
795     {
796       parser->method = NULL;
797     }
798   else if (ELEMENT_IS ("signal"))
799     {
800       parser->signal = NULL;
801     }
802   else if (ELEMENT_IS ("property"))
803     {
804       parser->property = NULL;
805     }
806   else if (ELEMENT_IS ("arg"))
807     {
808       parser->arg = NULL;
809     }
810   else if (ELEMENT_IS ("node"))
811     {
812       NodeInfo *top;
813
814       g_assert (parser->node_stack != NULL);
815       top = parser->node_stack->data;
816
817       parser->node_stack = g_slist_remove (parser->node_stack,
818                                            top);
819
820       if (parser->node_stack == NULL)
821         parser->result = top; /* We are done, store the result */      
822     }
823   else
824     g_assert_not_reached (); /* should have had an error on start_element */
825   
826   return TRUE;
827 }
828
829 gboolean
830 parser_content (Parser      *parser,
831                 const char  *content,
832                 int          len,
833                 GError     **error)
834 {
835   g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
836
837   /* FIXME check that it's all whitespace */
838   
839   return TRUE;
840 }
841
842 gboolean
843 parser_finished (Parser      *parser,
844                  GError     **error)
845 {
846   g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
847
848   return TRUE;
849 }
850
851 NodeInfo*
852 parser_get_nodes (Parser *parser)
853 {
854   return parser->result;
855 }
856
857 #endif /* DOXYGEN_SHOULD_SKIP_THIS */