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