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