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 * |
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 * |
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 * |
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 * |
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 * |
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 * |
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 * |
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 |
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 (mem: 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 |
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 (mem: info->key); |
266 | g_free (mem: info->value); |
267 | free_null_terminated_array (array: info->annotations, unref_func: (GDestroyNotify) g_dbus_annotation_info_unref); |
268 | g_free (mem: 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 |
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 (mem: info->name); |
290 | g_free (mem: info->signature); |
291 | free_null_terminated_array (array: info->annotations, unref_func: (GDestroyNotify) g_dbus_annotation_info_unref); |
292 | g_free (mem: 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 |
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 (mem: info->name); |
314 | free_null_terminated_array (array: info->in_args, unref_func: (GDestroyNotify) g_dbus_arg_info_unref); |
315 | free_null_terminated_array (array: info->out_args, unref_func: (GDestroyNotify) g_dbus_arg_info_unref); |
316 | free_null_terminated_array (array: info->annotations, unref_func: (GDestroyNotify) g_dbus_annotation_info_unref); |
317 | g_free (mem: 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 |
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 (mem: info->name); |
339 | free_null_terminated_array (array: info->args, unref_func: (GDestroyNotify) g_dbus_arg_info_unref); |
340 | free_null_terminated_array (array: info->annotations, unref_func: (GDestroyNotify) g_dbus_annotation_info_unref); |
341 | g_free (mem: 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 |
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 (mem: info->name); |
363 | g_free (mem: info->signature); |
364 | free_null_terminated_array (array: info->annotations, unref_func: (GDestroyNotify) g_dbus_annotation_info_unref); |
365 | g_free (mem: 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 |
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 (mem: info->name); |
387 | free_null_terminated_array (array: info->methods, unref_func: (GDestroyNotify) g_dbus_method_info_unref); |
388 | free_null_terminated_array (array: info->signals, unref_func: (GDestroyNotify) g_dbus_signal_info_unref); |
389 | free_null_terminated_array (array: info->properties, unref_func: (GDestroyNotify) g_dbus_property_info_unref); |
390 | free_null_terminated_array (array: info->annotations, unref_func: (GDestroyNotify) g_dbus_annotation_info_unref); |
391 | g_free (mem: 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 |
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 (mem: info->path); |
413 | free_null_terminated_array (array: info->interfaces, unref_func: (GDestroyNotify) g_dbus_interface_info_unref); |
414 | free_null_terminated_array (array: info->nodes, unref_func: (GDestroyNotify) g_dbus_node_info_unref); |
415 | free_null_terminated_array (array: info->annotations, unref_func: (GDestroyNotify) g_dbus_annotation_info_unref); |
416 | g_free (mem: info); |
417 | } |
418 | } |
419 | |
420 | /* ---------------------------------------------------------------------------------------------------- */ |
421 | |
422 | static void |
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 (str: key); |
433 | |
434 | if (value != NULL) |
435 | info->value = g_strdup (str: value); |
436 | |
437 | if (embedded_annotations != NULL) |
438 | info->annotations = embedded_annotations; |
439 | } |
440 | |
441 | static void |
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 (str: name); |
453 | |
454 | if (signature != NULL) |
455 | info->signature = g_strdup (str: signature); |
456 | |
457 | if (annotations != NULL) |
458 | info->annotations = annotations; |
459 | } |
460 | |
461 | static void |
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 (str: 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 |
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 (str: name); |
495 | |
496 | if (args != NULL) |
497 | info->args = args; |
498 | |
499 | if (annotations != NULL) |
500 | info->annotations = annotations; |
501 | } |
502 | |
503 | static void |
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 (str: 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 (str: signature); |
521 | |
522 | if (annotations != NULL) |
523 | info->annotations = annotations; |
524 | } |
525 | |
526 | static void |
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 (str: 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 |
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 (str: 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 |
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 (format: "%*s<annotation name=\"%s\" value=\"%s\"" , |
590 | indent, "" , |
591 | info->key, |
592 | info->value); |
593 | g_string_append (string: string_builder, val: tmp); |
594 | g_free (mem: tmp); |
595 | |
596 | if (info->annotations == NULL) |
597 | { |
598 | g_string_append (string: string_builder, val: "/>\n" ); |
599 | } |
600 | else |
601 | { |
602 | g_string_append (string: string_builder, val: ">\n" ); |
603 | |
604 | for (n = 0; info->annotations != NULL && info->annotations[n] != NULL; n++) |
605 | g_dbus_annotation_info_generate_xml (info: info->annotations[n], |
606 | indent: indent + 2, |
607 | string_builder); |
608 | |
609 | g_string_append_printf (string: string_builder, format: "%*s</annotation>\n" , |
610 | indent, "" ); |
611 | } |
612 | |
613 | } |
614 | |
615 | static void |
616 | g_dbus_arg_info_generate_xml (GDBusArgInfo *info, |
617 | guint indent, |
618 | const gchar *, |
619 | GString *string_builder) |
620 | { |
621 | guint n; |
622 | |
623 | g_string_append_printf (string: string_builder, format: "%*s<arg type=\"%s\"" , |
624 | indent, "" , |
625 | info->signature); |
626 | |
627 | if (info->name != NULL) |
628 | g_string_append_printf (string: string_builder, format: " name=\"%s\"" , info->name); |
629 | |
630 | if (extra_attributes != NULL) |
631 | g_string_append_printf (string: string_builder, format: " %s" , extra_attributes); |
632 | |
633 | if (info->annotations == NULL) |
634 | { |
635 | g_string_append (string: string_builder, val: "/>\n" ); |
636 | } |
637 | else |
638 | { |
639 | g_string_append (string: string_builder, val: ">\n" ); |
640 | |
641 | for (n = 0; info->annotations != NULL && info->annotations[n] != NULL; n++) |
642 | g_dbus_annotation_info_generate_xml (info: info->annotations[n], |
643 | indent: indent + 2, |
644 | string_builder); |
645 | |
646 | g_string_append_printf (string: string_builder, format: "%*s</arg>\n" , indent, "" ); |
647 | } |
648 | |
649 | } |
650 | |
651 | static void |
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: string_builder, format: "%*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: string_builder, val: "/>\n" ); |
665 | } |
666 | else |
667 | { |
668 | g_string_append (string: string_builder, val: ">\n" ); |
669 | |
670 | for (n = 0; info->annotations != NULL && info->annotations[n] != NULL; n++) |
671 | g_dbus_annotation_info_generate_xml (info: info->annotations[n], |
672 | indent: 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: info->in_args[n], |
677 | indent: indent + 2, |
678 | extra_attributes: "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: info->out_args[n], |
683 | indent: indent + 2, |
684 | extra_attributes: "direction=\"out\"" , |
685 | string_builder); |
686 | |
687 | g_string_append_printf (string: string_builder, format: "%*s</method>\n" , indent, "" ); |
688 | } |
689 | } |
690 | |
691 | static void |
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: string_builder, format: "%*s<signal name=\"%s\"" , |
699 | indent, "" , |
700 | info->name); |
701 | |
702 | if (info->annotations == NULL && info->args == NULL) |
703 | { |
704 | g_string_append (string: string_builder, val: "/>\n" ); |
705 | } |
706 | else |
707 | { |
708 | g_string_append (string: string_builder, val: ">\n" ); |
709 | |
710 | for (n = 0; info->annotations != NULL && info->annotations[n] != NULL; n++) |
711 | g_dbus_annotation_info_generate_xml (info: info->annotations[n], |
712 | indent: 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: info->args[n], |
717 | indent: indent + 2, |
718 | NULL, |
719 | string_builder); |
720 | |
721 | g_string_append_printf (string: string_builder, format: "%*s</signal>\n" , indent, "" ); |
722 | } |
723 | } |
724 | |
725 | static void |
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: string_builder, format: "%*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: string_builder, val: "/>\n" ); |
760 | } |
761 | else |
762 | { |
763 | g_string_append (string: string_builder, val: ">\n" ); |
764 | |
765 | for (n = 0; info->annotations != NULL && info->annotations[n] != NULL; n++) |
766 | g_dbus_annotation_info_generate_xml (info: info->annotations[n], |
767 | indent: indent + 2, |
768 | string_builder); |
769 | |
770 | g_string_append_printf (string: string_builder, format: "%*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 |
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: string_builder, format: "%*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: info->annotations[n], |
803 | indent: 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: info->methods[n], |
808 | indent: 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: info->signals[n], |
813 | indent: 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: info->properties[n], |
818 | indent: indent + 2, |
819 | string_builder); |
820 | |
821 | g_string_append_printf (string: string_builder, format: "%*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 |
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: string_builder, format: "%*s<node" , indent, "" ); |
845 | if (info->path != NULL) |
846 | g_string_append_printf (string: string_builder, format: " name=\"%s\"" , info->path); |
847 | |
848 | if (info->interfaces == NULL && info->nodes == NULL && info->annotations == NULL) |
849 | { |
850 | g_string_append (string: string_builder, val: "/>\n" ); |
851 | } |
852 | else |
853 | { |
854 | g_string_append (string: string_builder, val: ">\n" ); |
855 | |
856 | for (n = 0; info->annotations != NULL && info->annotations[n] != NULL; n++) |
857 | g_dbus_annotation_info_generate_xml (info: info->annotations[n], |
858 | indent: 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: info->interfaces[n], |
863 | indent: 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: info->nodes[n], |
868 | indent: indent + 2, |
869 | string_builder); |
870 | |
871 | g_string_append_printf (string: string_builder, format: "%*s</node>\n" , indent, "" ); |
872 | } |
873 | } |
874 | |
875 | /* ---------------------------------------------------------------------------------------------------- */ |
876 | |
877 | static GDBusAnnotationInfo ** |
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 (array: data->annotations, NULL); |
889 | ret = (GDBusAnnotationInfo **) g_ptr_array_free (array: data->annotations, FALSE); |
890 | } |
891 | data->annotations = g_ptr_array_new (); |
892 | return ret; |
893 | } |
894 | |
895 | static GDBusArgInfo ** |
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 (array: data->args, NULL); |
907 | ret = (GDBusArgInfo **) g_ptr_array_free (array: data->args, FALSE); |
908 | } |
909 | data->args = g_ptr_array_new (); |
910 | return ret; |
911 | } |
912 | |
913 | static GDBusArgInfo ** |
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 (array: data->out_args, NULL); |
925 | ret = (GDBusArgInfo **) g_ptr_array_free (array: data->out_args, FALSE); |
926 | } |
927 | data->out_args = g_ptr_array_new (); |
928 | return ret; |
929 | } |
930 | |
931 | static GDBusMethodInfo ** |
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 (array: data->methods, NULL); |
943 | ret = (GDBusMethodInfo **) g_ptr_array_free (array: data->methods, FALSE); |
944 | } |
945 | data->methods = g_ptr_array_new (); |
946 | return ret; |
947 | } |
948 | |
949 | static GDBusSignalInfo ** |
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 (array: data->signals, NULL); |
961 | ret = (GDBusSignalInfo **) g_ptr_array_free (array: data->signals, FALSE); |
962 | } |
963 | data->signals = g_ptr_array_new (); |
964 | return ret; |
965 | } |
966 | |
967 | static GDBusPropertyInfo ** |
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 (array: data->properties, NULL); |
979 | ret = (GDBusPropertyInfo **) g_ptr_array_free (array: data->properties, FALSE); |
980 | } |
981 | data->properties = g_ptr_array_new (); |
982 | return ret; |
983 | } |
984 | |
985 | static GDBusInterfaceInfo ** |
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 (array: data->interfaces, NULL); |
997 | ret = (GDBusInterfaceInfo **) g_ptr_array_free (array: data->interfaces, FALSE); |
998 | } |
999 | data->interfaces = g_ptr_array_new (); |
1000 | return ret; |
1001 | } |
1002 | |
1003 | static GDBusNodeInfo ** |
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 (array: data->nodes, NULL); |
1015 | ret = (GDBusNodeInfo **) g_ptr_array_free (array: data->nodes, FALSE); |
1016 | } |
1017 | data->nodes = g_ptr_array_new (); |
1018 | return ret; |
1019 | } |
1020 | |
1021 | /* ---------------------------------------------------------------------------------------------------- */ |
1022 | |
1023 | static void |
1024 | parse_data_free_annotations (ParseData *data) |
1025 | { |
1026 | if (data->annotations == NULL) |
1027 | return; |
1028 | g_ptr_array_foreach (array: data->annotations, func: (GFunc) g_dbus_annotation_info_unref, NULL); |
1029 | g_ptr_array_free (array: data->annotations, TRUE); |
1030 | data->annotations = NULL; |
1031 | } |
1032 | |
1033 | static void |
1034 | parse_data_free_args (ParseData *data) |
1035 | { |
1036 | if (data->args == NULL) |
1037 | return; |
1038 | g_ptr_array_foreach (array: data->args, func: (GFunc) g_dbus_arg_info_unref, NULL); |
1039 | g_ptr_array_free (array: data->args, TRUE); |
1040 | data->args = NULL; |
1041 | } |
1042 | |
1043 | static void |
1044 | parse_data_free_out_args (ParseData *data) |
1045 | { |
1046 | if (data->out_args == NULL) |
1047 | return; |
1048 | g_ptr_array_foreach (array: data->out_args, func: (GFunc) g_dbus_arg_info_unref, NULL); |
1049 | g_ptr_array_free (array: data->out_args, TRUE); |
1050 | data->out_args = NULL; |
1051 | } |
1052 | |
1053 | static void |
1054 | parse_data_free_methods (ParseData *data) |
1055 | { |
1056 | if (data->methods == NULL) |
1057 | return; |
1058 | g_ptr_array_foreach (array: data->methods, func: (GFunc) g_dbus_method_info_unref, NULL); |
1059 | g_ptr_array_free (array: data->methods, TRUE); |
1060 | data->methods = NULL; |
1061 | } |
1062 | |
1063 | static void |
1064 | parse_data_free_signals (ParseData *data) |
1065 | { |
1066 | if (data->signals == NULL) |
1067 | return; |
1068 | g_ptr_array_foreach (array: data->signals, func: (GFunc) g_dbus_signal_info_unref, NULL); |
1069 | g_ptr_array_free (array: data->signals, TRUE); |
1070 | data->signals = NULL; |
1071 | } |
1072 | |
1073 | static void |
1074 | parse_data_free_properties (ParseData *data) |
1075 | { |
1076 | if (data->properties == NULL) |
1077 | return; |
1078 | g_ptr_array_foreach (array: data->properties, func: (GFunc) g_dbus_property_info_unref, NULL); |
1079 | g_ptr_array_free (array: data->properties, TRUE); |
1080 | data->properties = NULL; |
1081 | } |
1082 | |
1083 | static void |
1084 | parse_data_free_interfaces (ParseData *data) |
1085 | { |
1086 | if (data->interfaces == NULL) |
1087 | return; |
1088 | g_ptr_array_foreach (array: data->interfaces, func: (GFunc) g_dbus_interface_info_unref, NULL); |
1089 | g_ptr_array_free (array: data->interfaces, TRUE); |
1090 | data->interfaces = NULL; |
1091 | } |
1092 | |
1093 | static void |
1094 | parse_data_free_nodes (ParseData *data) |
1095 | { |
1096 | if (data->nodes == NULL) |
1097 | return; |
1098 | g_ptr_array_foreach (array: data->nodes, func: (GFunc) g_dbus_node_info_unref, NULL); |
1099 | g_ptr_array_free (array: data->nodes, TRUE); |
1100 | data->nodes = NULL; |
1101 | } |
1102 | |
1103 | /* ---------------------------------------------------------------------------------------------------- */ |
1104 | |
1105 | static GDBusAnnotationInfo * |
1106 | parse_data_get_annotation (ParseData *data, |
1107 | gboolean create_new) |
1108 | { |
1109 | if (create_new) |
1110 | g_ptr_array_add (array: data->annotations, g_new0 (GDBusAnnotationInfo, 1)); |
1111 | return data->annotations->pdata[data->annotations->len - 1]; |
1112 | } |
1113 | |
1114 | static GDBusArgInfo * |
1115 | parse_data_get_arg (ParseData *data, |
1116 | gboolean create_new) |
1117 | { |
1118 | if (create_new) |
1119 | g_ptr_array_add (array: data->args, g_new0 (GDBusArgInfo, 1)); |
1120 | return data->args->pdata[data->args->len - 1]; |
1121 | } |
1122 | |
1123 | static GDBusArgInfo * |
1124 | parse_data_get_out_arg (ParseData *data, |
1125 | gboolean create_new) |
1126 | { |
1127 | if (create_new) |
1128 | g_ptr_array_add (array: data->out_args, g_new0 (GDBusArgInfo, 1)); |
1129 | return data->out_args->pdata[data->out_args->len - 1]; |
1130 | } |
1131 | |
1132 | static GDBusMethodInfo * |
1133 | parse_data_get_method (ParseData *data, |
1134 | gboolean create_new) |
1135 | { |
1136 | if (create_new) |
1137 | g_ptr_array_add (array: data->methods, g_new0 (GDBusMethodInfo, 1)); |
1138 | return data->methods->pdata[data->methods->len - 1]; |
1139 | } |
1140 | |
1141 | static GDBusSignalInfo * |
1142 | parse_data_get_signal (ParseData *data, |
1143 | gboolean create_new) |
1144 | { |
1145 | if (create_new) |
1146 | g_ptr_array_add (array: data->signals, g_new0 (GDBusSignalInfo, 1)); |
1147 | return data->signals->pdata[data->signals->len - 1]; |
1148 | } |
1149 | |
1150 | static GDBusPropertyInfo * |
1151 | parse_data_get_property (ParseData *data, |
1152 | gboolean create_new) |
1153 | { |
1154 | if (create_new) |
1155 | g_ptr_array_add (array: data->properties, g_new0 (GDBusPropertyInfo, 1)); |
1156 | return data->properties->pdata[data->properties->len - 1]; |
1157 | } |
1158 | |
1159 | static GDBusInterfaceInfo * |
1160 | parse_data_get_interface (ParseData *data, |
1161 | gboolean create_new) |
1162 | { |
1163 | if (create_new) |
1164 | g_ptr_array_add (array: data->interfaces, g_new0 (GDBusInterfaceInfo, 1)); |
1165 | return data->interfaces->pdata[data->interfaces->len - 1]; |
1166 | } |
1167 | |
1168 | static GDBusNodeInfo * |
1169 | parse_data_get_node (ParseData *data, |
1170 | gboolean create_new) |
1171 | { |
1172 | if (create_new) |
1173 | g_ptr_array_add (array: data->nodes, g_new0 (GDBusNodeInfo, 1)); |
1174 | return data->nodes->pdata[data->nodes->len - 1]; |
1175 | } |
1176 | |
1177 | /* ---------------------------------------------------------------------------------------------------- */ |
1178 | |
1179 | static ParseData * |
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 |
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 (array: annotations, func: (GFunc) g_dbus_annotation_info_unref, NULL); |
1209 | g_ptr_array_free (array: annotations, TRUE); |
1210 | } |
1211 | g_slist_free (list: 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 (array: interfaces, func: (GFunc) g_dbus_interface_info_unref, NULL); |
1218 | g_ptr_array_free (array: interfaces, TRUE); |
1219 | } |
1220 | g_slist_free (list: 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 (array: nodes, func: (GFunc) g_dbus_node_info_unref, NULL); |
1227 | g_ptr_array_free (array: nodes, TRUE); |
1228 | } |
1229 | g_slist_free (list: 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 (mem: data); |
1242 | } |
1243 | |
1244 | /* ---------------------------------------------------------------------------------------------------- */ |
1245 | |
1246 | static void |
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 (s1: element_name, s2: "node" ) == 0) |
1272 | { |
1273 | if (!(g_slist_length (list: stack) >= 1 || strcmp (s1: stack->next->data, s2: "node" ) != 0)) |
1274 | { |
1275 | g_set_error_literal (err: error, |
1276 | G_MARKUP_ERROR, |
1277 | code: G_MARKUP_ERROR_INVALID_CONTENT, |
1278 | message: "<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 | first_type: G_MARKUP_COLLECT_STRING | G_MARKUP_COLLECT_OPTIONAL, first_attr: "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 | info: parse_data_get_node (data, TRUE), |
1294 | path: 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 (list: data->interfaces_stack, data: data->interfaces); |
1301 | data->interfaces = NULL; |
1302 | parse_data_steal_interfaces (data, NULL); |
1303 | |
1304 | data->nodes_stack = g_slist_prepend (list: data->nodes_stack, data: data->nodes); |
1305 | data->nodes = NULL; |
1306 | parse_data_steal_nodes (data, NULL); |
1307 | |
1308 | } |
1309 | /* ---------------------------------------------------------------------------------------------------- */ |
1310 | else if (strcmp (s1: element_name, s2: "interface" ) == 0) |
1311 | { |
1312 | if (g_slist_length (list: stack) < 2 || strcmp (s1: stack->next->data, s2: "node" ) != 0) |
1313 | { |
1314 | g_set_error_literal (err: error, |
1315 | G_MARKUP_ERROR, |
1316 | code: G_MARKUP_ERROR_INVALID_CONTENT, |
1317 | message: "<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 | first_type: G_MARKUP_COLLECT_STRING, first_attr: "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 | info: parse_data_get_interface (data, TRUE), |
1333 | name, |
1334 | NULL, |
1335 | NULL, |
1336 | NULL, |
1337 | NULL); |
1338 | |
1339 | } |
1340 | /* ---------------------------------------------------------------------------------------------------- */ |
1341 | else if (strcmp (s1: element_name, s2: "method" ) == 0) |
1342 | { |
1343 | if (g_slist_length (list: stack) < 2 || strcmp (s1: stack->next->data, s2: "interface" ) != 0) |
1344 | { |
1345 | g_set_error_literal (err: error, |
1346 | G_MARKUP_ERROR, |
1347 | code: G_MARKUP_ERROR_INVALID_CONTENT, |
1348 | message: "<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 | first_type: G_MARKUP_COLLECT_STRING, first_attr: "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 | info: 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 (s1: element_name, s2: "signal" ) == 0) |
1374 | { |
1375 | if (g_slist_length (list: stack) < 2 || strcmp (s1: stack->next->data, s2: "interface" ) != 0) |
1376 | { |
1377 | g_set_error_literal (err: error, |
1378 | G_MARKUP_ERROR, |
1379 | code: G_MARKUP_ERROR_INVALID_CONTENT, |
1380 | message: "<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 | first_type: G_MARKUP_COLLECT_STRING, first_attr: "name" , &name, |
1389 | G_MARKUP_COLLECT_INVALID)) |
1390 | goto out; |
1391 | |
1392 | g_dbus_signal_info_set (data, |
1393 | info: 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 (s1: element_name, s2: "property" ) == 0) |
1403 | { |
1404 | GDBusPropertyInfoFlags flags; |
1405 | |
1406 | if (g_slist_length (list: stack) < 2 || strcmp (s1: stack->next->data, s2: "interface" ) != 0) |
1407 | { |
1408 | g_set_error_literal (err: error, |
1409 | G_MARKUP_ERROR, |
1410 | code: G_MARKUP_ERROR_INVALID_CONTENT, |
1411 | message: "<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 | first_type: G_MARKUP_COLLECT_STRING, first_attr: "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 (s1: access, s2: "read" ) == 0) |
1426 | flags = G_DBUS_PROPERTY_INFO_FLAGS_READABLE; |
1427 | else if (strcmp (s1: access, s2: "write" ) == 0) |
1428 | flags = G_DBUS_PROPERTY_INFO_FLAGS_WRITABLE; |
1429 | else if (strcmp (s1: access, s2: "readwrite" ) == 0) |
1430 | flags = G_DBUS_PROPERTY_INFO_FLAGS_READABLE | G_DBUS_PROPERTY_INFO_FLAGS_WRITABLE; |
1431 | else |
1432 | { |
1433 | g_set_error (err: error, |
1434 | G_MARKUP_ERROR, |
1435 | code: G_MARKUP_ERROR_INVALID_CONTENT, |
1436 | format: "Unknown value '%s' of access attribute for element <property>" , |
1437 | access); |
1438 | goto out; |
1439 | } |
1440 | |
1441 | g_dbus_property_info_set (data, |
1442 | info: parse_data_get_property (data, TRUE), |
1443 | name, |
1444 | signature: type, |
1445 | flags, |
1446 | NULL); |
1447 | |
1448 | } |
1449 | /* ---------------------------------------------------------------------------------------------------- */ |
1450 | else if (strcmp (s1: element_name, s2: "arg" ) == 0) |
1451 | { |
1452 | gboolean is_in; |
1453 | gchar *name_to_use; |
1454 | |
1455 | if (g_slist_length (list: stack) < 2 || |
1456 | (strcmp (s1: stack->next->data, s2: "method" ) != 0 && |
1457 | strcmp (s1: stack->next->data, s2: "signal" ) != 0)) |
1458 | { |
1459 | g_set_error_literal (err: error, |
1460 | G_MARKUP_ERROR, |
1461 | code: G_MARKUP_ERROR_INVALID_CONTENT, |
1462 | message: "<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 | first_type: G_MARKUP_COLLECT_STRING | G_MARKUP_COLLECT_OPTIONAL, first_attr: "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 (s1: stack->next->data, s2: "method" ) == 0) |
1477 | is_in = TRUE; |
1478 | else |
1479 | is_in = FALSE; |
1480 | if (direction != NULL) |
1481 | { |
1482 | if (strcmp (s1: direction, s2: "in" ) == 0) |
1483 | is_in = TRUE; |
1484 | else if (strcmp (s1: direction, s2: "out" ) == 0) |
1485 | is_in = FALSE; |
1486 | else |
1487 | { |
1488 | g_set_error (err: error, |
1489 | G_MARKUP_ERROR, |
1490 | code: G_MARKUP_ERROR_INVALID_CONTENT, |
1491 | format: "Unknown value '%s' of direction attribute" , |
1492 | direction); |
1493 | goto out; |
1494 | } |
1495 | } |
1496 | |
1497 | if (is_in && strcmp (s1: stack->next->data, s2: "signal" ) == 0) |
1498 | { |
1499 | g_set_error_literal (err: error, |
1500 | G_MARKUP_ERROR, |
1501 | code: G_MARKUP_ERROR_INVALID_CONTENT, |
1502 | message: "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 (format: "arg_%d" , data->num_args); |
1508 | else |
1509 | name_to_use = g_strdup (str: name); |
1510 | data->num_args++; |
1511 | |
1512 | if (is_in) |
1513 | { |
1514 | g_dbus_arg_info_set (data, |
1515 | info: parse_data_get_arg (data, TRUE), |
1516 | name: name_to_use, |
1517 | signature: type, |
1518 | NULL); |
1519 | data->last_arg_was_in = TRUE; |
1520 | } |
1521 | else |
1522 | { |
1523 | g_dbus_arg_info_set (data, |
1524 | info: parse_data_get_out_arg (data, TRUE), |
1525 | name: name_to_use, |
1526 | signature: type, |
1527 | NULL); |
1528 | data->last_arg_was_in = FALSE; |
1529 | |
1530 | } |
1531 | |
1532 | g_free (mem: name_to_use); |
1533 | } |
1534 | /* ---------------------------------------------------------------------------------------------------- */ |
1535 | else if (strcmp (s1: element_name, s2: "annotation" ) == 0) |
1536 | { |
1537 | if (g_slist_length (list: stack) < 2 || |
1538 | (strcmp (s1: stack->next->data, s2: "node" ) != 0 && |
1539 | strcmp (s1: stack->next->data, s2: "interface" ) != 0 && |
1540 | strcmp (s1: stack->next->data, s2: "signal" ) != 0 && |
1541 | strcmp (s1: stack->next->data, s2: "method" ) != 0 && |
1542 | strcmp (s1: stack->next->data, s2: "property" ) != 0 && |
1543 | strcmp (s1: stack->next->data, s2: "arg" ) != 0 && |
1544 | strcmp (s1: stack->next->data, s2: "annotation" ) != 0)) |
1545 | { |
1546 | g_set_error_literal (err: error, |
1547 | G_MARKUP_ERROR, |
1548 | code: G_MARKUP_ERROR_INVALID_CONTENT, |
1549 | message: "<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 | first_type: G_MARKUP_COLLECT_STRING, first_attr: "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 | info: parse_data_get_annotation (data, TRUE), |
1564 | key: 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 (list: data->annotations_stack, data: data->annotations); |
1577 | data->annotations = NULL; |
1578 | parse_data_steal_annotations (data, NULL); |
1579 | |
1580 | out: |
1581 | ; |
1582 | } |
1583 | |
1584 | /* ---------------------------------------------------------------------------------------------------- */ |
1585 | |
1586 | static GDBusAnnotationInfo ** |
1587 | steal_annotations (ParseData *data) |
1588 | { |
1589 | return parse_data_steal_annotations (data, NULL); |
1590 | } |
1591 | |
1592 | |
1593 | static void |
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 (s1: element_name, s2: "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, out_num_elements: &num_nodes); |
1612 | interfaces = parse_data_steal_interfaces (data, out_num_elements: &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 (list: data->interfaces_stack, data: 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 (list: data->nodes_stack, data: data->nodes_stack->data); |
1624 | |
1625 | g_dbus_node_info_set (data, |
1626 | info: parse_data_get_node (data, FALSE), |
1627 | NULL, |
1628 | interfaces, |
1629 | nodes, |
1630 | annotations: steal_annotations (data)); |
1631 | |
1632 | } |
1633 | else if (strcmp (s1: element_name, s2: "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, out_num_elements: &num_methods); |
1643 | signals = parse_data_steal_signals (data, out_num_elements: &num_signals); |
1644 | properties = parse_data_steal_properties (data, out_num_elements: &num_properties); |
1645 | |
1646 | g_dbus_interface_info_set (data, |
1647 | info: parse_data_get_interface (data, FALSE), |
1648 | NULL, |
1649 | methods, |
1650 | signals, |
1651 | properties, |
1652 | annotations: steal_annotations (data)); |
1653 | |
1654 | } |
1655 | else if (strcmp (s1: element_name, s2: "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, out_num_elements: &in_num_args); |
1663 | out_args = parse_data_steal_out_args (data, out_num_elements: &out_num_args); |
1664 | |
1665 | g_dbus_method_info_set (data, |
1666 | info: parse_data_get_method (data, FALSE), |
1667 | NULL, |
1668 | in_args, |
1669 | out_args, |
1670 | annotations: steal_annotations (data)); |
1671 | } |
1672 | else if (strcmp (s1: element_name, s2: "signal" ) == 0) |
1673 | { |
1674 | guint num_args; |
1675 | GDBusArgInfo **args; |
1676 | |
1677 | args = parse_data_steal_out_args (data, out_num_elements: &num_args); |
1678 | |
1679 | g_dbus_signal_info_set (data, |
1680 | info: parse_data_get_signal (data, FALSE), |
1681 | NULL, |
1682 | args, |
1683 | annotations: steal_annotations (data)); |
1684 | } |
1685 | else if (strcmp (s1: element_name, s2: "property" ) == 0) |
1686 | { |
1687 | g_dbus_property_info_set (data, |
1688 | info: parse_data_get_property (data, FALSE), |
1689 | NULL, |
1690 | NULL, |
1691 | flags: G_DBUS_PROPERTY_INFO_FLAGS_NONE, |
1692 | annotations: steal_annotations (data)); |
1693 | } |
1694 | else if (strcmp (s1: element_name, s2: "arg" ) == 0) |
1695 | { |
1696 | g_dbus_arg_info_set (data, |
1697 | info: data->last_arg_was_in ? parse_data_get_arg (data, FALSE) : parse_data_get_out_arg (data, FALSE), |
1698 | NULL, |
1699 | NULL, |
1700 | annotations: steal_annotations (data)); |
1701 | } |
1702 | else if (strcmp (s1: element_name, s2: "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 (list: data->annotations_stack, data: data->annotations_stack->data); |
1712 | |
1713 | have_popped_annotations = TRUE; |
1714 | |
1715 | g_dbus_annotation_info_set (data, |
1716 | info: 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 (list: data->annotations_stack, data: data->annotations_stack->data); |
1732 | } |
1733 | } |
1734 | |
1735 | /* ---------------------------------------------------------------------------------------------------- */ |
1736 | |
1737 | static void |
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: &line_number, char_number: &char_number); |
1746 | |
1747 | g_prefix_error (err: &error, format: "%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 * |
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 | flags: G_MARKUP_IGNORE_QUALIFIED, |
1796 | user_data: data, |
1797 | user_data_dnotify: (GDestroyNotify) parse_data_free); |
1798 | |
1799 | if (!g_markup_parse_context_parse (context, |
1800 | text: xml_data, |
1801 | text_len: strlen (s: 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, out_num_elements: &num_nodes); |
1809 | |
1810 | if (num_nodes != 1) |
1811 | { |
1812 | guint n; |
1813 | |
1814 | g_set_error (err: error, |
1815 | G_MARKUP_ERROR, |
1816 | code: G_MARKUP_ERROR_INVALID_CONTENT, |
1817 | format: "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 (info: ughret[n]); |
1824 | ughret[n] = NULL; |
1825 | } |
1826 | } |
1827 | |
1828 | ret = ughret[0]; |
1829 | g_free (mem: ughret); |
1830 | |
1831 | out: |
1832 | g_free (mem: 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 * |
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 (str1: annotations[n]->key, str2: 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 |
1894 | info_cache_free (InfoCacheEntry *cache) |
1895 | { |
1896 | g_assert (cache->use_count == 0); |
1897 | g_hash_table_unref (hash_table: cache->method_name_to_data); |
1898 | g_hash_table_unref (hash_table: cache->signal_name_to_data); |
1899 | g_hash_table_unref (hash_table: 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 * |
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 (hash_table: info_cache, key: info); |
1934 | if (G_LIKELY (cache != NULL)) |
1935 | { |
1936 | result = g_hash_table_lookup (hash_table: cache->method_name_to_data, key: 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 (str1: i->name, str2: 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 * |
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 (hash_table: info_cache, key: info); |
1988 | if (G_LIKELY (cache != NULL)) |
1989 | { |
1990 | result = g_hash_table_lookup (hash_table: cache->signal_name_to_data, key: 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 (str1: i->name, str2: 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 * |
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 (hash_table: info_cache, key: info); |
2042 | if (G_LIKELY (cache != NULL)) |
2043 | { |
2044 | result = g_hash_table_lookup (hash_table: cache->property_name_to_data, key: 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 (str1: i->name, str2: 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 |
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 (hash_func: g_direct_hash, key_equal_func: g_direct_equal, NULL, value_destroy_func: (GDestroyNotify) info_cache_free); |
2096 | cache = g_hash_table_lookup (hash_table: info_cache, key: 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 (hash_func: g_str_hash, key_equal_func: g_str_equal); |
2105 | cache->signal_name_to_data = g_hash_table_new (hash_func: g_str_hash, key_equal_func: g_str_equal); |
2106 | cache->property_name_to_data = g_hash_table_new (hash_func: g_str_hash, key_equal_func: g_str_equal); |
2107 | for (n = 0; info->methods != NULL && info->methods[n] != NULL; n++) |
2108 | g_hash_table_insert (hash_table: cache->method_name_to_data, key: info->methods[n]->name, value: info->methods[n]); |
2109 | for (n = 0; info->signals != NULL && info->signals[n] != NULL; n++) |
2110 | g_hash_table_insert (hash_table: cache->signal_name_to_data, key: info->signals[n]->name, value: info->signals[n]); |
2111 | for (n = 0; info->properties != NULL && info->properties[n] != NULL; n++) |
2112 | g_hash_table_insert (hash_table: cache->property_name_to_data, key: info->properties[n]->name, value: info->properties[n]); |
2113 | g_hash_table_insert (hash_table: info_cache, key: info, value: 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 |
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 (hash_table: info_cache, key: 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 (hash_table: info_cache, key: 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 * |
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 (str1: i->name, str2: name) == 0) |
2184 | { |
2185 | result = i; |
2186 | goto out; |
2187 | } |
2188 | } |
2189 | |
2190 | result = NULL; |
2191 | |
2192 | out: |
2193 | return result; |
2194 | } |
2195 | |