Coverage Report

Created: 2025-07-11 07:03

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