• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1  /* GDBus - GLib D-Bus Library
2   *
3   * Copyright (C) 2008-2010 Red Hat, Inc.
4   *
5   * This library is free software; you can redistribute it and/or
6   * modify it under the terms of the GNU Lesser General Public
7   * License as published by the Free Software Foundation; either
8   * version 2.1 of the License, or (at your option) any later version.
9   *
10   * This library is distributed in the hope that it will be useful,
11   * but WITHOUT ANY WARRANTY; without even the implied warranty of
12   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13   * Lesser General Public License for more details.
14   *
15   * You should have received a copy of the GNU Lesser General
16   * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
17   *
18   * Author: David Zeuthen <davidz@redhat.com>
19   */
20  
21  #include "config.h"
22  
23  #include <stdlib.h>
24  #include <string.h>
25  
26  #include "gdbusintrospection.h"
27  
28  #include "glibintl.h"
29  
30  /**
31   * SECTION:gdbusintrospection
32   * @title: D-Bus Introspection Data
33   * @short_description: Node and interface description data structures
34   * @include: gio/gio.h
35   *
36   * Various data structures and convenience routines to parse and
37   * generate D-Bus introspection XML. Introspection information is
38   * used when registering objects with g_dbus_connection_register_object().
39   *
40   * The format of D-Bus introspection XML is specified in the
41   * [D-Bus specification](http://dbus.freedesktop.org/doc/dbus-specification.html#introspection-format)
42   */
43  
44  /* ---------------------------------------------------------------------------------------------------- */
45  
46  #define _MY_DEFINE_BOXED_TYPE(TypeName, type_name) \
47    G_DEFINE_BOXED_TYPE (TypeName, type_name, type_name##_ref, type_name##_unref)
48  
49  _MY_DEFINE_BOXED_TYPE (GDBusNodeInfo,       g_dbus_node_info)
50  _MY_DEFINE_BOXED_TYPE (GDBusInterfaceInfo,  g_dbus_interface_info)
51  _MY_DEFINE_BOXED_TYPE (GDBusMethodInfo,     g_dbus_method_info)
52  _MY_DEFINE_BOXED_TYPE (GDBusSignalInfo,     g_dbus_signal_info)
53  _MY_DEFINE_BOXED_TYPE (GDBusPropertyInfo,   g_dbus_property_info)
54  _MY_DEFINE_BOXED_TYPE (GDBusArgInfo,        g_dbus_arg_info)
55  _MY_DEFINE_BOXED_TYPE (GDBusAnnotationInfo, g_dbus_annotation_info)
56  
57  #undef _MY_DEFINE_BOXED_TYPE
58  
59  /* ---------------------------------------------------------------------------------------------------- */
60  
61  typedef struct
62  {
63    /* stuff we are currently collecting */
64    GPtrArray *args;
65    GPtrArray *out_args;
66    GPtrArray *methods;
67    GPtrArray *signals;
68    GPtrArray *properties;
69    GPtrArray *interfaces;
70    GPtrArray *nodes;
71    GPtrArray *annotations;
72  
73    /* A list of GPtrArray's containing annotations */
74    GSList *annotations_stack;
75  
76    /* A list of GPtrArray's containing interfaces */
77    GSList *interfaces_stack;
78  
79    /* A list of GPtrArray's containing nodes */
80    GSList *nodes_stack;
81  
82    /* Whether the direction was "in" for last parsed arg */
83    gboolean last_arg_was_in;
84  
85    /* Number of args currently being collected; used for assigning
86     * names to args without a "name" attribute
87     */
88    guint num_args;
89  
90  } ParseData;
91  
92  /* ---------------------------------------------------------------------------------------------------- */
93  
94  /**
95   * g_dbus_node_info_ref:
96   * @info: A #GDBusNodeInfo
97   *
98   * If @info is statically allocated does nothing. Otherwise increases
99   * the reference count.
100   *
101   * Returns: The same @info.
102   *
103   * Since: 2.26
104   */
105  GDBusNodeInfo *
g_dbus_node_info_ref(GDBusNodeInfo * info)106  g_dbus_node_info_ref (GDBusNodeInfo *info)
107  {
108    if (g_atomic_int_get (&info->ref_count) == -1)
109      return info;
110    g_atomic_int_inc (&info->ref_count);
111    return info;
112  }
113  
114  /**
115   * g_dbus_interface_info_ref:
116   * @info: A #GDBusInterfaceInfo
117   *
118   * If @info is statically allocated does nothing. Otherwise increases
119   * the reference count.
120   *
121   * Returns: The same @info.
122   *
123   * Since: 2.26
124   */
125  GDBusInterfaceInfo *
g_dbus_interface_info_ref(GDBusInterfaceInfo * info)126  g_dbus_interface_info_ref (GDBusInterfaceInfo *info)
127  {
128    if (g_atomic_int_get (&info->ref_count) == -1)
129      return info;
130    g_atomic_int_inc (&info->ref_count);
131    return info;
132  }
133  
134  /**
135   * g_dbus_method_info_ref:
136   * @info: A #GDBusMethodInfo
137   *
138   * If @info is statically allocated does nothing. Otherwise increases
139   * the reference count.
140   *
141   * Returns: The same @info.
142   *
143   * Since: 2.26
144   */
145  GDBusMethodInfo *
g_dbus_method_info_ref(GDBusMethodInfo * info)146  g_dbus_method_info_ref (GDBusMethodInfo *info)
147  {
148    if (g_atomic_int_get (&info->ref_count) == -1)
149      return info;
150    g_atomic_int_inc (&info->ref_count);
151    return info;
152  }
153  
154  /**
155   * g_dbus_signal_info_ref:
156   * @info: A #GDBusSignalInfo
157   *
158   * If @info is statically allocated does nothing. Otherwise increases
159   * the reference count.
160   *
161   * Returns: The same @info.
162   *
163   * Since: 2.26
164   */
165  GDBusSignalInfo *
g_dbus_signal_info_ref(GDBusSignalInfo * info)166  g_dbus_signal_info_ref (GDBusSignalInfo *info)
167  {
168    if (g_atomic_int_get (&info->ref_count) == -1)
169      return info;
170    g_atomic_int_inc (&info->ref_count);
171    return info;
172  }
173  
174  /**
175   * g_dbus_property_info_ref:
176   * @info: A #GDBusPropertyInfo
177   *
178   * If @info is statically allocated does nothing. Otherwise increases
179   * the reference count.
180   *
181   * Returns: The same @info.
182   *
183   * Since: 2.26
184   */
185  GDBusPropertyInfo *
g_dbus_property_info_ref(GDBusPropertyInfo * info)186  g_dbus_property_info_ref (GDBusPropertyInfo *info)
187  {
188    if (g_atomic_int_get (&info->ref_count) == -1)
189      return info;
190    g_atomic_int_inc (&info->ref_count);
191    return info;
192  }
193  
194  /**
195   * g_dbus_arg_info_ref:
196   * @info: A #GDBusArgInfo
197   *
198   * If @info is statically allocated does nothing. Otherwise increases
199   * the reference count.
200   *
201   * Returns: The same @info.
202   *
203   * Since: 2.26
204   */
205  GDBusArgInfo *
g_dbus_arg_info_ref(GDBusArgInfo * info)206  g_dbus_arg_info_ref (GDBusArgInfo *info)
207  {
208    if (g_atomic_int_get (&info->ref_count) == -1)
209      return info;
210    g_atomic_int_inc (&info->ref_count);
211    return info;
212  }
213  
214  /**
215   * g_dbus_annotation_info_ref:
216   * @info: A #GDBusNodeInfo
217   *
218   * If @info is statically allocated does nothing. Otherwise increases
219   * the reference count.
220   *
221   * Returns: The same @info.
222   *
223   * Since: 2.26
224   */
225  GDBusAnnotationInfo *
g_dbus_annotation_info_ref(GDBusAnnotationInfo * info)226  g_dbus_annotation_info_ref (GDBusAnnotationInfo *info)
227  {
228    if (g_atomic_int_get (&info->ref_count) == -1)
229      return info;
230    g_atomic_int_inc (&info->ref_count);
231    return info;
232  }
233  
234  /* ---------------------------------------------------------------------------------------------------- */
235  
236  static void
free_null_terminated_array(gpointer array,GDestroyNotify unref_func)237  free_null_terminated_array (gpointer array, GDestroyNotify unref_func)
238  {
239    guint n;
240    gpointer *p = array;
241    if (p == NULL)
242      return;
243    for (n = 0; p[n] != NULL; n++)
244      unref_func (p[n]);
245    g_free (p);
246  }
247  
248  /**
249   * g_dbus_annotation_info_unref:
250   * @info: A #GDBusAnnotationInfo.
251   *
252   * If @info is statically allocated, does nothing. Otherwise decreases
253   * the reference count of @info. When its reference count drops to 0,
254   * the memory used is freed.
255   *
256   * Since: 2.26
257   */
258  void
g_dbus_annotation_info_unref(GDBusAnnotationInfo * info)259  g_dbus_annotation_info_unref (GDBusAnnotationInfo *info)
260  {
261    if (g_atomic_int_get (&info->ref_count) == -1)
262      return;
263    if (g_atomic_int_dec_and_test (&info->ref_count))
264      {
265        g_free (info->key);
266        g_free (info->value);
267        free_null_terminated_array (info->annotations, (GDestroyNotify) g_dbus_annotation_info_unref);
268        g_free (info);
269      }
270  }
271  
272  /**
273   * g_dbus_arg_info_unref:
274   * @info: A #GDBusArgInfo.
275   *
276   * If @info is statically allocated, does nothing. Otherwise decreases
277   * the reference count of @info. When its reference count drops to 0,
278   * the memory used is freed.
279   *
280   * Since: 2.26
281   */
282  void
g_dbus_arg_info_unref(GDBusArgInfo * info)283  g_dbus_arg_info_unref (GDBusArgInfo *info)
284  {
285    if (g_atomic_int_get (&info->ref_count) == -1)
286      return;
287    if (g_atomic_int_dec_and_test (&info->ref_count))
288      {
289        g_free (info->name);
290        g_free (info->signature);
291        free_null_terminated_array (info->annotations, (GDestroyNotify) g_dbus_annotation_info_unref);
292        g_free (info);
293      }
294  }
295  
296  /**
297   * g_dbus_method_info_unref:
298   * @info: A #GDBusMethodInfo.
299   *
300   * If @info is statically allocated, does nothing. Otherwise decreases
301   * the reference count of @info. When its reference count drops to 0,
302   * the memory used is freed.
303   *
304   * Since: 2.26
305   */
306  void
g_dbus_method_info_unref(GDBusMethodInfo * info)307  g_dbus_method_info_unref (GDBusMethodInfo *info)
308  {
309    if (g_atomic_int_get (&info->ref_count) == -1)
310      return;
311    if (g_atomic_int_dec_and_test (&info->ref_count))
312      {
313        g_free (info->name);
314        free_null_terminated_array (info->in_args, (GDestroyNotify) g_dbus_arg_info_unref);
315        free_null_terminated_array (info->out_args, (GDestroyNotify) g_dbus_arg_info_unref);
316        free_null_terminated_array (info->annotations, (GDestroyNotify) g_dbus_annotation_info_unref);
317        g_free (info);
318      }
319  }
320  
321  /**
322   * g_dbus_signal_info_unref:
323   * @info: A #GDBusSignalInfo.
324   *
325   * If @info is statically allocated, does nothing. Otherwise decreases
326   * the reference count of @info. When its reference count drops to 0,
327   * the memory used is freed.
328   *
329   * Since: 2.26
330   */
331  void
g_dbus_signal_info_unref(GDBusSignalInfo * info)332  g_dbus_signal_info_unref (GDBusSignalInfo *info)
333  {
334    if (g_atomic_int_get (&info->ref_count) == -1)
335      return;
336    if (g_atomic_int_dec_and_test (&info->ref_count))
337      {
338        g_free (info->name);
339        free_null_terminated_array (info->args, (GDestroyNotify) g_dbus_arg_info_unref);
340        free_null_terminated_array (info->annotations, (GDestroyNotify) g_dbus_annotation_info_unref);
341        g_free (info);
342      }
343  }
344  
345  /**
346   * g_dbus_property_info_unref:
347   * @info: A #GDBusPropertyInfo.
348   *
349   * If @info is statically allocated, does nothing. Otherwise decreases
350   * the reference count of @info. When its reference count drops to 0,
351   * the memory used is freed.
352   *
353   * Since: 2.26
354   */
355  void
g_dbus_property_info_unref(GDBusPropertyInfo * info)356  g_dbus_property_info_unref (GDBusPropertyInfo *info)
357  {
358    if (g_atomic_int_get (&info->ref_count) == -1)
359      return;
360    if (g_atomic_int_dec_and_test (&info->ref_count))
361      {
362        g_free (info->name);
363        g_free (info->signature);
364        free_null_terminated_array (info->annotations, (GDestroyNotify) g_dbus_annotation_info_unref);
365        g_free (info);
366      }
367  }
368  
369  /**
370   * g_dbus_interface_info_unref:
371   * @info: A #GDBusInterfaceInfo.
372   *
373   * If @info is statically allocated, does nothing. Otherwise decreases
374   * the reference count of @info. When its reference count drops to 0,
375   * the memory used is freed.
376   *
377   * Since: 2.26
378   */
379  void
g_dbus_interface_info_unref(GDBusInterfaceInfo * info)380  g_dbus_interface_info_unref (GDBusInterfaceInfo *info)
381  {
382    if (g_atomic_int_get (&info->ref_count) == -1)
383      return;
384    if (g_atomic_int_dec_and_test (&info->ref_count))
385      {
386        g_free (info->name);
387        free_null_terminated_array (info->methods, (GDestroyNotify) g_dbus_method_info_unref);
388        free_null_terminated_array (info->signals, (GDestroyNotify) g_dbus_signal_info_unref);
389        free_null_terminated_array (info->properties, (GDestroyNotify) g_dbus_property_info_unref);
390        free_null_terminated_array (info->annotations, (GDestroyNotify) g_dbus_annotation_info_unref);
391        g_free (info);
392      }
393  }
394  
395  /**
396   * g_dbus_node_info_unref:
397   * @info: A #GDBusNodeInfo.
398   *
399   * If @info is statically allocated, does nothing. Otherwise decreases
400   * the reference count of @info. When its reference count drops to 0,
401   * the memory used is freed.
402   *
403   * Since: 2.26
404   */
405  void
g_dbus_node_info_unref(GDBusNodeInfo * info)406  g_dbus_node_info_unref (GDBusNodeInfo *info)
407  {
408    if (g_atomic_int_get (&info->ref_count) == -1)
409      return;
410    if (g_atomic_int_dec_and_test (&info->ref_count))
411      {
412        g_free (info->path);
413        free_null_terminated_array (info->interfaces, (GDestroyNotify) g_dbus_interface_info_unref);
414        free_null_terminated_array (info->nodes, (GDestroyNotify) g_dbus_node_info_unref);
415        free_null_terminated_array (info->annotations, (GDestroyNotify) g_dbus_annotation_info_unref);
416        g_free (info);
417      }
418  }
419  
420  /* ---------------------------------------------------------------------------------------------------- */
421  
422  static void
g_dbus_annotation_info_set(ParseData * data,GDBusAnnotationInfo * info,const gchar * key,const gchar * value,GDBusAnnotationInfo ** embedded_annotations)423  g_dbus_annotation_info_set (ParseData            *data,
424                              GDBusAnnotationInfo  *info,
425                              const gchar          *key,
426                              const gchar          *value,
427                              GDBusAnnotationInfo **embedded_annotations)
428  {
429    info->ref_count = 1;
430  
431    if (key != NULL)
432      info->key = g_strdup (key);
433  
434    if (value != NULL)
435      info->value = g_strdup (value);
436  
437    if (embedded_annotations != NULL)
438      info->annotations = embedded_annotations;
439  }
440  
441  static void
g_dbus_arg_info_set(ParseData * data,GDBusArgInfo * info,const gchar * name,const gchar * signature,GDBusAnnotationInfo ** annotations)442  g_dbus_arg_info_set (ParseData            *data,
443                       GDBusArgInfo         *info,
444                       const gchar          *name,
445                       const gchar          *signature,
446                       GDBusAnnotationInfo **annotations)
447  {
448    info->ref_count = 1;
449  
450    /* name may be NULL - TODO: compute name? */
451    if (name != NULL)
452      info->name = g_strdup (name);
453  
454    if (signature != NULL)
455      info->signature = g_strdup (signature);
456  
457    if (annotations != NULL)
458      info->annotations = annotations;
459  }
460  
461  static void
g_dbus_method_info_set(ParseData * data,GDBusMethodInfo * info,const gchar * name,GDBusArgInfo ** in_args,GDBusArgInfo ** out_args,GDBusAnnotationInfo ** annotations)462  g_dbus_method_info_set (ParseData            *data,
463                          GDBusMethodInfo      *info,
464                          const gchar          *name,
465                          GDBusArgInfo        **in_args,
466                          GDBusArgInfo        **out_args,
467                          GDBusAnnotationInfo **annotations)
468  {
469    info->ref_count = 1;
470  
471    if (name != NULL)
472      info->name = g_strdup (name);
473  
474    if (in_args != NULL)
475      info->in_args = in_args;
476  
477    if (out_args != NULL)
478      info->out_args = out_args;
479  
480    if (annotations != NULL)
481      info->annotations = annotations;
482  }
483  
484  static void
g_dbus_signal_info_set(ParseData * data,GDBusSignalInfo * info,const gchar * name,GDBusArgInfo ** args,GDBusAnnotationInfo ** annotations)485  g_dbus_signal_info_set (ParseData            *data,
486                          GDBusSignalInfo      *info,
487                          const gchar          *name,
488                          GDBusArgInfo        **args,
489                          GDBusAnnotationInfo **annotations)
490  {
491    info->ref_count = 1;
492  
493    if (name != NULL)
494      info->name = g_strdup (name);
495  
496    if (args != NULL)
497      info->args = args;
498  
499    if (annotations != NULL)
500      info->annotations = annotations;
501  }
502  
503  static void
g_dbus_property_info_set(ParseData * data,GDBusPropertyInfo * info,const gchar * name,const gchar * signature,GDBusPropertyInfoFlags flags,GDBusAnnotationInfo ** annotations)504  g_dbus_property_info_set (ParseData               *data,
505                            GDBusPropertyInfo       *info,
506                            const gchar             *name,
507                            const gchar             *signature,
508                            GDBusPropertyInfoFlags   flags,
509                            GDBusAnnotationInfo    **annotations)
510  {
511    info->ref_count = 1;
512  
513    if (name != NULL)
514      info->name = g_strdup (name);
515  
516    if (flags != G_DBUS_PROPERTY_INFO_FLAGS_NONE)
517      info->flags = flags;
518  
519    if (signature != NULL)
520      info->signature = g_strdup (signature);
521  
522    if (annotations != NULL)
523      info->annotations = annotations;
524  }
525  
526  static void
g_dbus_interface_info_set(ParseData * data,GDBusInterfaceInfo * info,const gchar * name,GDBusMethodInfo ** methods,GDBusSignalInfo ** signals,GDBusPropertyInfo ** properties,GDBusAnnotationInfo ** annotations)527  g_dbus_interface_info_set (ParseData            *data,
528                             GDBusInterfaceInfo   *info,
529                             const gchar          *name,
530                             GDBusMethodInfo     **methods,
531                             GDBusSignalInfo     **signals,
532                             GDBusPropertyInfo   **properties,
533                             GDBusAnnotationInfo **annotations)
534  {
535    info->ref_count = 1;
536  
537    if (name != NULL)
538      info->name = g_strdup (name);
539  
540    if (methods != NULL)
541      info->methods = methods;
542  
543    if (signals != NULL)
544      info->signals = signals;
545  
546    if (properties != NULL)
547      info->properties = properties;
548  
549    if (annotations != NULL)
550      info->annotations = annotations;
551  }
552  
553  static void
g_dbus_node_info_set(ParseData * data,GDBusNodeInfo * info,const gchar * path,GDBusInterfaceInfo ** interfaces,GDBusNodeInfo ** nodes,GDBusAnnotationInfo ** annotations)554  g_dbus_node_info_set (ParseData            *data,
555                        GDBusNodeInfo        *info,
556                        const gchar          *path,
557                        GDBusInterfaceInfo  **interfaces,
558                        GDBusNodeInfo       **nodes,
559                        GDBusAnnotationInfo **annotations)
560  {
561    info->ref_count = 1;
562  
563    if (path != NULL)
564      {
565        info->path = g_strdup (path);
566        /* TODO: relative / absolute path snafu */
567      }
568  
569    if (interfaces != NULL)
570      info->interfaces = interfaces;
571  
572    if (nodes != NULL)
573      info->nodes = nodes;
574  
575    if (annotations != NULL)
576      info->annotations = annotations;
577  }
578  
579  /* ---------------------------------------------------------------------------------------------------- */
580  
581  static void
g_dbus_annotation_info_generate_xml(GDBusAnnotationInfo * info,guint indent,GString * string_builder)582  g_dbus_annotation_info_generate_xml (GDBusAnnotationInfo *info,
583                                       guint                indent,
584                                       GString             *string_builder)
585  {
586    gchar *tmp;
587    guint n;
588  
589    tmp = g_markup_printf_escaped ("%*s<annotation name=\"%s\" value=\"%s\"",
590                                   indent, "",
591                                   info->key,
592                                   info->value);
593    g_string_append (string_builder, tmp);
594    g_free (tmp);
595  
596    if (info->annotations == NULL)
597      {
598        g_string_append (string_builder, "/>\n");
599      }
600    else
601      {
602        g_string_append (string_builder, ">\n");
603  
604        for (n = 0; info->annotations != NULL && info->annotations[n] != NULL; n++)
605          g_dbus_annotation_info_generate_xml (info->annotations[n],
606                                               indent + 2,
607                                               string_builder);
608  
609        g_string_append_printf (string_builder, "%*s</annotation>\n",
610                                indent, "");
611      }
612  
613  }
614  
615  static void
g_dbus_arg_info_generate_xml(GDBusArgInfo * info,guint indent,const gchar * extra_attributes,GString * string_builder)616  g_dbus_arg_info_generate_xml (GDBusArgInfo *info,
617                                guint         indent,
618                                const gchar  *extra_attributes,
619                                GString      *string_builder)
620  {
621    guint n;
622  
623    g_string_append_printf (string_builder, "%*s<arg type=\"%s\"",
624                            indent, "",
625                            info->signature);
626  
627    if (info->name != NULL)
628      g_string_append_printf (string_builder, " name=\"%s\"", info->name);
629  
630    if (extra_attributes != NULL)
631      g_string_append_printf (string_builder, " %s", extra_attributes);
632  
633    if (info->annotations == NULL)
634      {
635        g_string_append (string_builder, "/>\n");
636      }
637    else
638      {
639        g_string_append (string_builder, ">\n");
640  
641        for (n = 0; info->annotations != NULL && info->annotations[n] != NULL; n++)
642          g_dbus_annotation_info_generate_xml (info->annotations[n],
643                                               indent + 2,
644                                               string_builder);
645  
646        g_string_append_printf (string_builder, "%*s</arg>\n", indent, "");
647      }
648  
649  }
650  
651  static void
g_dbus_method_info_generate_xml(GDBusMethodInfo * info,guint indent,GString * string_builder)652  g_dbus_method_info_generate_xml (GDBusMethodInfo *info,
653                                   guint            indent,
654                                   GString         *string_builder)
655  {
656    guint n;
657  
658    g_string_append_printf (string_builder, "%*s<method name=\"%s\"",
659                            indent, "",
660                            info->name);
661  
662    if (info->annotations == NULL && info->in_args == NULL && info->out_args == NULL)
663      {
664        g_string_append (string_builder, "/>\n");
665      }
666    else
667      {
668        g_string_append (string_builder, ">\n");
669  
670        for (n = 0; info->annotations != NULL && info->annotations[n] != NULL; n++)
671          g_dbus_annotation_info_generate_xml (info->annotations[n],
672                                               indent + 2,
673                                               string_builder);
674  
675        for (n = 0; info->in_args != NULL && info->in_args[n] != NULL; n++)
676          g_dbus_arg_info_generate_xml (info->in_args[n],
677                                        indent + 2,
678                                        "direction=\"in\"",
679                                        string_builder);
680  
681        for (n = 0; info->out_args != NULL && info->out_args[n] != NULL; n++)
682          g_dbus_arg_info_generate_xml (info->out_args[n],
683                                        indent + 2,
684                                        "direction=\"out\"",
685                                        string_builder);
686  
687        g_string_append_printf (string_builder, "%*s</method>\n", indent, "");
688      }
689  }
690  
691  static void
g_dbus_signal_info_generate_xml(GDBusSignalInfo * info,guint indent,GString * string_builder)692  g_dbus_signal_info_generate_xml (GDBusSignalInfo *info,
693                                   guint            indent,
694                                   GString         *string_builder)
695  {
696    guint n;
697  
698    g_string_append_printf (string_builder, "%*s<signal name=\"%s\"",
699                            indent, "",
700                            info->name);
701  
702    if (info->annotations == NULL && info->args == NULL)
703      {
704        g_string_append (string_builder, "/>\n");
705      }
706    else
707      {
708        g_string_append (string_builder, ">\n");
709  
710        for (n = 0; info->annotations != NULL && info->annotations[n] != NULL; n++)
711          g_dbus_annotation_info_generate_xml (info->annotations[n],
712                                               indent + 2,
713                                               string_builder);
714  
715        for (n = 0; info->args != NULL && info->args[n] != NULL; n++)
716          g_dbus_arg_info_generate_xml (info->args[n],
717                                        indent + 2,
718                                        NULL,
719                                        string_builder);
720  
721        g_string_append_printf (string_builder, "%*s</signal>\n", indent, "");
722      }
723  }
724  
725  static void
g_dbus_property_info_generate_xml(GDBusPropertyInfo * info,guint indent,GString * string_builder)726  g_dbus_property_info_generate_xml (GDBusPropertyInfo *info,
727                                     guint              indent,
728                                     GString           *string_builder)
729  {
730    guint n;
731    const gchar *access_string;
732  
733    if ((info->flags & G_DBUS_PROPERTY_INFO_FLAGS_READABLE) &&
734        (info->flags & G_DBUS_PROPERTY_INFO_FLAGS_WRITABLE))
735      {
736        access_string = "readwrite";
737      }
738    else if (info->flags & G_DBUS_PROPERTY_INFO_FLAGS_READABLE)
739      {
740        access_string = "read";
741      }
742    else if (info->flags & G_DBUS_PROPERTY_INFO_FLAGS_WRITABLE)
743      {
744        access_string = "write";
745      }
746    else
747      {
748        g_assert_not_reached ();
749      }
750  
751    g_string_append_printf (string_builder, "%*s<property type=\"%s\" name=\"%s\" access=\"%s\"",
752                            indent, "",
753                            info->signature,
754                            info->name,
755                            access_string);
756  
757    if (info->annotations == NULL)
758      {
759        g_string_append (string_builder, "/>\n");
760      }
761    else
762      {
763        g_string_append (string_builder, ">\n");
764  
765        for (n = 0; info->annotations != NULL && info->annotations[n] != NULL; n++)
766          g_dbus_annotation_info_generate_xml (info->annotations[n],
767                                                 indent + 2,
768                                                 string_builder);
769  
770        g_string_append_printf (string_builder, "%*s</property>\n", indent, "");
771      }
772  
773  }
774  
775  /**
776   * g_dbus_interface_info_generate_xml:
777   * @info: A #GDBusNodeInfo
778   * @indent: Indentation level.
779   * @string_builder: A #GString to to append XML data to.
780   *
781   * Appends an XML representation of @info (and its children) to @string_builder.
782   *
783   * This function is typically used for generating introspection XML
784   * documents at run-time for handling the
785   * `org.freedesktop.DBus.Introspectable.Introspect`
786   * method.
787   *
788   * Since: 2.26
789   */
790  void
g_dbus_interface_info_generate_xml(GDBusInterfaceInfo * info,guint indent,GString * string_builder)791  g_dbus_interface_info_generate_xml (GDBusInterfaceInfo *info,
792                                      guint               indent,
793                                      GString            *string_builder)
794  {
795    guint n;
796  
797    g_string_append_printf (string_builder, "%*s<interface name=\"%s\">\n",
798                            indent, "",
799                            info->name);
800  
801    for (n = 0; info->annotations != NULL && info->annotations[n] != NULL; n++)
802      g_dbus_annotation_info_generate_xml (info->annotations[n],
803                                           indent + 2,
804                                           string_builder);
805  
806    for (n = 0; info->methods != NULL && info->methods[n] != NULL; n++)
807      g_dbus_method_info_generate_xml (info->methods[n],
808                                       indent + 2,
809                                       string_builder);
810  
811    for (n = 0; info->signals != NULL && info->signals[n] != NULL; n++)
812      g_dbus_signal_info_generate_xml (info->signals[n],
813                                       indent + 2,
814                                       string_builder);
815  
816    for (n = 0; info->properties != NULL && info->properties[n] != NULL; n++)
817      g_dbus_property_info_generate_xml (info->properties[n],
818                                         indent + 2,
819                                         string_builder);
820  
821    g_string_append_printf (string_builder, "%*s</interface>\n", indent, "");
822  }
823  
824  /**
825   * g_dbus_node_info_generate_xml:
826   * @info: A #GDBusNodeInfo.
827   * @indent: Indentation level.
828   * @string_builder: A #GString to to append XML data to.
829   *
830   * Appends an XML representation of @info (and its children) to @string_builder.
831   *
832   * This function is typically used for generating introspection XML documents at run-time for
833   * handling the `org.freedesktop.DBus.Introspectable.Introspect`  method.
834   *
835   * Since: 2.26
836   */
837  void
g_dbus_node_info_generate_xml(GDBusNodeInfo * info,guint indent,GString * string_builder)838  g_dbus_node_info_generate_xml (GDBusNodeInfo *info,
839                                 guint          indent,
840                                 GString       *string_builder)
841  {
842    guint n;
843  
844    g_string_append_printf (string_builder, "%*s<node", indent, "");
845    if (info->path != NULL)
846      g_string_append_printf (string_builder, " name=\"%s\"", info->path);
847  
848    if (info->interfaces == NULL && info->nodes == NULL && info->annotations == NULL)
849      {
850        g_string_append (string_builder, "/>\n");
851      }
852    else
853      {
854        g_string_append (string_builder, ">\n");
855  
856        for (n = 0; info->annotations != NULL && info->annotations[n] != NULL; n++)
857          g_dbus_annotation_info_generate_xml (info->annotations[n],
858                                               indent + 2,
859                                               string_builder);
860  
861        for (n = 0; info->interfaces != NULL && info->interfaces[n] != NULL; n++)
862          g_dbus_interface_info_generate_xml (info->interfaces[n],
863                                              indent + 2,
864                                              string_builder);
865  
866        for (n = 0; info->nodes != NULL && info->nodes[n] != NULL; n++)
867          g_dbus_node_info_generate_xml (info->nodes[n],
868                                         indent + 2,
869                                         string_builder);
870  
871        g_string_append_printf (string_builder, "%*s</node>\n", indent, "");
872      }
873  }
874  
875  /* ---------------------------------------------------------------------------------------------------- */
876  
877  static GDBusAnnotationInfo **
parse_data_steal_annotations(ParseData * data,guint * out_num_elements)878  parse_data_steal_annotations (ParseData *data,
879                                guint     *out_num_elements)
880  {
881    GDBusAnnotationInfo **ret;
882    if (out_num_elements != NULL)
883      *out_num_elements = data->annotations->len;
884    if (data->annotations == NULL)
885      ret = NULL;
886    else
887      {
888        g_ptr_array_add (data->annotations, NULL);
889        ret = (GDBusAnnotationInfo **) g_ptr_array_free (data->annotations, FALSE);
890      }
891    data->annotations = g_ptr_array_new ();
892    return ret;
893  }
894  
895  static GDBusArgInfo **
parse_data_steal_args(ParseData * data,guint * out_num_elements)896  parse_data_steal_args (ParseData *data,
897                         guint     *out_num_elements)
898  {
899    GDBusArgInfo **ret;
900    if (out_num_elements != NULL)
901      *out_num_elements = data->args->len;
902    if (data->args == NULL)
903      ret = NULL;
904    else
905      {
906        g_ptr_array_add (data->args, NULL);
907        ret = (GDBusArgInfo **) g_ptr_array_free (data->args, FALSE);
908      }
909    data->args = g_ptr_array_new ();
910    return ret;
911  }
912  
913  static GDBusArgInfo **
parse_data_steal_out_args(ParseData * data,guint * out_num_elements)914  parse_data_steal_out_args (ParseData *data,
915                             guint     *out_num_elements)
916  {
917    GDBusArgInfo **ret;
918    if (out_num_elements != NULL)
919      *out_num_elements = data->out_args->len;
920    if (data->out_args == NULL)
921      ret = NULL;
922    else
923      {
924        g_ptr_array_add (data->out_args, NULL);
925        ret = (GDBusArgInfo **) g_ptr_array_free (data->out_args, FALSE);
926      }
927    data->out_args = g_ptr_array_new ();
928    return ret;
929  }
930  
931  static GDBusMethodInfo **
parse_data_steal_methods(ParseData * data,guint * out_num_elements)932  parse_data_steal_methods (ParseData *data,
933                            guint     *out_num_elements)
934  {
935    GDBusMethodInfo **ret;
936    if (out_num_elements != NULL)
937      *out_num_elements = data->methods->len;
938    if (data->methods == NULL)
939      ret = NULL;
940    else
941      {
942        g_ptr_array_add (data->methods, NULL);
943        ret = (GDBusMethodInfo **) g_ptr_array_free (data->methods, FALSE);
944      }
945    data->methods = g_ptr_array_new ();
946    return ret;
947  }
948  
949  static GDBusSignalInfo **
parse_data_steal_signals(ParseData * data,guint * out_num_elements)950  parse_data_steal_signals (ParseData *data,
951                            guint     *out_num_elements)
952  {
953    GDBusSignalInfo **ret;
954    if (out_num_elements != NULL)
955      *out_num_elements = data->signals->len;
956    if (data->signals == NULL)
957      ret = NULL;
958    else
959      {
960        g_ptr_array_add (data->signals, NULL);
961        ret = (GDBusSignalInfo **) g_ptr_array_free (data->signals, FALSE);
962      }
963    data->signals = g_ptr_array_new ();
964    return ret;
965  }
966  
967  static GDBusPropertyInfo **
parse_data_steal_properties(ParseData * data,guint * out_num_elements)968  parse_data_steal_properties (ParseData *data,
969                               guint     *out_num_elements)
970  {
971    GDBusPropertyInfo **ret;
972    if (out_num_elements != NULL)
973      *out_num_elements = data->properties->len;
974    if (data->properties == NULL)
975      ret = NULL;
976    else
977      {
978        g_ptr_array_add (data->properties, NULL);
979        ret = (GDBusPropertyInfo **) g_ptr_array_free (data->properties, FALSE);
980      }
981    data->properties = g_ptr_array_new ();
982    return ret;
983  }
984  
985  static GDBusInterfaceInfo **
parse_data_steal_interfaces(ParseData * data,guint * out_num_elements)986  parse_data_steal_interfaces (ParseData *data,
987                               guint     *out_num_elements)
988  {
989    GDBusInterfaceInfo **ret;
990    if (out_num_elements != NULL)
991      *out_num_elements = data->interfaces->len;
992    if (data->interfaces == NULL)
993      ret = NULL;
994    else
995      {
996        g_ptr_array_add (data->interfaces, NULL);
997        ret = (GDBusInterfaceInfo **) g_ptr_array_free (data->interfaces, FALSE);
998      }
999    data->interfaces = g_ptr_array_new ();
1000    return ret;
1001  }
1002  
1003  static GDBusNodeInfo **
parse_data_steal_nodes(ParseData * data,guint * out_num_elements)1004  parse_data_steal_nodes (ParseData *data,
1005                          guint     *out_num_elements)
1006  {
1007    GDBusNodeInfo **ret;
1008    if (out_num_elements != NULL)
1009      *out_num_elements = data->nodes->len;
1010    if (data->nodes == NULL)
1011      ret = NULL;
1012    else
1013      {
1014        g_ptr_array_add (data->nodes, NULL);
1015        ret = (GDBusNodeInfo **) g_ptr_array_free (data->nodes, FALSE);
1016      }
1017    data->nodes = g_ptr_array_new ();
1018    return ret;
1019  }
1020  
1021  /* ---------------------------------------------------------------------------------------------------- */
1022  
1023  static void
parse_data_free_annotations(ParseData * data)1024  parse_data_free_annotations (ParseData *data)
1025  {
1026    if (data->annotations == NULL)
1027      return;
1028    g_ptr_array_foreach (data->annotations, (GFunc) g_dbus_annotation_info_unref, NULL);
1029    g_ptr_array_free (data->annotations, TRUE);
1030    data->annotations = NULL;
1031  }
1032  
1033  static void
parse_data_free_args(ParseData * data)1034  parse_data_free_args (ParseData *data)
1035  {
1036    if (data->args == NULL)
1037      return;
1038    g_ptr_array_foreach (data->args, (GFunc) g_dbus_arg_info_unref, NULL);
1039    g_ptr_array_free (data->args, TRUE);
1040    data->args = NULL;
1041  }
1042  
1043  static void
parse_data_free_out_args(ParseData * data)1044  parse_data_free_out_args (ParseData *data)
1045  {
1046    if (data->out_args == NULL)
1047      return;
1048    g_ptr_array_foreach (data->out_args, (GFunc) g_dbus_arg_info_unref, NULL);
1049    g_ptr_array_free (data->out_args, TRUE);
1050    data->out_args = NULL;
1051  }
1052  
1053  static void
parse_data_free_methods(ParseData * data)1054  parse_data_free_methods (ParseData *data)
1055  {
1056    if (data->methods == NULL)
1057      return;
1058    g_ptr_array_foreach (data->methods, (GFunc) g_dbus_method_info_unref, NULL);
1059    g_ptr_array_free (data->methods, TRUE);
1060    data->methods = NULL;
1061  }
1062  
1063  static void
parse_data_free_signals(ParseData * data)1064  parse_data_free_signals (ParseData *data)
1065  {
1066    if (data->signals == NULL)
1067      return;
1068    g_ptr_array_foreach (data->signals, (GFunc) g_dbus_signal_info_unref, NULL);
1069    g_ptr_array_free (data->signals, TRUE);
1070    data->signals = NULL;
1071  }
1072  
1073  static void
parse_data_free_properties(ParseData * data)1074  parse_data_free_properties (ParseData *data)
1075  {
1076    if (data->properties == NULL)
1077      return;
1078    g_ptr_array_foreach (data->properties, (GFunc) g_dbus_property_info_unref, NULL);
1079    g_ptr_array_free (data->properties, TRUE);
1080    data->properties = NULL;
1081  }
1082  
1083  static void
parse_data_free_interfaces(ParseData * data)1084  parse_data_free_interfaces (ParseData *data)
1085  {
1086    if (data->interfaces == NULL)
1087      return;
1088    g_ptr_array_foreach (data->interfaces, (GFunc) g_dbus_interface_info_unref, NULL);
1089    g_ptr_array_free (data->interfaces, TRUE);
1090    data->interfaces = NULL;
1091  }
1092  
1093  static void
parse_data_free_nodes(ParseData * data)1094  parse_data_free_nodes (ParseData *data)
1095  {
1096    if (data->nodes == NULL)
1097      return;
1098    g_ptr_array_foreach (data->nodes, (GFunc) g_dbus_node_info_unref, NULL);
1099    g_ptr_array_free (data->nodes, TRUE);
1100    data->nodes = NULL;
1101  }
1102  
1103  /* ---------------------------------------------------------------------------------------------------- */
1104  
1105  static GDBusAnnotationInfo *
parse_data_get_annotation(ParseData * data,gboolean create_new)1106  parse_data_get_annotation (ParseData *data,
1107                             gboolean   create_new)
1108  {
1109    if (create_new)
1110      g_ptr_array_add (data->annotations, g_new0 (GDBusAnnotationInfo, 1));
1111    return data->annotations->pdata[data->annotations->len - 1];
1112  }
1113  
1114  static GDBusArgInfo *
parse_data_get_arg(ParseData * data,gboolean create_new)1115  parse_data_get_arg (ParseData *data,
1116                      gboolean   create_new)
1117  {
1118    if (create_new)
1119      g_ptr_array_add (data->args, g_new0 (GDBusArgInfo, 1));
1120    return data->args->pdata[data->args->len - 1];
1121  }
1122  
1123  static GDBusArgInfo *
parse_data_get_out_arg(ParseData * data,gboolean create_new)1124  parse_data_get_out_arg (ParseData *data,
1125                          gboolean   create_new)
1126  {
1127    if (create_new)
1128      g_ptr_array_add (data->out_args, g_new0 (GDBusArgInfo, 1));
1129    return data->out_args->pdata[data->out_args->len - 1];
1130  }
1131  
1132  static GDBusMethodInfo *
parse_data_get_method(ParseData * data,gboolean create_new)1133  parse_data_get_method (ParseData *data,
1134                         gboolean   create_new)
1135  {
1136    if (create_new)
1137      g_ptr_array_add (data->methods, g_new0 (GDBusMethodInfo, 1));
1138    return data->methods->pdata[data->methods->len - 1];
1139  }
1140  
1141  static GDBusSignalInfo *
parse_data_get_signal(ParseData * data,gboolean create_new)1142  parse_data_get_signal (ParseData *data,
1143                         gboolean   create_new)
1144  {
1145    if (create_new)
1146      g_ptr_array_add (data->signals, g_new0 (GDBusSignalInfo, 1));
1147    return data->signals->pdata[data->signals->len - 1];
1148  }
1149  
1150  static GDBusPropertyInfo *
parse_data_get_property(ParseData * data,gboolean create_new)1151  parse_data_get_property (ParseData *data,
1152                           gboolean   create_new)
1153  {
1154    if (create_new)
1155      g_ptr_array_add (data->properties, g_new0 (GDBusPropertyInfo, 1));
1156    return data->properties->pdata[data->properties->len - 1];
1157  }
1158  
1159  static GDBusInterfaceInfo *
parse_data_get_interface(ParseData * data,gboolean create_new)1160  parse_data_get_interface (ParseData *data,
1161                            gboolean   create_new)
1162  {
1163    if (create_new)
1164      g_ptr_array_add (data->interfaces, g_new0 (GDBusInterfaceInfo, 1));
1165    return data->interfaces->pdata[data->interfaces->len - 1];
1166  }
1167  
1168  static GDBusNodeInfo *
parse_data_get_node(ParseData * data,gboolean create_new)1169  parse_data_get_node (ParseData *data,
1170                       gboolean   create_new)
1171  {
1172    if (create_new)
1173      g_ptr_array_add (data->nodes, g_new0 (GDBusNodeInfo, 1));
1174    return data->nodes->pdata[data->nodes->len - 1];
1175  }
1176  
1177  /* ---------------------------------------------------------------------------------------------------- */
1178  
1179  static ParseData *
parse_data_new(void)1180  parse_data_new (void)
1181  {
1182    ParseData *data;
1183  
1184    data = g_new0 (ParseData, 1);
1185  
1186    /* initialize arrays */
1187    parse_data_steal_annotations (data, NULL);
1188    parse_data_steal_args (data, NULL);
1189    parse_data_steal_out_args (data, NULL);
1190    parse_data_steal_methods (data, NULL);
1191    parse_data_steal_signals (data, NULL);
1192    parse_data_steal_properties (data, NULL);
1193    parse_data_steal_interfaces (data, NULL);
1194    parse_data_steal_nodes (data, NULL);
1195  
1196    return data;
1197  }
1198  
1199  static void
parse_data_free(ParseData * data)1200  parse_data_free (ParseData *data)
1201  {
1202    GSList *l;
1203  
1204    /* free stack of annotation arrays */
1205    for (l = data->annotations_stack; l != NULL; l = l->next)
1206      {
1207        GPtrArray *annotations = l->data;
1208        g_ptr_array_foreach (annotations, (GFunc) g_dbus_annotation_info_unref, NULL);
1209        g_ptr_array_free (annotations, TRUE);
1210      }
1211    g_slist_free (data->annotations_stack);
1212  
1213    /* free stack of interface arrays */
1214    for (l = data->interfaces_stack; l != NULL; l = l->next)
1215      {
1216        GPtrArray *interfaces = l->data;
1217        g_ptr_array_foreach (interfaces, (GFunc) g_dbus_interface_info_unref, NULL);
1218        g_ptr_array_free (interfaces, TRUE);
1219      }
1220    g_slist_free (data->interfaces_stack);
1221  
1222    /* free stack of node arrays */
1223    for (l = data->nodes_stack; l != NULL; l = l->next)
1224      {
1225        GPtrArray *nodes = l->data;
1226        g_ptr_array_foreach (nodes, (GFunc) g_dbus_node_info_unref, NULL);
1227        g_ptr_array_free (nodes, TRUE);
1228      }
1229    g_slist_free (data->nodes_stack);
1230  
1231    /* free arrays (data->annotations, data->interfaces and data->nodes have been freed above) */
1232    parse_data_free_args (data);
1233    parse_data_free_out_args (data);
1234    parse_data_free_methods (data);
1235    parse_data_free_signals (data);
1236    parse_data_free_properties (data);
1237    parse_data_free_interfaces (data);
1238    parse_data_free_annotations (data);
1239    parse_data_free_nodes (data);
1240  
1241    g_free (data);
1242  }
1243  
1244  /* ---------------------------------------------------------------------------------------------------- */
1245  
1246  static void
parser_start_element(GMarkupParseContext * context,const gchar * element_name,const gchar ** attribute_names,const gchar ** attribute_values,gpointer user_data,GError ** error)1247  parser_start_element (GMarkupParseContext  *context,
1248                        const gchar          *element_name,
1249                        const gchar         **attribute_names,
1250                        const gchar         **attribute_values,
1251                        gpointer              user_data,
1252                        GError              **error)
1253  {
1254    ParseData *data = user_data;
1255    GSList *stack;
1256    const gchar *name;
1257    const gchar *type;
1258    const gchar *access;
1259    const gchar *direction;
1260    const gchar *value;
1261  
1262    name = NULL;
1263    type = NULL;
1264    access = NULL;
1265    direction = NULL;
1266    value = NULL;
1267  
1268    stack = (GSList *) g_markup_parse_context_get_element_stack (context);
1269  
1270    /* ---------------------------------------------------------------------------------------------------- */
1271    if (strcmp (element_name, "node") == 0)
1272      {
1273        if (!(g_slist_length (stack) >= 1 || strcmp (stack->next->data, "node") != 0))
1274          {
1275            g_set_error_literal (error,
1276                                 G_MARKUP_ERROR,
1277                                 G_MARKUP_ERROR_INVALID_CONTENT,
1278                                 "<node> elements can only be top-level or embedded in other <node> elements");
1279            goto out;
1280          }
1281  
1282        if (!g_markup_collect_attributes (element_name,
1283                                          attribute_names,
1284                                          attribute_values,
1285                                          error,
1286                                          G_MARKUP_COLLECT_STRING | G_MARKUP_COLLECT_OPTIONAL, "name", &name,
1287                                          /* some hand-written introspection XML documents use this */
1288                                          G_MARKUP_COLLECT_STRING | G_MARKUP_COLLECT_OPTIONAL, "xmlns:doc", NULL,
1289                                          G_MARKUP_COLLECT_INVALID))
1290          goto out;
1291  
1292        g_dbus_node_info_set (data,
1293                              parse_data_get_node (data, TRUE),
1294                              name,
1295                              NULL,
1296                              NULL,
1297                              NULL);
1298  
1299        /* push the currently retrieved interfaces and nodes on the stack and prepare new arrays */
1300        data->interfaces_stack = g_slist_prepend (data->interfaces_stack, data->interfaces);
1301        data->interfaces = NULL;
1302        parse_data_steal_interfaces (data, NULL);
1303  
1304        data->nodes_stack = g_slist_prepend (data->nodes_stack, data->nodes);
1305        data->nodes = NULL;
1306        parse_data_steal_nodes (data, NULL);
1307  
1308      }
1309    /* ---------------------------------------------------------------------------------------------------- */
1310    else if (strcmp (element_name, "interface") == 0)
1311      {
1312        if (g_slist_length (stack) < 2 || strcmp (stack->next->data, "node") != 0)
1313          {
1314            g_set_error_literal (error,
1315                                 G_MARKUP_ERROR,
1316                                 G_MARKUP_ERROR_INVALID_CONTENT,
1317                                 "<interface> elements can only be embedded in <node> elements");
1318            goto out;
1319          }
1320  
1321        if (!g_markup_collect_attributes (element_name,
1322                                          attribute_names,
1323                                          attribute_values,
1324                                          error,
1325                                          G_MARKUP_COLLECT_STRING, "name", &name,
1326                                          /* seen in the wild */
1327                                          G_MARKUP_COLLECT_STRING | G_MARKUP_COLLECT_OPTIONAL, "version", NULL,
1328                                          G_MARKUP_COLLECT_INVALID))
1329          goto out;
1330  
1331        g_dbus_interface_info_set (data,
1332                                   parse_data_get_interface (data, TRUE),
1333                                   name,
1334                                   NULL,
1335                                   NULL,
1336                                   NULL,
1337                                   NULL);
1338  
1339      }
1340    /* ---------------------------------------------------------------------------------------------------- */
1341    else if (strcmp (element_name, "method") == 0)
1342      {
1343        if (g_slist_length (stack) < 2 || strcmp (stack->next->data, "interface") != 0)
1344          {
1345            g_set_error_literal (error,
1346                                 G_MARKUP_ERROR,
1347                                 G_MARKUP_ERROR_INVALID_CONTENT,
1348                                 "<method> elements can only be embedded in <interface> elements");
1349            goto out;
1350          }
1351  
1352        if (!g_markup_collect_attributes (element_name,
1353                                          attribute_names,
1354                                          attribute_values,
1355                                          error,
1356                                          G_MARKUP_COLLECT_STRING, "name", &name,
1357                                          /* seen in the wild */
1358                                          G_MARKUP_COLLECT_STRING | G_MARKUP_COLLECT_OPTIONAL, "version", NULL,
1359                                          G_MARKUP_COLLECT_INVALID))
1360          goto out;
1361  
1362        g_dbus_method_info_set (data,
1363                                parse_data_get_method (data, TRUE),
1364                                name,
1365                                NULL,
1366                                NULL,
1367                                NULL);
1368  
1369        data->num_args = 0;
1370  
1371      }
1372    /* ---------------------------------------------------------------------------------------------------- */
1373    else if (strcmp (element_name, "signal") == 0)
1374      {
1375        if (g_slist_length (stack) < 2 || strcmp (stack->next->data, "interface") != 0)
1376          {
1377            g_set_error_literal (error,
1378                                 G_MARKUP_ERROR,
1379                                 G_MARKUP_ERROR_INVALID_CONTENT,
1380                                 "<signal> elements can only be embedded in <interface> elements");
1381            goto out;
1382          }
1383  
1384        if (!g_markup_collect_attributes (element_name,
1385                                          attribute_names,
1386                                          attribute_values,
1387                                          error,
1388                                          G_MARKUP_COLLECT_STRING, "name", &name,
1389                                          G_MARKUP_COLLECT_INVALID))
1390          goto out;
1391  
1392        g_dbus_signal_info_set (data,
1393                                parse_data_get_signal (data, TRUE),
1394                                name,
1395                                NULL,
1396                                NULL);
1397  
1398        data->num_args = 0;
1399  
1400      }
1401    /* ---------------------------------------------------------------------------------------------------- */
1402    else if (strcmp (element_name, "property") == 0)
1403      {
1404        GDBusPropertyInfoFlags flags;
1405  
1406        if (g_slist_length (stack) < 2 || strcmp (stack->next->data, "interface") != 0)
1407          {
1408            g_set_error_literal (error,
1409                                 G_MARKUP_ERROR,
1410                                 G_MARKUP_ERROR_INVALID_CONTENT,
1411                                 "<property> elements can only be embedded in <interface> elements");
1412            goto out;
1413          }
1414  
1415        if (!g_markup_collect_attributes (element_name,
1416                                          attribute_names,
1417                                          attribute_values,
1418                                          error,
1419                                          G_MARKUP_COLLECT_STRING, "name", &name,
1420                                          G_MARKUP_COLLECT_STRING, "type", &type,
1421                                          G_MARKUP_COLLECT_STRING, "access", &access,
1422                                          G_MARKUP_COLLECT_INVALID))
1423          goto out;
1424  
1425        if (strcmp (access, "read") == 0)
1426          flags = G_DBUS_PROPERTY_INFO_FLAGS_READABLE;
1427        else if (strcmp (access, "write") == 0)
1428          flags = G_DBUS_PROPERTY_INFO_FLAGS_WRITABLE;
1429        else if (strcmp (access, "readwrite") == 0)
1430          flags = G_DBUS_PROPERTY_INFO_FLAGS_READABLE | G_DBUS_PROPERTY_INFO_FLAGS_WRITABLE;
1431        else
1432          {
1433            g_set_error (error,
1434                         G_MARKUP_ERROR,
1435                         G_MARKUP_ERROR_INVALID_CONTENT,
1436                         "Unknown value '%s' of access attribute for element <property>",
1437                         access);
1438            goto out;
1439          }
1440  
1441        g_dbus_property_info_set (data,
1442                                  parse_data_get_property (data, TRUE),
1443                                  name,
1444                                  type,
1445                                  flags,
1446                                  NULL);
1447  
1448      }
1449    /* ---------------------------------------------------------------------------------------------------- */
1450    else if (strcmp (element_name, "arg") == 0)
1451      {
1452        gboolean is_in;
1453        gchar *name_to_use;
1454  
1455        if (g_slist_length (stack) < 2 ||
1456            (strcmp (stack->next->data, "method") != 0 &&
1457             strcmp (stack->next->data, "signal") != 0))
1458          {
1459            g_set_error_literal (error,
1460                                 G_MARKUP_ERROR,
1461                                 G_MARKUP_ERROR_INVALID_CONTENT,
1462                                 "<arg> elements can only be embedded in <method> or <signal> elements");
1463            goto out;
1464          }
1465  
1466        if (!g_markup_collect_attributes (element_name,
1467                                          attribute_names,
1468                                          attribute_values,
1469                                          error,
1470                                          G_MARKUP_COLLECT_STRING | G_MARKUP_COLLECT_OPTIONAL, "name", &name,
1471                                          G_MARKUP_COLLECT_STRING | G_MARKUP_COLLECT_OPTIONAL, "direction", &direction,
1472                                          G_MARKUP_COLLECT_STRING, "type", &type,
1473                                          G_MARKUP_COLLECT_INVALID))
1474          goto out;
1475  
1476        if (strcmp (stack->next->data, "method") == 0)
1477          is_in = TRUE;
1478        else
1479          is_in = FALSE;
1480        if (direction != NULL)
1481          {
1482            if (strcmp (direction, "in") == 0)
1483              is_in = TRUE;
1484            else if (strcmp (direction, "out") == 0)
1485              is_in = FALSE;
1486            else
1487              {
1488                g_set_error (error,
1489                             G_MARKUP_ERROR,
1490                             G_MARKUP_ERROR_INVALID_CONTENT,
1491                             "Unknown value '%s' of direction attribute",
1492                             direction);
1493                goto out;
1494              }
1495          }
1496  
1497        if (is_in && strcmp (stack->next->data, "signal") == 0)
1498          {
1499            g_set_error_literal (error,
1500                                 G_MARKUP_ERROR,
1501                                 G_MARKUP_ERROR_INVALID_CONTENT,
1502                                 "Only direction 'out' is allowed for <arg> elements embedded in <signal>");
1503            goto out;
1504          }
1505  
1506        if (name == NULL)
1507          name_to_use = g_strdup_printf ("arg_%d", data->num_args);
1508        else
1509          name_to_use = g_strdup (name);
1510        data->num_args++;
1511  
1512        if (is_in)
1513          {
1514            g_dbus_arg_info_set (data,
1515                                 parse_data_get_arg (data, TRUE),
1516                                 name_to_use,
1517                                 type,
1518                                 NULL);
1519            data->last_arg_was_in = TRUE;
1520          }
1521        else
1522          {
1523            g_dbus_arg_info_set (data,
1524                                 parse_data_get_out_arg (data, TRUE),
1525                                 name_to_use,
1526                                 type,
1527                                 NULL);
1528            data->last_arg_was_in = FALSE;
1529  
1530          }
1531  
1532        g_free (name_to_use);
1533      }
1534    /* ---------------------------------------------------------------------------------------------------- */
1535    else if (strcmp (element_name, "annotation") == 0)
1536      {
1537        if (g_slist_length (stack) < 2 ||
1538            (strcmp (stack->next->data, "node") != 0 &&
1539             strcmp (stack->next->data, "interface") != 0 &&
1540             strcmp (stack->next->data, "signal") != 0 &&
1541             strcmp (stack->next->data, "method") != 0 &&
1542             strcmp (stack->next->data, "property") != 0 &&
1543             strcmp (stack->next->data, "arg") != 0 &&
1544             strcmp (stack->next->data, "annotation") != 0))
1545          {
1546            g_set_error_literal (error,
1547                                 G_MARKUP_ERROR,
1548                                 G_MARKUP_ERROR_INVALID_CONTENT,
1549                                 "<annotation> elements can only be embedded in <node>, <interface>, <signal>, <method>, <property>, <arg> or <annotation> elements");
1550            goto out;
1551          }
1552  
1553        if (!g_markup_collect_attributes (element_name,
1554                                          attribute_names,
1555                                          attribute_values,
1556                                          error,
1557                                          G_MARKUP_COLLECT_STRING, "name", &name,
1558                                          G_MARKUP_COLLECT_STRING, "value", &value,
1559                                          G_MARKUP_COLLECT_INVALID))
1560          goto out;
1561  
1562        g_dbus_annotation_info_set (data,
1563                                    parse_data_get_annotation (data, TRUE),
1564                                    name,
1565                                    value,
1566                                    NULL);
1567      }
1568    /* ---------------------------------------------------------------------------------------------------- */
1569    else
1570      {
1571        /* don't bail on unknown elements; just ignore them */
1572      }
1573    /* ---------------------------------------------------------------------------------------------------- */
1574  
1575    /* push the currently retrieved annotations on the stack and prepare a new one */
1576    data->annotations_stack = g_slist_prepend (data->annotations_stack, data->annotations);
1577    data->annotations = NULL;
1578    parse_data_steal_annotations (data, NULL);
1579  
1580   out:
1581    ;
1582  }
1583  
1584  /* ---------------------------------------------------------------------------------------------------- */
1585  
1586  static GDBusAnnotationInfo **
steal_annotations(ParseData * data)1587  steal_annotations (ParseData *data)
1588  {
1589    return parse_data_steal_annotations (data, NULL);
1590  }
1591  
1592  
1593  static void
parser_end_element(GMarkupParseContext * context,const gchar * element_name,gpointer user_data,GError ** error)1594  parser_end_element (GMarkupParseContext  *context,
1595                      const gchar          *element_name,
1596                      gpointer              user_data,
1597                      GError              **error)
1598  {
1599    ParseData *data = user_data;
1600    gboolean have_popped_annotations;
1601  
1602    have_popped_annotations = FALSE;
1603  
1604    if (strcmp (element_name, "node") == 0)
1605      {
1606        guint num_nodes;
1607        guint num_interfaces;
1608        GDBusNodeInfo **nodes;
1609        GDBusInterfaceInfo **interfaces;
1610  
1611        nodes = parse_data_steal_nodes (data, &num_nodes);
1612        interfaces = parse_data_steal_interfaces (data, &num_interfaces);
1613  
1614        /* destroy the nodes, interfaces for scope we're exiting and and pop the nodes, interfaces from the
1615         * scope we're reentering
1616         */
1617        parse_data_free_interfaces (data);
1618        data->interfaces = (GPtrArray *) data->interfaces_stack->data;
1619        data->interfaces_stack = g_slist_remove (data->interfaces_stack, data->interfaces_stack->data);
1620  
1621        parse_data_free_nodes (data);
1622        data->nodes = (GPtrArray *) data->nodes_stack->data;
1623        data->nodes_stack = g_slist_remove (data->nodes_stack, data->nodes_stack->data);
1624  
1625        g_dbus_node_info_set (data,
1626                              parse_data_get_node (data, FALSE),
1627                              NULL,
1628                              interfaces,
1629                              nodes,
1630                              steal_annotations (data));
1631  
1632      }
1633    else if (strcmp (element_name, "interface") == 0)
1634      {
1635        guint num_methods;
1636        guint num_signals;
1637        guint num_properties;
1638        GDBusMethodInfo **methods;
1639        GDBusSignalInfo **signals;
1640        GDBusPropertyInfo **properties;
1641  
1642        methods    = parse_data_steal_methods    (data, &num_methods);
1643        signals    = parse_data_steal_signals    (data, &num_signals);
1644        properties = parse_data_steal_properties (data, &num_properties);
1645  
1646        g_dbus_interface_info_set (data,
1647                                   parse_data_get_interface (data, FALSE),
1648                                   NULL,
1649                                   methods,
1650                                   signals,
1651                                   properties,
1652                                   steal_annotations (data));
1653  
1654      }
1655    else if (strcmp (element_name, "method") == 0)
1656      {
1657        guint in_num_args;
1658        guint out_num_args;
1659        GDBusArgInfo **in_args;
1660        GDBusArgInfo **out_args;
1661  
1662        in_args  = parse_data_steal_args     (data, &in_num_args);
1663        out_args = parse_data_steal_out_args (data, &out_num_args);
1664  
1665        g_dbus_method_info_set (data,
1666                                parse_data_get_method (data, FALSE),
1667                                NULL,
1668                                in_args,
1669                                out_args,
1670                                steal_annotations (data));
1671      }
1672    else if (strcmp (element_name, "signal") == 0)
1673      {
1674        guint num_args;
1675        GDBusArgInfo **args;
1676  
1677        args = parse_data_steal_out_args (data, &num_args);
1678  
1679        g_dbus_signal_info_set (data,
1680                                parse_data_get_signal (data, FALSE),
1681                                NULL,
1682                                args,
1683                                steal_annotations (data));
1684      }
1685    else if (strcmp (element_name, "property") == 0)
1686      {
1687        g_dbus_property_info_set (data,
1688                                  parse_data_get_property (data, FALSE),
1689                                  NULL,
1690                                  NULL,
1691                                  G_DBUS_PROPERTY_INFO_FLAGS_NONE,
1692                                  steal_annotations (data));
1693      }
1694    else if (strcmp (element_name, "arg") == 0)
1695      {
1696        g_dbus_arg_info_set (data,
1697                             data->last_arg_was_in ? parse_data_get_arg (data, FALSE) : parse_data_get_out_arg (data, FALSE),
1698                             NULL,
1699                             NULL,
1700                             steal_annotations (data));
1701      }
1702    else if (strcmp (element_name, "annotation") == 0)
1703      {
1704        GDBusAnnotationInfo **embedded_annotations;
1705  
1706        embedded_annotations = steal_annotations (data);
1707  
1708        /* destroy the annotations for scope we're exiting and and pop the annotations from the scope we're reentering */
1709        parse_data_free_annotations (data);
1710        data->annotations = (GPtrArray *) data->annotations_stack->data;
1711        data->annotations_stack = g_slist_remove (data->annotations_stack, data->annotations_stack->data);
1712  
1713        have_popped_annotations = TRUE;
1714  
1715        g_dbus_annotation_info_set (data,
1716                                    parse_data_get_annotation (data, FALSE),
1717                                    NULL,
1718                                    NULL,
1719                                    embedded_annotations);
1720      }
1721    else
1722      {
1723        /* don't bail on unknown elements; just ignore them */
1724      }
1725  
1726    if (!have_popped_annotations)
1727      {
1728        /* destroy the annotations for scope we're exiting and and pop the annotations from the scope we're reentering */
1729        parse_data_free_annotations (data);
1730        data->annotations = (GPtrArray *) data->annotations_stack->data;
1731        data->annotations_stack = g_slist_remove (data->annotations_stack, data->annotations_stack->data);
1732      }
1733  }
1734  
1735  /* ---------------------------------------------------------------------------------------------------- */
1736  
1737  static void
parser_error(GMarkupParseContext * context,GError * error,gpointer user_data)1738  parser_error (GMarkupParseContext *context,
1739                GError              *error,
1740                gpointer             user_data)
1741  {
1742    gint line_number;
1743    gint char_number;
1744  
1745    g_markup_parse_context_get_position (context, &line_number, &char_number);
1746  
1747    g_prefix_error (&error, "%d:%d: ",
1748                    line_number,
1749                    char_number);
1750  }
1751  
1752  /* ---------------------------------------------------------------------------------------------------- */
1753  
1754  /**
1755   * g_dbus_node_info_new_for_xml:
1756   * @xml_data: Valid D-Bus introspection XML.
1757   * @error: Return location for error.
1758   *
1759   * Parses @xml_data and returns a #GDBusNodeInfo representing the data.
1760   *
1761   * The introspection XML must contain exactly one top-level
1762   * <node> element.
1763   *
1764   * Note that this routine is using a
1765   * [GMarkup][glib-Simple-XML-Subset-Parser.description]-based
1766   * parser that only accepts a subset of valid XML documents.
1767   *
1768   * Returns: A #GDBusNodeInfo structure or %NULL if @error is set. Free
1769   * with g_dbus_node_info_unref().
1770   *
1771   * Since: 2.26
1772   */
1773  GDBusNodeInfo *
g_dbus_node_info_new_for_xml(const gchar * xml_data,GError ** error)1774  g_dbus_node_info_new_for_xml (const gchar  *xml_data,
1775                                GError      **error)
1776  {
1777    GDBusNodeInfo *ret;
1778    GMarkupParseContext *context;
1779    GMarkupParser *parser;
1780    guint num_nodes;
1781    ParseData *data;
1782    GDBusNodeInfo **ughret;
1783  
1784    ret = NULL;
1785    parser = NULL;
1786    context = NULL;
1787  
1788    parser = g_new0 (GMarkupParser, 1);
1789    parser->start_element = parser_start_element;
1790    parser->end_element   = parser_end_element;
1791    parser->error         = parser_error;
1792  
1793    data = parse_data_new ();
1794    context = g_markup_parse_context_new (parser,
1795                                          G_MARKUP_IGNORE_QUALIFIED,
1796                                          data,
1797                                          (GDestroyNotify) parse_data_free);
1798  
1799    if (!g_markup_parse_context_parse (context,
1800                                       xml_data,
1801                                       strlen (xml_data),
1802                                       error))
1803      goto out;
1804  
1805    if (!g_markup_parse_context_end_parse (context, error))
1806      goto out;
1807  
1808    ughret = parse_data_steal_nodes (data, &num_nodes);
1809  
1810    if (num_nodes != 1)
1811      {
1812        guint n;
1813  
1814        g_set_error (error,
1815                     G_MARKUP_ERROR,
1816                     G_MARKUP_ERROR_INVALID_CONTENT,
1817                     "Expected a single node in introspection XML, found %d",
1818                     num_nodes);
1819  
1820        /* clean up */
1821        for (n = 0; n < num_nodes; n++)
1822          {
1823            g_dbus_node_info_unref (ughret[n]);
1824            ughret[n] = NULL;
1825          }
1826      }
1827  
1828    ret = ughret[0];
1829    g_free (ughret);
1830  
1831   out:
1832    g_free (parser);
1833    if (context != NULL)
1834      g_markup_parse_context_free (context);
1835  
1836    return ret;
1837  }
1838  
1839  /* ---------------------------------------------------------------------------------------------------- */
1840  
1841  /**
1842   * g_dbus_annotation_info_lookup:
1843   * @annotations: (array zero-terminated=1) (nullable): A %NULL-terminated array of annotations or %NULL.
1844   * @name: The name of the annotation to look up.
1845   *
1846   * Looks up the value of an annotation.
1847   *
1848   * The cost of this function is O(n) in number of annotations.
1849   *
1850   * Returns: (nullable): The value or %NULL if not found. Do not free, it is owned by @annotations.
1851   *
1852   * Since: 2.26
1853   */
1854  const gchar *
g_dbus_annotation_info_lookup(GDBusAnnotationInfo ** annotations,const gchar * name)1855  g_dbus_annotation_info_lookup (GDBusAnnotationInfo **annotations,
1856                                 const gchar          *name)
1857  {
1858    guint n;
1859    const gchar *ret;
1860  
1861    ret = NULL;
1862    for (n = 0; annotations != NULL && annotations[n] != NULL; n++)
1863      {
1864        if (g_strcmp0 (annotations[n]->key, name) == 0)
1865          {
1866            ret = annotations[n]->value;
1867            goto out;
1868          }
1869      }
1870  
1871   out:
1872    return ret;
1873  }
1874  
1875  /* ---------------------------------------------------------------------------------------------------- */
1876  
1877  G_LOCK_DEFINE_STATIC (info_cache_lock);
1878  
1879  typedef struct
1880  {
1881    gint use_count;
1882  
1883    /* gchar* -> GDBusMethodInfo* */
1884    GHashTable *method_name_to_data;
1885  
1886    /* gchar* -> GDBusMethodInfo* */
1887    GHashTable *signal_name_to_data;
1888  
1889    /* gchar* -> GDBusMethodInfo* */
1890    GHashTable *property_name_to_data;
1891  } InfoCacheEntry;
1892  
1893  static void
info_cache_free(InfoCacheEntry * cache)1894  info_cache_free (InfoCacheEntry *cache)
1895  {
1896    g_assert (cache->use_count == 0);
1897    g_hash_table_unref (cache->method_name_to_data);
1898    g_hash_table_unref (cache->signal_name_to_data);
1899    g_hash_table_unref (cache->property_name_to_data);
1900    g_slice_free (InfoCacheEntry, cache);
1901  }
1902  
1903  /* maps from GDBusInterfaceInfo* to InfoCacheEntry* */
1904  static GHashTable *info_cache = NULL;
1905  
1906  /* ---------------------------------------------------------------------------------------------------- */
1907  
1908  /**
1909   * g_dbus_interface_info_lookup_method:
1910   * @info: A #GDBusInterfaceInfo.
1911   * @name: A D-Bus method name (typically in CamelCase)
1912   *
1913   * Looks up information about a method.
1914   *
1915   * The cost of this function is O(n) in number of methods unless
1916   * g_dbus_interface_info_cache_build() has been used on @info.
1917   *
1918   * Returns: (nullable) (transfer none): A #GDBusMethodInfo or %NULL if not found. Do not free, it is owned by @info.
1919   *
1920   * Since: 2.26
1921   */
1922  GDBusMethodInfo *
g_dbus_interface_info_lookup_method(GDBusInterfaceInfo * info,const gchar * name)1923  g_dbus_interface_info_lookup_method (GDBusInterfaceInfo *info,
1924                                       const gchar        *name)
1925  {
1926    guint n;
1927    GDBusMethodInfo *result;
1928  
1929    G_LOCK (info_cache_lock);
1930    if (G_LIKELY (info_cache != NULL))
1931      {
1932        InfoCacheEntry *cache;
1933        cache = g_hash_table_lookup (info_cache, info);
1934        if (G_LIKELY (cache != NULL))
1935          {
1936            result = g_hash_table_lookup (cache->method_name_to_data, name);
1937            G_UNLOCK (info_cache_lock);
1938            goto out;
1939          }
1940      }
1941    G_UNLOCK (info_cache_lock);
1942  
1943    for (n = 0; info->methods != NULL && info->methods[n] != NULL; n++)
1944      {
1945        GDBusMethodInfo *i = info->methods[n];
1946  
1947        if (g_strcmp0 (i->name, name) == 0)
1948          {
1949            result = i;
1950            goto out;
1951          }
1952      }
1953  
1954    result = NULL;
1955  
1956   out:
1957    return result;
1958  }
1959  
1960  /* ---------------------------------------------------------------------------------------------------- */
1961  
1962  /**
1963   * g_dbus_interface_info_lookup_signal:
1964   * @info: A #GDBusInterfaceInfo.
1965   * @name: A D-Bus signal name (typically in CamelCase)
1966   *
1967   * Looks up information about a signal.
1968   *
1969   * The cost of this function is O(n) in number of signals unless
1970   * g_dbus_interface_info_cache_build() has been used on @info.
1971   *
1972   * Returns: (nullable) (transfer none): A #GDBusSignalInfo or %NULL if not found. Do not free, it is owned by @info.
1973   *
1974   * Since: 2.26
1975   */
1976  GDBusSignalInfo *
g_dbus_interface_info_lookup_signal(GDBusInterfaceInfo * info,const gchar * name)1977  g_dbus_interface_info_lookup_signal (GDBusInterfaceInfo *info,
1978                                       const gchar        *name)
1979  {
1980    guint n;
1981    GDBusSignalInfo *result;
1982  
1983    G_LOCK (info_cache_lock);
1984    if (G_LIKELY (info_cache != NULL))
1985      {
1986        InfoCacheEntry *cache;
1987        cache = g_hash_table_lookup (info_cache, info);
1988        if (G_LIKELY (cache != NULL))
1989          {
1990            result = g_hash_table_lookup (cache->signal_name_to_data, name);
1991            G_UNLOCK (info_cache_lock);
1992            goto out;
1993          }
1994      }
1995    G_UNLOCK (info_cache_lock);
1996  
1997    for (n = 0; info->signals != NULL && info->signals[n] != NULL; n++)
1998      {
1999        GDBusSignalInfo *i = info->signals[n];
2000  
2001        if (g_strcmp0 (i->name, name) == 0)
2002          {
2003            result = i;
2004            goto out;
2005          }
2006      }
2007  
2008    result = NULL;
2009  
2010   out:
2011    return result;
2012  }
2013  
2014  /* ---------------------------------------------------------------------------------------------------- */
2015  
2016  /**
2017   * g_dbus_interface_info_lookup_property:
2018   * @info: A #GDBusInterfaceInfo.
2019   * @name: A D-Bus property name (typically in CamelCase).
2020   *
2021   * Looks up information about a property.
2022   *
2023   * The cost of this function is O(n) in number of properties unless
2024   * g_dbus_interface_info_cache_build() has been used on @info.
2025   *
2026   * Returns: (nullable) (transfer none): A #GDBusPropertyInfo or %NULL if not found. Do not free, it is owned by @info.
2027   *
2028   * Since: 2.26
2029   */
2030  GDBusPropertyInfo *
g_dbus_interface_info_lookup_property(GDBusInterfaceInfo * info,const gchar * name)2031  g_dbus_interface_info_lookup_property (GDBusInterfaceInfo *info,
2032                                         const gchar        *name)
2033  {
2034    guint n;
2035    GDBusPropertyInfo *result;
2036  
2037    G_LOCK (info_cache_lock);
2038    if (G_LIKELY (info_cache != NULL))
2039      {
2040        InfoCacheEntry *cache;
2041        cache = g_hash_table_lookup (info_cache, info);
2042        if (G_LIKELY (cache != NULL))
2043          {
2044            result = g_hash_table_lookup (cache->property_name_to_data, name);
2045            G_UNLOCK (info_cache_lock);
2046            goto out;
2047          }
2048      }
2049    G_UNLOCK (info_cache_lock);
2050  
2051    for (n = 0; info->properties != NULL && info->properties[n] != NULL; n++)
2052      {
2053        GDBusPropertyInfo *i = info->properties[n];
2054  
2055        if (g_strcmp0 (i->name, name) == 0)
2056          {
2057            result = i;
2058            goto out;
2059          }
2060      }
2061  
2062    result = NULL;
2063  
2064   out:
2065    return result;
2066  }
2067  
2068  /* ---------------------------------------------------------------------------------------------------- */
2069  
2070  /**
2071   * g_dbus_interface_info_cache_build:
2072   * @info: A #GDBusInterfaceInfo.
2073   *
2074   * Builds a lookup-cache to speed up
2075   * g_dbus_interface_info_lookup_method(),
2076   * g_dbus_interface_info_lookup_signal() and
2077   * g_dbus_interface_info_lookup_property().
2078   *
2079   * If this has already been called with @info, the existing cache is
2080   * used and its use count is increased.
2081   *
2082   * Note that @info cannot be modified until
2083   * g_dbus_interface_info_cache_release() is called.
2084   *
2085   * Since: 2.30
2086   */
2087  void
g_dbus_interface_info_cache_build(GDBusInterfaceInfo * info)2088  g_dbus_interface_info_cache_build (GDBusInterfaceInfo *info)
2089  {
2090    InfoCacheEntry *cache;
2091    guint n;
2092  
2093    G_LOCK (info_cache_lock);
2094    if (info_cache == NULL)
2095      info_cache = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) info_cache_free);
2096    cache = g_hash_table_lookup (info_cache, info);
2097    if (cache != NULL)
2098      {
2099        cache->use_count += 1;
2100        goto out;
2101      }
2102    cache = g_slice_new0 (InfoCacheEntry);
2103    cache->use_count = 1;
2104    cache->method_name_to_data = g_hash_table_new (g_str_hash, g_str_equal);
2105    cache->signal_name_to_data = g_hash_table_new (g_str_hash, g_str_equal);
2106    cache->property_name_to_data = g_hash_table_new (g_str_hash, g_str_equal);
2107    for (n = 0; info->methods != NULL && info->methods[n] != NULL; n++)
2108      g_hash_table_insert (cache->method_name_to_data, info->methods[n]->name, info->methods[n]);
2109    for (n = 0; info->signals != NULL && info->signals[n] != NULL; n++)
2110      g_hash_table_insert (cache->signal_name_to_data, info->signals[n]->name, info->signals[n]);
2111    for (n = 0; info->properties != NULL && info->properties[n] != NULL; n++)
2112      g_hash_table_insert (cache->property_name_to_data, info->properties[n]->name, info->properties[n]);
2113    g_hash_table_insert (info_cache, info, cache);
2114   out:
2115    G_UNLOCK (info_cache_lock);
2116  }
2117  
2118  /**
2119   * g_dbus_interface_info_cache_release:
2120   * @info: A GDBusInterfaceInfo
2121   *
2122   * Decrements the usage count for the cache for @info built by
2123   * g_dbus_interface_info_cache_build() (if any) and frees the
2124   * resources used by the cache if the usage count drops to zero.
2125   *
2126   * Since: 2.30
2127   */
2128  void
g_dbus_interface_info_cache_release(GDBusInterfaceInfo * info)2129  g_dbus_interface_info_cache_release (GDBusInterfaceInfo *info)
2130  {
2131    InfoCacheEntry *cache;
2132  
2133    G_LOCK (info_cache_lock);
2134    if (G_UNLIKELY (info_cache == NULL))
2135      {
2136        g_warning ("%s called for interface %s but there is no cache", info->name, G_STRFUNC);
2137        goto out;
2138      }
2139  
2140    cache = g_hash_table_lookup (info_cache, info);
2141    if (G_UNLIKELY (cache == NULL))
2142      {
2143        g_warning ("%s called for interface %s but there is no cache entry", info->name, G_STRFUNC);
2144        goto out;
2145      }
2146    cache->use_count -= 1;
2147    if (cache->use_count == 0)
2148      {
2149        g_hash_table_remove (info_cache, info);
2150        /* could nuke info_cache itself if empty */
2151      }
2152   out:
2153    G_UNLOCK (info_cache_lock);
2154  }
2155  
2156  
2157  /* ---------------------------------------------------------------------------------------------------- */
2158  
2159  /**
2160   * g_dbus_node_info_lookup_interface:
2161   * @info: A #GDBusNodeInfo.
2162   * @name: A D-Bus interface name.
2163   *
2164   * Looks up information about an interface.
2165   *
2166   * The cost of this function is O(n) in number of interfaces.
2167   *
2168   * Returns: (nullable) (transfer none): A #GDBusInterfaceInfo or %NULL if not found. Do not free, it is owned by @info.
2169   *
2170   * Since: 2.26
2171   */
2172  GDBusInterfaceInfo *
g_dbus_node_info_lookup_interface(GDBusNodeInfo * info,const gchar * name)2173  g_dbus_node_info_lookup_interface (GDBusNodeInfo *info,
2174                                     const gchar   *name)
2175  {
2176    guint n;
2177    GDBusInterfaceInfo *result;
2178  
2179    for (n = 0; info->interfaces != NULL && info->interfaces[n] != NULL; n++)
2180      {
2181        GDBusInterfaceInfo *i = info->interfaces[n];
2182  
2183        if (g_strcmp0 (i->name, name) == 0)
2184          {
2185            result = i;
2186            goto out;
2187          }
2188      }
2189  
2190    result = NULL;
2191  
2192   out:
2193    return result;
2194  }
2195