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