2004-05-31 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  Red Hat, Inc.
5  *
6  * Licensed under the Academic Free License version 2.0
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   ArgInfo *arg;
173 };
174
175 Parser*
176 parser_new (void)
177 {
178   Parser *parser;
179
180   parser = g_new0 (Parser, 1);
181
182   parser->refcount = 1;
183
184   return parser;
185 }
186
187 Parser *
188 parser_ref (Parser *parser)
189 {
190   parser->refcount += 1;
191
192   return parser;
193 }
194
195 void
196 parser_unref (Parser *parser)
197 {
198   parser->refcount -= 1;
199   if (parser->refcount == 0)
200     {
201       if (parser->result)
202         node_info_unref (parser->result);
203
204       g_free (parser);
205     }
206 }
207
208 gboolean
209 parser_check_doctype (Parser      *parser,
210                       const char  *doctype,
211                       GError     **error)
212 {
213   g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
214   
215   if (strcmp (doctype, "node") != 0)
216     {
217       g_set_error (error,
218                    G_MARKUP_ERROR,
219                    G_MARKUP_ERROR_PARSE,
220                    "D-BUS description file has the wrong document type %s, use node or interface",
221                    doctype);
222       return FALSE;
223     }
224   else
225     return TRUE;
226 }
227
228 static gboolean
229 parse_node (Parser      *parser,
230             const char  *element_name,
231             const char **attribute_names,
232             const char **attribute_values,
233             GError     **error)
234 {
235   const char *name;
236   NodeInfo *node;
237   
238   if (parser->interface ||
239       parser->method ||
240       parser->signal ||
241       parser->arg)
242     {
243       g_set_error (error, G_MARKUP_ERROR,
244                    G_MARKUP_ERROR_PARSE,
245                    _("Can't put a <%s> element here"),
246                    element_name);
247       return FALSE;      
248     }
249
250   name = NULL;
251   if (!locate_attributes (element_name, attribute_names,
252                           attribute_values, error,
253                           "name", &name,
254                           NULL))
255     return FALSE;
256
257   /* Only the root node can have no name */
258   if (parser->node_stack != NULL && name == NULL)
259     {
260       g_set_error (error, G_MARKUP_ERROR,
261                    G_MARKUP_ERROR_PARSE,
262                    _("\"%s\" attribute required on <%s> element "),
263                    "name", element_name);
264       return FALSE;
265     }
266
267   
268   node = node_info_new (name);
269
270   if (parser->node_stack != NULL)
271     {
272       node_info_add_node (parser->node_stack->data,
273                           node);
274     }
275   
276   parser->node_stack = g_slist_prepend (parser->node_stack,
277                                         node);
278   
279   return TRUE;
280 }
281
282 static gboolean
283 parse_interface (Parser      *parser,
284                  const char  *element_name,
285                  const char **attribute_names,
286                  const char **attribute_values,
287                  GError     **error)
288 {
289   const char *name;
290   InterfaceInfo *iface;
291   NodeInfo *top;
292   
293   if (parser->interface ||
294       parser->method ||
295       parser->signal ||
296       parser->arg ||
297       (parser->node_stack == NULL))
298     {
299       g_set_error (error, G_MARKUP_ERROR,
300                    G_MARKUP_ERROR_PARSE,
301                    _("Can't put a <%s> element here"),
302                    element_name);
303       return FALSE;      
304     }
305
306   name = NULL;
307   if (!locate_attributes (element_name, attribute_names,
308                           attribute_values, error,
309                           "name", &name,
310                           NULL))
311     return FALSE;
312
313   if (name == NULL)
314     {
315       g_set_error (error, G_MARKUP_ERROR,
316                    G_MARKUP_ERROR_PARSE,
317                    _("\"%s\" attribute required on <%s> element "),
318                    "name", element_name);
319       return FALSE;
320     }
321
322   top = parser->node_stack->data;
323   
324   iface = interface_info_new (name);
325   node_info_add_interface (top, iface);
326   interface_info_unref (iface);
327
328   parser->interface = iface;
329   
330   return TRUE;
331 }
332
333 static gboolean
334 parse_method (Parser      *parser,
335               const char  *element_name,
336               const char **attribute_names,
337               const char **attribute_values,
338               GError     **error)
339 {
340   const char *name;
341   MethodInfo *method;
342   NodeInfo *top;
343   
344   if (parser->interface == NULL ||
345       parser->node_stack == NULL ||
346       parser->method ||
347       parser->signal ||
348       parser->arg)
349     {
350       g_set_error (error, G_MARKUP_ERROR,
351                    G_MARKUP_ERROR_PARSE,
352                    _("Can't put a <%s> element here"),
353                    element_name);
354       return FALSE;      
355     }
356
357   name = NULL;
358   if (!locate_attributes (element_name, attribute_names,
359                           attribute_values, error,
360                           "name", &name,
361                           NULL))
362     return FALSE;
363
364   if (name == NULL)
365     {
366       g_set_error (error, G_MARKUP_ERROR,
367                    G_MARKUP_ERROR_PARSE,
368                    _("\"%s\" attribute required on <%s> element "),
369                    "name", element_name);
370       return FALSE;
371     }
372
373   top = parser->node_stack->data;
374   
375   method = method_info_new (name);
376   interface_info_add_method (parser->interface, method);
377   method_info_unref (method);
378
379   parser->method = method;
380   
381   return TRUE;
382 }
383
384 static gboolean
385 parse_signal (Parser      *parser,
386               const char  *element_name,
387               const char **attribute_names,
388               const char **attribute_values,
389               GError     **error)
390 {
391   const char *name;
392   SignalInfo *signal;
393   NodeInfo *top;
394   
395   if (parser->interface == NULL ||
396       parser->node_stack == NULL ||
397       parser->signal ||
398       parser->signal ||
399       parser->arg)
400     {
401       g_set_error (error, G_MARKUP_ERROR,
402                    G_MARKUP_ERROR_PARSE,
403                    _("Can't put a <%s> element here"),
404                    element_name);
405       return FALSE;      
406     }
407
408   name = NULL;
409   if (!locate_attributes (element_name, attribute_names,
410                           attribute_values, error,
411                           "name", &name,
412                           NULL))
413     return FALSE;
414
415   if (name == NULL)
416     {
417       g_set_error (error, G_MARKUP_ERROR,
418                    G_MARKUP_ERROR_PARSE,
419                    _("\"%s\" attribute required on <%s> element "),
420                    "name", element_name);
421       return FALSE;
422     }
423
424   top = parser->node_stack->data;
425   
426   signal = signal_info_new (name);
427   interface_info_add_signal (parser->interface, signal);
428   signal_info_unref (signal);
429
430   parser->signal = signal;
431   
432   return TRUE;
433 }
434
435 static int
436 basic_type_from_string (const char *str)
437 {
438   if (strcmp (str, "string") == 0)
439     return DBUS_TYPE_STRING;
440   else if (strcmp (str, "int32") == 0)
441     return DBUS_TYPE_INT32;
442   else if (strcmp (str, "uint32") == 0)
443     return DBUS_TYPE_UINT32;
444   else if (strcmp (str, "int64") == 0)
445     return DBUS_TYPE_INT64;
446   else if (strcmp (str, "uint64") == 0)
447     return DBUS_TYPE_UINT64;
448   else if (strcmp (str, "double") == 0)
449     return DBUS_TYPE_DOUBLE;
450   else if (strcmp (str, "byte") == 0)
451     return DBUS_TYPE_BYTE;
452   else if (strcmp (str, "boolean") == 0)
453     return DBUS_TYPE_BOOLEAN;
454   else if (strcmp (str, "byte") == 0)
455     return DBUS_TYPE_BYTE;
456   else if (strcmp (str, "object") == 0)
457     return DBUS_TYPE_OBJECT_PATH;
458   else
459     return DBUS_TYPE_INVALID;
460 }
461
462 static int
463 type_from_string (const char *str)
464 {
465   return basic_type_from_string (str);
466 }
467
468 static gboolean
469 parse_arg (Parser      *parser,
470            const char  *element_name,
471            const char **attribute_names,
472            const char **attribute_values,
473            GError     **error)
474 {
475   const char *name;
476   const char *type;
477   const char *direction;
478   ArgDirection dir;
479   int t;
480   ArgInfo *arg;
481   
482   if (!(parser->method || parser->signal) ||
483       parser->node_stack == NULL ||
484       parser->arg)
485     {
486       g_set_error (error, G_MARKUP_ERROR,
487                    G_MARKUP_ERROR_PARSE,
488                    _("Can't put a <%s> element here"),
489                    element_name);
490       return FALSE;      
491     }
492
493   name = NULL;
494   if (!locate_attributes (element_name, attribute_names,
495                           attribute_values, error,
496                           "name", &name,
497                           "type", &type,
498                           "direction", &direction,
499                           NULL))
500     return FALSE;
501
502   /* name can be null for args */
503   
504   if (type == NULL)
505     {
506       g_set_error (error, G_MARKUP_ERROR,
507                    G_MARKUP_ERROR_PARSE,
508                    _("\"%s\" attribute required on <%s> element "),
509                    "type", element_name);
510       return FALSE;
511     }
512
513   if (direction == NULL)
514     {
515       /* methods default to in, signal to out */
516       if (parser->method)
517         direction = "in";
518       else if (parser->signal)
519         direction = "out";
520       else
521         g_assert_not_reached ();
522     }
523
524   if (strcmp (direction, "in") == 0)
525     dir = ARG_IN;
526   else if (strcmp (direction, "out") == 0)
527     dir = ARG_OUT;
528   else
529     {
530       g_set_error (error, G_MARKUP_ERROR,
531                    G_MARKUP_ERROR_PARSE,
532                    _("\"%s\" attribute on <%s> has value \"in\" or \"out\""),
533                    "direction", element_name);
534       return FALSE;
535     }
536
537   t = type_from_string (type);
538   
539   arg = arg_info_new (name, dir, t);
540   if (parser->method)
541     method_info_add_arg (parser->method, arg);
542   else if (parser->signal)
543     signal_info_add_arg (parser->signal, arg);
544   else
545     g_assert_not_reached ();
546
547   arg_info_unref (arg);
548
549   parser->arg = arg;
550   
551   return TRUE;
552 }
553
554 gboolean
555 parser_start_element (Parser      *parser,
556                       const char  *element_name,
557                       const char **attribute_names,
558                       const char **attribute_values,
559                       GError     **error)
560 {
561   g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
562
563   if (ELEMENT_IS ("node"))
564     {
565       if (!parse_node (parser, element_name, attribute_names,
566                        attribute_values, error))
567         return FALSE;
568     }
569   else if (ELEMENT_IS ("interface"))
570     {
571       if (!parse_interface (parser, element_name, attribute_names,
572                             attribute_values, error))
573         return FALSE;
574     }
575   else if (ELEMENT_IS ("method"))
576     {
577       if (!parse_method (parser, element_name, attribute_names,
578                          attribute_values, error))
579         return FALSE;
580     }
581   else if (ELEMENT_IS ("signal"))
582     {
583       if (!parse_signal (parser, element_name, attribute_names,
584                          attribute_values, error))
585         return FALSE;
586     }
587   else if (ELEMENT_IS ("arg"))
588     {
589       if (!parse_arg (parser, element_name, attribute_names,
590                       attribute_values, error))
591         return FALSE;
592     }
593   else
594     {
595       g_set_error (error, G_MARKUP_ERROR,
596                    G_MARKUP_ERROR_PARSE,
597                    _("Element <%s> not recognized"),
598                    element_name);
599     }
600   
601   return TRUE;
602 }
603
604 gboolean
605 parser_end_element (Parser      *parser,
606                     const char  *element_name,
607                     GError     **error)
608 {
609   g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
610
611   if (ELEMENT_IS ("interface"))
612     {
613       parser->interface = NULL;
614     }
615   else if (ELEMENT_IS ("method"))
616     {
617       parser->method = NULL;
618     }
619   else if (ELEMENT_IS ("signal"))
620     {
621       parser->signal = NULL;
622     }
623   else if (ELEMENT_IS ("arg"))
624     {
625       parser->arg = NULL;
626     }
627   else if (ELEMENT_IS ("node"))
628     {
629       NodeInfo *top;
630
631       g_assert (parser->node_stack != NULL);
632       top = parser->node_stack->data;
633
634       parser->node_stack = g_slist_remove (parser->node_stack,
635                                            top);
636
637       if (parser->node_stack == NULL)
638         parser->result = top; /* We are done, store the result */      
639     }
640   else
641     g_assert_not_reached (); /* should have had an error on start_element */
642   
643   return TRUE;
644 }
645
646 gboolean
647 parser_content (Parser      *parser,
648                 const char  *content,
649                 int          len,
650                 GError     **error)
651 {
652   g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
653
654   return TRUE;
655 }
656
657 gboolean
658 parser_finished (Parser      *parser,
659                  GError     **error)
660 {
661   g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
662
663   return TRUE;
664 }
665
666 NodeInfo*
667 parser_get_nodes (Parser *parser)
668 {
669   return parser->result;
670 }
671
672 #endif /* DOXYGEN_SHOULD_SKIP_THIS */