Home | History | Annotate | Download | only in dbus
      1 /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
      2 /* dbus-object-tree.c  DBusObjectTree (internals of DBusConnection)
      3  *
      4  * Copyright (C) 2003, 2005  Red Hat Inc.
      5  *
      6  * Licensed under the Academic Free License version 2.1
      7  *
      8  * This program is free software; you can redistribute it and/or modify
      9  * it under the terms of the GNU General Public License as published by
     10  * the Free Software Foundation; either version 2 of the License, or
     11  * (at your option) any later version.
     12  *
     13  * This program is distributed in the hope that it will be useful,
     14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     16  * GNU General Public License for more details.
     17  *
     18  * You should have received a copy of the GNU General Public License
     19  * along with this program; if not, write to the Free Software
     20  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
     21  *
     22  */
     23 
     24 #include <config.h>
     25 #include "dbus-object-tree.h"
     26 #include "dbus-connection-internal.h"
     27 #include "dbus-internals.h"
     28 #include "dbus-hash.h"
     29 #include "dbus-protocol.h"
     30 #include "dbus-string.h"
     31 #include <string.h>
     32 #include <stdlib.h>
     33 
     34 /**
     35  * @defgroup DBusObjectTree A hierarchy of objects with container-contained relationship
     36  * @ingroup  DBusInternals
     37  * @brief DBusObjectTree is used by DBusConnection to track the object tree
     38  *
     39  * Types and functions related to DBusObjectTree. These
     40  * are all library-internal.
     41  *
     42  * @{
     43  */
     44 
     45 /** Subnode of the object hierarchy */
     46 typedef struct DBusObjectSubtree DBusObjectSubtree;
     47 
     48 static DBusObjectSubtree* _dbus_object_subtree_new   (const char                  *name,
     49                                                       const DBusObjectPathVTable  *vtable,
     50                                                       void                        *user_data);
     51 static DBusObjectSubtree* _dbus_object_subtree_ref   (DBusObjectSubtree           *subtree);
     52 static void               _dbus_object_subtree_unref (DBusObjectSubtree           *subtree);
     53 
     54 /**
     55  * Internals of DBusObjectTree
     56  */
     57 struct DBusObjectTree
     58 {
     59   int                 refcount;   /**< Reference count */
     60   DBusConnection     *connection; /**< Connection this tree belongs to */
     61 
     62   DBusObjectSubtree  *root;       /**< Root of the tree ("/" node) */
     63 };
     64 
     65 /**
     66  * Struct representing a single registered subtree handler, or node
     67  * that's a parent of a registered subtree handler. If
     68  * message_function != NULL there's actually a handler at this node.
     69  */
     70 struct DBusObjectSubtree
     71 {
     72   DBusAtomic                         refcount;            /**< Reference count */
     73   DBusObjectSubtree                 *parent;              /**< Parent node */
     74   DBusObjectPathUnregisterFunction   unregister_function; /**< Function to call on unregister */
     75   DBusObjectPathMessageFunction      message_function;    /**< Function to handle messages */
     76   void                              *user_data;           /**< Data for functions */
     77   DBusObjectSubtree                **subtrees;            /**< Child nodes */
     78   int                                n_subtrees;          /**< Number of child nodes */
     79   int                                max_subtrees;        /**< Number of allocated entries in subtrees */
     80   unsigned int                       invoke_as_fallback : 1; /**< Whether to invoke message_function when child nodes don't handle the message */
     81   char                               name[1]; /**< Allocated as large as necessary */
     82 };
     83 
     84 /**
     85  * Creates a new object tree, representing a mapping from paths
     86  * to handler vtables.
     87  *
     88  * @param connection the connection this tree belongs to
     89  * @returns the new tree or #NULL if no memory
     90  */
     91 DBusObjectTree*
     92 _dbus_object_tree_new (DBusConnection *connection)
     93 {
     94   DBusObjectTree *tree;
     95 
     96   /* the connection passed in here isn't fully constructed,
     97    * so don't do anything more than store a pointer to
     98    * it
     99    */
    100 
    101   tree = dbus_new0 (DBusObjectTree, 1);
    102   if (tree == NULL)
    103     goto oom;
    104 
    105   tree->refcount = 1;
    106   tree->connection = connection;
    107   tree->root = _dbus_object_subtree_new ("/", NULL, NULL);
    108   if (tree->root == NULL)
    109     goto oom;
    110   tree->root->invoke_as_fallback = TRUE;
    111 
    112   return tree;
    113 
    114  oom:
    115   if (tree)
    116     {
    117       dbus_free (tree);
    118     }
    119 
    120   return NULL;
    121 }
    122 
    123 /**
    124  * Increment the reference count
    125  * @param tree the object tree
    126  * @returns the object tree
    127  */
    128 DBusObjectTree *
    129 _dbus_object_tree_ref (DBusObjectTree *tree)
    130 {
    131   _dbus_assert (tree->refcount > 0);
    132 
    133   tree->refcount += 1;
    134 
    135   return tree;
    136 }
    137 
    138 /**
    139  * Decrement the reference count
    140  * @param tree the object tree
    141  */
    142 void
    143 _dbus_object_tree_unref (DBusObjectTree *tree)
    144 {
    145   _dbus_assert (tree->refcount > 0);
    146 
    147   tree->refcount -= 1;
    148 
    149   if (tree->refcount == 0)
    150     {
    151       _dbus_object_tree_free_all_unlocked (tree);
    152 
    153       dbus_free (tree);
    154     }
    155 }
    156 
    157 /** Set to 1 to get a bunch of debug spew about finding the
    158  * subtree nodes
    159  */
    160 #define VERBOSE_FIND 0
    161 
    162 static DBusObjectSubtree*
    163 find_subtree_recurse (DBusObjectSubtree  *subtree,
    164                       const char        **path,
    165                       dbus_bool_t         create_if_not_found,
    166                       int                *index_in_parent,
    167                       dbus_bool_t        *exact_match)
    168 {
    169   int i, j;
    170   dbus_bool_t return_deepest_match;
    171 
    172   return_deepest_match = exact_match != NULL;
    173 
    174   _dbus_assert (!(return_deepest_match && create_if_not_found));
    175 
    176   if (path[0] == NULL)
    177     {
    178 #if VERBOSE_FIND
    179       _dbus_verbose ("  path exhausted, returning %s\n",
    180                      subtree->name);
    181 #endif
    182       if (exact_match != NULL)
    183 	*exact_match = TRUE;
    184       return subtree;
    185     }
    186 
    187 #if VERBOSE_FIND
    188   _dbus_verbose ("  searching children of %s for %s\n",
    189                  subtree->name, path[0]);
    190 #endif
    191 
    192   i = 0;
    193   j = subtree->n_subtrees;
    194   while (i < j)
    195     {
    196       int k, v;
    197 
    198       k = (i + j) / 2;
    199       v = strcmp (path[0], subtree->subtrees[k]->name);
    200 
    201 #if VERBOSE_FIND
    202       _dbus_verbose ("  %s cmp %s = %d\n",
    203                      path[0], subtree->subtrees[k]->name,
    204                      v);
    205 #endif
    206 
    207       if (v == 0)
    208         {
    209           if (index_in_parent)
    210             {
    211 #if VERBOSE_FIND
    212               _dbus_verbose ("  storing parent index %d\n", k);
    213 #endif
    214               *index_in_parent = k;
    215             }
    216 
    217           if (return_deepest_match)
    218             {
    219               DBusObjectSubtree *next;
    220 
    221               next = find_subtree_recurse (subtree->subtrees[k],
    222                                            &path[1], create_if_not_found,
    223                                            index_in_parent, exact_match);
    224               if (next == NULL &&
    225                   subtree->invoke_as_fallback)
    226                 {
    227 #if VERBOSE_FIND
    228                   _dbus_verbose ("  no deeper match found, returning %s\n",
    229                                  subtree->name);
    230 #endif
    231 		  if (exact_match != NULL)
    232 		    *exact_match = FALSE;
    233                   return subtree;
    234                 }
    235               else
    236                 return next;
    237             }
    238           else
    239             return find_subtree_recurse (subtree->subtrees[k],
    240                                          &path[1], create_if_not_found,
    241                                          index_in_parent, exact_match);
    242         }
    243       else if (v < 0)
    244         {
    245           j = k;
    246         }
    247       else
    248         {
    249           i = k + 1;
    250         }
    251     }
    252 
    253 #if VERBOSE_FIND
    254   _dbus_verbose ("  no match found, current tree %s, create_if_not_found = %d\n",
    255                  subtree->name, create_if_not_found);
    256 #endif
    257 
    258   if (create_if_not_found)
    259     {
    260       DBusObjectSubtree* child;
    261       int child_pos, new_n_subtrees;
    262 
    263 #if VERBOSE_FIND
    264       _dbus_verbose ("  creating subtree %s\n",
    265                      path[0]);
    266 #endif
    267 
    268       child = _dbus_object_subtree_new (path[0],
    269                                         NULL, NULL);
    270       if (child == NULL)
    271         return NULL;
    272 
    273       new_n_subtrees = subtree->n_subtrees + 1;
    274       if (new_n_subtrees > subtree->max_subtrees)
    275         {
    276           int new_max_subtrees;
    277           DBusObjectSubtree **new_subtrees;
    278 
    279           new_max_subtrees = subtree->max_subtrees == 0 ? 1 : 2 * subtree->max_subtrees;
    280           new_subtrees = dbus_realloc (subtree->subtrees,
    281                                        new_max_subtrees * sizeof (DBusObjectSubtree*));
    282           if (new_subtrees == NULL)
    283             {
    284               _dbus_object_subtree_unref (child);
    285               return NULL;
    286             }
    287           subtree->subtrees = new_subtrees;
    288           subtree->max_subtrees = new_max_subtrees;
    289         }
    290 
    291       /* The binary search failed, so i == j points to the
    292          place the child should be inserted. */
    293       child_pos = i;
    294       _dbus_assert (child_pos < new_n_subtrees &&
    295                     new_n_subtrees <= subtree->max_subtrees);
    296       if (child_pos + 1 < new_n_subtrees)
    297 	{
    298 	  memmove (&subtree->subtrees[child_pos+1],
    299 		   &subtree->subtrees[child_pos],
    300 		   (new_n_subtrees - child_pos - 1) *
    301 		   sizeof subtree->subtrees[0]);
    302 	}
    303       subtree->subtrees[child_pos] = child;
    304 
    305       if (index_in_parent)
    306         *index_in_parent = child_pos;
    307       subtree->n_subtrees = new_n_subtrees;
    308       child->parent = subtree;
    309 
    310       return find_subtree_recurse (child,
    311                                    &path[1], create_if_not_found,
    312                                    index_in_parent, exact_match);
    313     }
    314   else
    315     {
    316       if (exact_match != NULL)
    317 	*exact_match = FALSE;
    318       return (return_deepest_match && subtree->invoke_as_fallback) ? subtree : NULL;
    319     }
    320 }
    321 
    322 static DBusObjectSubtree*
    323 find_subtree (DBusObjectTree *tree,
    324               const char    **path,
    325               int            *index_in_parent)
    326 {
    327   DBusObjectSubtree *subtree;
    328 
    329 #if VERBOSE_FIND
    330   _dbus_verbose ("Looking for exact registered subtree\n");
    331 #endif
    332 
    333   subtree = find_subtree_recurse (tree->root, path, FALSE, index_in_parent, NULL);
    334 
    335   if (subtree && subtree->message_function == NULL)
    336     return NULL;
    337   else
    338     return subtree;
    339 }
    340 
    341 static DBusObjectSubtree*
    342 lookup_subtree (DBusObjectTree *tree,
    343                 const char    **path)
    344 {
    345 #if VERBOSE_FIND
    346   _dbus_verbose ("Looking for subtree\n");
    347 #endif
    348   return find_subtree_recurse (tree->root, path, FALSE, NULL, NULL);
    349 }
    350 
    351 static DBusObjectSubtree*
    352 find_handler (DBusObjectTree *tree,
    353               const char    **path,
    354               dbus_bool_t    *exact_match)
    355 {
    356 #if VERBOSE_FIND
    357   _dbus_verbose ("Looking for deepest handler\n");
    358 #endif
    359   _dbus_assert (exact_match != NULL);
    360 
    361   *exact_match = FALSE; /* ensure always initialized */
    362 
    363   return find_subtree_recurse (tree->root, path, FALSE, NULL, exact_match);
    364 }
    365 
    366 static DBusObjectSubtree*
    367 ensure_subtree (DBusObjectTree *tree,
    368                 const char    **path)
    369 {
    370 #if VERBOSE_FIND
    371   _dbus_verbose ("Ensuring subtree\n");
    372 #endif
    373   return find_subtree_recurse (tree->root, path, TRUE, NULL, NULL);
    374 }
    375 
    376 static char *flatten_path (const char **path);
    377 
    378 /**
    379  * Registers a new subtree in the global object tree.
    380  *
    381  * @param tree the global object tree
    382  * @param fallback #TRUE to handle messages to children of this path
    383  * @param path NULL-terminated array of path elements giving path to subtree
    384  * @param vtable the vtable used to traverse this subtree
    385  * @param user_data user data to pass to methods in the vtable
    386  * @param error address where an error can be returned
    387  * @returns #FALSE if an error (#DBUS_ERROR_NO_MEMORY or
    388  *    #DBUS_ERROR_OBJECT_PATH_IN_USE) is reported
    389  */
    390 dbus_bool_t
    391 _dbus_object_tree_register (DBusObjectTree              *tree,
    392                             dbus_bool_t                  fallback,
    393                             const char                 **path,
    394                             const DBusObjectPathVTable  *vtable,
    395                             void                        *user_data,
    396                             DBusError                   *error)
    397 {
    398   DBusObjectSubtree  *subtree;
    399 
    400   _dbus_assert (tree != NULL);
    401   _dbus_assert (vtable->message_function != NULL);
    402   _dbus_assert (path != NULL);
    403 
    404   subtree = ensure_subtree (tree, path);
    405   if (subtree == NULL)
    406     {
    407       _DBUS_SET_OOM (error);
    408       return FALSE;
    409     }
    410 
    411   if (subtree->message_function != NULL)
    412     {
    413       if (error != NULL)
    414         {
    415           char *complete_path = flatten_path (path);
    416 
    417           dbus_set_error (error, DBUS_ERROR_OBJECT_PATH_IN_USE,
    418                           "A handler is already registered for %s",
    419                           complete_path ? complete_path
    420                                         : "(cannot represent path: out of memory!)");
    421 
    422           dbus_free (complete_path);
    423         }
    424 
    425       return FALSE;
    426     }
    427 
    428   subtree->message_function = vtable->message_function;
    429   subtree->unregister_function = vtable->unregister_function;
    430   subtree->user_data = user_data;
    431   subtree->invoke_as_fallback = fallback != FALSE;
    432 
    433   return TRUE;
    434 }
    435 
    436 /**
    437  * Unregisters an object subtree that was registered with the
    438  * same path.
    439  *
    440  * @param tree the global object tree
    441  * @param path path to the subtree (same as the one passed to _dbus_object_tree_register())
    442  */
    443 void
    444 _dbus_object_tree_unregister_and_unlock (DBusObjectTree          *tree,
    445                                          const char             **path)
    446 {
    447   int i;
    448   DBusObjectSubtree *subtree;
    449   DBusObjectPathUnregisterFunction unregister_function;
    450   void *user_data;
    451   DBusConnection *connection;
    452 
    453   _dbus_assert (path != NULL);
    454 
    455   unregister_function = NULL;
    456   user_data = NULL;
    457 
    458   subtree = find_subtree (tree, path, &i);
    459 
    460 #ifndef DBUS_DISABLE_CHECKS
    461   if (subtree == NULL)
    462     {
    463       _dbus_warn ("Attempted to unregister path (path[0] = %s path[1] = %s) which isn't registered\n",
    464                   path[0] ? path[0] : "null",
    465                   path[1] ? path[1] : "null");
    466       goto unlock;
    467     }
    468 #else
    469   _dbus_assert (subtree != NULL);
    470 #endif
    471 
    472   _dbus_assert (subtree->parent == NULL ||
    473                 (i >= 0 && subtree->parent->subtrees[i] == subtree));
    474 
    475   subtree->message_function = NULL;
    476 
    477   unregister_function = subtree->unregister_function;
    478   user_data = subtree->user_data;
    479 
    480   subtree->unregister_function = NULL;
    481   subtree->user_data = NULL;
    482 
    483   /* If we have no subtrees of our own, remove from
    484    * our parent (FIXME could also be more aggressive
    485    * and remove our parent if it becomes empty)
    486    */
    487   if (subtree->parent && subtree->n_subtrees == 0)
    488     {
    489       /* assumes a 0-byte memmove is OK */
    490       memmove (&subtree->parent->subtrees[i],
    491                &subtree->parent->subtrees[i+1],
    492                (subtree->parent->n_subtrees - i - 1) *
    493                sizeof (subtree->parent->subtrees[0]));
    494       subtree->parent->n_subtrees -= 1;
    495 
    496       subtree->parent = NULL;
    497 
    498       _dbus_object_subtree_unref (subtree);
    499     }
    500   subtree = NULL;
    501 
    502 unlock:
    503   connection = tree->connection;
    504 
    505   /* Unlock and call application code */
    506 #ifdef DBUS_BUILD_TESTS
    507   if (connection)
    508 #endif
    509     {
    510       _dbus_connection_ref_unlocked (connection);
    511       _dbus_verbose ("unlock\n");
    512       _dbus_connection_unlock (connection);
    513     }
    514 
    515   if (unregister_function)
    516     (* unregister_function) (connection, user_data);
    517 
    518 #ifdef DBUS_BUILD_TESTS
    519   if (connection)
    520 #endif
    521     dbus_connection_unref (connection);
    522 }
    523 
    524 static void
    525 free_subtree_recurse (DBusConnection    *connection,
    526                       DBusObjectSubtree *subtree)
    527 {
    528   /* Delete them from the end, for slightly
    529    * more robustness against odd reentrancy.
    530    */
    531   while (subtree->n_subtrees > 0)
    532     {
    533       DBusObjectSubtree *child;
    534 
    535       child = subtree->subtrees[subtree->n_subtrees - 1];
    536       subtree->subtrees[subtree->n_subtrees - 1] = NULL;
    537       subtree->n_subtrees -= 1;
    538       child->parent = NULL;
    539 
    540       free_subtree_recurse (connection, child);
    541     }
    542 
    543   /* Call application code */
    544   if (subtree->unregister_function)
    545     (* subtree->unregister_function) (connection,
    546 				      subtree->user_data);
    547 
    548   subtree->message_function = NULL;
    549   subtree->unregister_function = NULL;
    550   subtree->user_data = NULL;
    551 
    552   /* Now free ourselves */
    553   _dbus_object_subtree_unref (subtree);
    554 }
    555 
    556 /**
    557  * Free all the handlers in the tree. Lock on tree's connection
    558  * must not be held.
    559  *
    560  * @param tree the object tree
    561  */
    562 void
    563 _dbus_object_tree_free_all_unlocked (DBusObjectTree *tree)
    564 {
    565   if (tree->root)
    566     free_subtree_recurse (tree->connection,
    567                           tree->root);
    568   tree->root = NULL;
    569 }
    570 
    571 static dbus_bool_t
    572 _dbus_object_tree_list_registered_unlocked (DBusObjectTree *tree,
    573                                             const char    **parent_path,
    574                                             char         ***child_entries)
    575 {
    576   DBusObjectSubtree *subtree;
    577   char **retval;
    578 
    579   _dbus_assert (parent_path != NULL);
    580   _dbus_assert (child_entries != NULL);
    581 
    582   *child_entries = NULL;
    583 
    584   subtree = lookup_subtree (tree, parent_path);
    585   if (subtree == NULL)
    586     {
    587       retval = dbus_new0 (char *, 1);
    588     }
    589   else
    590     {
    591       int i;
    592       retval = dbus_new0 (char*, subtree->n_subtrees + 1);
    593       if (retval == NULL)
    594         goto out;
    595       i = 0;
    596       while (i < subtree->n_subtrees)
    597         {
    598           retval[i] = _dbus_strdup (subtree->subtrees[i]->name);
    599           if (retval[i] == NULL)
    600             {
    601               dbus_free_string_array (retval);
    602               retval = NULL;
    603               goto out;
    604             }
    605           ++i;
    606         }
    607     }
    608 
    609  out:
    610 
    611   *child_entries = retval;
    612   return retval != NULL;
    613 }
    614 
    615 static DBusHandlerResult
    616 handle_default_introspect_and_unlock (DBusObjectTree          *tree,
    617                                       DBusMessage             *message,
    618                                       const char             **path)
    619 {
    620   DBusString xml;
    621   DBusHandlerResult result;
    622   char **children;
    623   int i;
    624   DBusMessage *reply;
    625   DBusMessageIter iter;
    626   const char *v_STRING;
    627   dbus_bool_t already_unlocked;
    628 
    629   /* We have the connection lock here */
    630 
    631   already_unlocked = FALSE;
    632 
    633   _dbus_verbose (" considering default Introspect() handler...\n");
    634 
    635   reply = NULL;
    636 
    637   if (!dbus_message_is_method_call (message,
    638                                     DBUS_INTERFACE_INTROSPECTABLE,
    639                                     "Introspect"))
    640     {
    641 #ifdef DBUS_BUILD_TESTS
    642       if (tree->connection)
    643 #endif
    644         {
    645           _dbus_verbose ("unlock\n");
    646           _dbus_connection_unlock (tree->connection);
    647         }
    648 
    649       return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
    650     }
    651 
    652   _dbus_verbose (" using default Introspect() handler!\n");
    653 
    654   if (!_dbus_string_init (&xml))
    655     {
    656 #ifdef DBUS_BUILD_TESTS
    657       if (tree->connection)
    658 #endif
    659         {
    660           _dbus_verbose ("unlock\n");
    661           _dbus_connection_unlock (tree->connection);
    662         }
    663 
    664       return DBUS_HANDLER_RESULT_NEED_MEMORY;
    665     }
    666 
    667   result = DBUS_HANDLER_RESULT_NEED_MEMORY;
    668 
    669   children = NULL;
    670   if (!_dbus_object_tree_list_registered_unlocked (tree, path, &children))
    671     goto out;
    672 
    673   if (!_dbus_string_append (&xml, DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE))
    674     goto out;
    675 
    676   if (!_dbus_string_append (&xml, "<node>\n"))
    677     goto out;
    678 
    679   i = 0;
    680   while (children[i] != NULL)
    681     {
    682       if (!_dbus_string_append_printf (&xml, "  <node name=\"%s\"/>\n",
    683                                        children[i]))
    684         goto out;
    685 
    686       ++i;
    687     }
    688 
    689   if (!_dbus_string_append (&xml, "</node>\n"))
    690     goto out;
    691 
    692   reply = dbus_message_new_method_return (message);
    693   if (reply == NULL)
    694     goto out;
    695 
    696   dbus_message_iter_init_append (reply, &iter);
    697   v_STRING = _dbus_string_get_const_data (&xml);
    698   if (!dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &v_STRING))
    699     goto out;
    700 
    701 #ifdef DBUS_BUILD_TESTS
    702   if (tree->connection)
    703 #endif
    704     {
    705       already_unlocked = TRUE;
    706 
    707       if (!_dbus_connection_send_and_unlock (tree->connection, reply, NULL))
    708         goto out;
    709     }
    710 
    711   result = DBUS_HANDLER_RESULT_HANDLED;
    712 
    713  out:
    714 #ifdef DBUS_BUILD_TESTS
    715   if (tree->connection)
    716 #endif
    717     {
    718       if (!already_unlocked)
    719         {
    720           _dbus_verbose ("unlock\n");
    721           _dbus_connection_unlock (tree->connection);
    722         }
    723     }
    724 
    725   _dbus_string_free (&xml);
    726   dbus_free_string_array (children);
    727   if (reply)
    728     dbus_message_unref (reply);
    729 
    730   return result;
    731 }
    732 
    733 /**
    734  * Tries to dispatch a message by directing it to handler for the
    735  * object path listed in the message header, if any. Messages are
    736  * dispatched first to the registered handler that matches the largest
    737  * number of path elements; that is, message to /foo/bar/baz would go
    738  * to the handler for /foo/bar before the one for /foo.
    739  *
    740  * @todo thread problems
    741  *
    742  * @param tree the global object tree
    743  * @param message the message to dispatch
    744  * @returns whether message was handled successfully
    745  */
    746 DBusHandlerResult
    747 _dbus_object_tree_dispatch_and_unlock (DBusObjectTree          *tree,
    748                                        DBusMessage             *message)
    749 {
    750   char **path;
    751   dbus_bool_t exact_match;
    752   DBusList *list;
    753   DBusList *link;
    754   DBusHandlerResult result;
    755   DBusObjectSubtree *subtree;
    756 
    757 #if 0
    758   _dbus_verbose ("Dispatch of message by object path\n");
    759 #endif
    760 
    761   path = NULL;
    762   if (!dbus_message_get_path_decomposed (message, &path))
    763     {
    764 #ifdef DBUS_BUILD_TESTS
    765       if (tree->connection)
    766 #endif
    767         {
    768           _dbus_verbose ("unlock\n");
    769           _dbus_connection_unlock (tree->connection);
    770         }
    771 
    772       _dbus_verbose ("No memory to get decomposed path\n");
    773 
    774       return DBUS_HANDLER_RESULT_NEED_MEMORY;
    775     }
    776 
    777   if (path == NULL)
    778     {
    779 #ifdef DBUS_BUILD_TESTS
    780       if (tree->connection)
    781 #endif
    782         {
    783           _dbus_verbose ("unlock\n");
    784           _dbus_connection_unlock (tree->connection);
    785         }
    786 
    787       _dbus_verbose ("No path field in message\n");
    788       return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
    789     }
    790 
    791   /* Find the deepest path that covers the path in the message */
    792   subtree = find_handler (tree, (const char**) path, &exact_match);
    793 
    794   /* Build a list of all paths that cover the path in the message */
    795 
    796   list = NULL;
    797 
    798   while (subtree != NULL)
    799     {
    800       if (subtree->message_function != NULL && (exact_match || subtree->invoke_as_fallback))
    801         {
    802           _dbus_object_subtree_ref (subtree);
    803 
    804           /* run deepest paths first */
    805           if (!_dbus_list_append (&list, subtree))
    806             {
    807               result = DBUS_HANDLER_RESULT_NEED_MEMORY;
    808               _dbus_object_subtree_unref (subtree);
    809               goto free_and_return;
    810             }
    811         }
    812 
    813       exact_match = FALSE;
    814       subtree = subtree->parent;
    815     }
    816 
    817   _dbus_verbose ("%d handlers in the path tree for this message\n",
    818                  _dbus_list_get_length (&list));
    819 
    820   /* Invoke each handler in the list */
    821 
    822   result = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
    823 
    824   link = _dbus_list_get_first_link (&list);
    825   while (link != NULL)
    826     {
    827       DBusList *next = _dbus_list_get_next_link (&list, link);
    828       subtree = link->data;
    829 
    830       /* message_function is NULL if we're unregistered
    831        * due to reentrancy
    832        */
    833       if (subtree->message_function)
    834         {
    835           DBusObjectPathMessageFunction message_function;
    836           void *user_data;
    837 
    838           message_function = subtree->message_function;
    839           user_data = subtree->user_data;
    840 
    841 #if 0
    842           _dbus_verbose ("  (invoking a handler)\n");
    843 #endif
    844 
    845 #ifdef DBUS_BUILD_TESTS
    846           if (tree->connection)
    847 #endif
    848             {
    849               _dbus_verbose ("unlock\n");
    850               _dbus_connection_unlock (tree->connection);
    851             }
    852 
    853           /* FIXME you could unregister the subtree in another thread
    854            * before we invoke the callback, and I can't figure out a
    855            * good way to solve this.
    856            */
    857 
    858           result = (* message_function) (tree->connection,
    859                                          message,
    860                                          user_data);
    861 
    862 #ifdef DBUS_BUILD_TESTS
    863           if (tree->connection)
    864 #endif
    865             _dbus_connection_lock (tree->connection);
    866 
    867           if (result != DBUS_HANDLER_RESULT_NOT_YET_HANDLED)
    868             goto free_and_return;
    869         }
    870 
    871       link = next;
    872     }
    873 
    874  free_and_return:
    875 
    876   if (result == DBUS_HANDLER_RESULT_NOT_YET_HANDLED)
    877     {
    878       /* This hardcoded default handler does a minimal Introspect()
    879        */
    880       result = handle_default_introspect_and_unlock (tree, message,
    881                                                      (const char**) path);
    882     }
    883   else
    884     {
    885 #ifdef DBUS_BUILD_TESTS
    886       if (tree->connection)
    887 #endif
    888         {
    889           _dbus_verbose ("unlock\n");
    890           _dbus_connection_unlock (tree->connection);
    891         }
    892     }
    893 
    894   while (list != NULL)
    895     {
    896       link = _dbus_list_get_first_link (&list);
    897       _dbus_object_subtree_unref (link->data);
    898       _dbus_list_remove_link (&list, link);
    899     }
    900 
    901   dbus_free_string_array (path);
    902 
    903   return result;
    904 }
    905 
    906 /**
    907  * Looks up the data passed to _dbus_object_tree_register() for a
    908  * handler at the given path.
    909  *
    910  * @param tree the global object tree
    911  * @param path NULL-terminated array of path elements giving path to subtree
    912  * @returns the object's user_data or #NULL if none found
    913  */
    914 void*
    915 _dbus_object_tree_get_user_data_unlocked (DBusObjectTree *tree,
    916                                           const char    **path)
    917 {
    918   dbus_bool_t exact_match;
    919   DBusObjectSubtree *subtree;
    920 
    921   _dbus_assert (tree != NULL);
    922   _dbus_assert (path != NULL);
    923 
    924   /* Find the deepest path that covers the path in the message */
    925   subtree = find_handler (tree, (const char**) path, &exact_match);
    926 
    927   if ((subtree == NULL) || !exact_match)
    928     {
    929       _dbus_verbose ("No object at specified path found\n");
    930       return NULL;
    931     }
    932 
    933   return subtree->user_data;
    934 }
    935 
    936 /**
    937  * Allocates a subtree object.
    938  *
    939  * @param name name to duplicate.
    940  * @returns newly-allocated subtree
    941  */
    942 static DBusObjectSubtree*
    943 allocate_subtree_object (const char *name)
    944 {
    945   int len;
    946   DBusObjectSubtree *subtree;
    947   const size_t front_padding = _DBUS_STRUCT_OFFSET (DBusObjectSubtree, name);
    948 
    949   _dbus_assert (name != NULL);
    950 
    951   len = strlen (name);
    952 
    953   subtree = dbus_malloc (MAX (front_padding + (len + 1), sizeof (DBusObjectSubtree)));
    954 
    955   if (subtree == NULL)
    956     return NULL;
    957 
    958   memcpy (subtree->name, name, len + 1);
    959 
    960   return subtree;
    961 }
    962 
    963 static DBusObjectSubtree*
    964 _dbus_object_subtree_new (const char                  *name,
    965                           const DBusObjectPathVTable  *vtable,
    966                           void                        *user_data)
    967 {
    968   DBusObjectSubtree *subtree;
    969 
    970   subtree = allocate_subtree_object (name);
    971   if (subtree == NULL)
    972     goto oom;
    973 
    974   _dbus_assert (name != NULL);
    975 
    976   subtree->parent = NULL;
    977 
    978   if (vtable)
    979     {
    980       subtree->message_function = vtable->message_function;
    981       subtree->unregister_function = vtable->unregister_function;
    982     }
    983   else
    984     {
    985       subtree->message_function = NULL;
    986       subtree->unregister_function = NULL;
    987     }
    988 
    989   subtree->user_data = user_data;
    990   subtree->refcount.value = 1;
    991   subtree->subtrees = NULL;
    992   subtree->n_subtrees = 0;
    993   subtree->max_subtrees = 0;
    994   subtree->invoke_as_fallback = FALSE;
    995 
    996   return subtree;
    997 
    998  oom:
    999   return NULL;
   1000 }
   1001 
   1002 static DBusObjectSubtree *
   1003 _dbus_object_subtree_ref (DBusObjectSubtree *subtree)
   1004 {
   1005   _dbus_assert (subtree->refcount.value > 0);
   1006   _dbus_atomic_inc (&subtree->refcount);
   1007 
   1008   return subtree;
   1009 }
   1010 
   1011 static void
   1012 _dbus_object_subtree_unref (DBusObjectSubtree *subtree)
   1013 {
   1014   _dbus_assert (subtree->refcount.value > 0);
   1015 
   1016   if (_dbus_atomic_dec (&subtree->refcount) == 1)
   1017     {
   1018       _dbus_assert (subtree->unregister_function == NULL);
   1019       _dbus_assert (subtree->message_function == NULL);
   1020 
   1021       dbus_free (subtree->subtrees);
   1022       dbus_free (subtree);
   1023     }
   1024 }
   1025 
   1026 /**
   1027  * Lists the registered fallback handlers and object path handlers at
   1028  * the given parent_path. The returned array should be freed with
   1029  * dbus_free_string_array().
   1030  *
   1031  * @param tree the object tree
   1032  * @param parent_path the path to list the child handlers of
   1033  * @param child_entries returns #NULL-terminated array of children
   1034  * @returns #FALSE if no memory to allocate the child entries
   1035  */
   1036 dbus_bool_t
   1037 _dbus_object_tree_list_registered_and_unlock (DBusObjectTree *tree,
   1038                                               const char    **parent_path,
   1039                                               char         ***child_entries)
   1040 {
   1041   dbus_bool_t result;
   1042 
   1043   result = _dbus_object_tree_list_registered_unlocked (tree,
   1044                                                        parent_path,
   1045                                                        child_entries);
   1046 
   1047 #ifdef DBUS_BUILD_TESTS
   1048   if (tree->connection)
   1049 #endif
   1050     {
   1051       _dbus_verbose ("unlock\n");
   1052       _dbus_connection_unlock (tree->connection);
   1053     }
   1054 
   1055   return result;
   1056 }
   1057 
   1058 
   1059 /** Set to 1 to get a bunch of spew about disassembling the path string */
   1060 #define VERBOSE_DECOMPOSE 0
   1061 
   1062 /**
   1063  * Decompose an object path.  A path of just "/" is
   1064  * represented as an empty vector of strings.
   1065  * The path need not be nul terminated.
   1066  *
   1067  * @param data the path data
   1068  * @param len  the length of the path string
   1069  * @param path address to store new object path
   1070  * @param path_len length of stored path
   1071  */
   1072 dbus_bool_t
   1073 _dbus_decompose_path (const char*     data,
   1074                       int             len,
   1075                       char         ***path,
   1076                       int            *path_len)
   1077 {
   1078   char **retval;
   1079   int n_components;
   1080   int i, j, comp;
   1081 
   1082   _dbus_assert (data != NULL);
   1083   _dbus_assert (path != NULL);
   1084 
   1085 #if VERBOSE_DECOMPOSE
   1086   _dbus_verbose ("Decomposing path \"%s\"\n",
   1087                  data);
   1088 #endif
   1089 
   1090   n_components = 0;
   1091   if (len > 1) /* if path is not just "/" */
   1092     {
   1093       i = 0;
   1094       while (i < len)
   1095         {
   1096           _dbus_assert (data[i] != '\0');
   1097           if (data[i] == '/')
   1098             n_components += 1;
   1099           ++i;
   1100         }
   1101     }
   1102 
   1103   retval = dbus_new0 (char*, n_components + 1);
   1104 
   1105   if (retval == NULL)
   1106     return FALSE;
   1107 
   1108   comp = 0;
   1109   if (n_components == 0)
   1110     i = 1;
   1111   else
   1112     i = 0;
   1113   while (comp < n_components)
   1114     {
   1115       _dbus_assert (i < len);
   1116 
   1117       if (data[i] == '/')
   1118         ++i;
   1119       j = i;
   1120 
   1121       while (j < len && data[j] != '/')
   1122         ++j;
   1123 
   1124       /* Now [i, j) is the path component */
   1125       _dbus_assert (i < j);
   1126       _dbus_assert (data[i] != '/');
   1127       _dbus_assert (j == len || data[j] == '/');
   1128 
   1129 #if VERBOSE_DECOMPOSE
   1130       _dbus_verbose ("  (component in [%d,%d))\n",
   1131                      i, j);
   1132 #endif
   1133 
   1134       retval[comp] = _dbus_memdup (&data[i], j - i + 1);
   1135       if (retval[comp] == NULL)
   1136         {
   1137           dbus_free_string_array (retval);
   1138           return FALSE;
   1139         }
   1140       retval[comp][j-i] = '\0';
   1141 #if VERBOSE_DECOMPOSE
   1142       _dbus_verbose ("  (component %d = \"%s\")\n",
   1143                      comp, retval[comp]);
   1144 #endif
   1145 
   1146       ++comp;
   1147       i = j;
   1148     }
   1149   _dbus_assert (i == len);
   1150 
   1151   *path = retval;
   1152   if (path_len)
   1153     *path_len = n_components;
   1154 
   1155   return TRUE;
   1156 }
   1157 
   1158 /** @} */
   1159 
   1160 static char*
   1161 flatten_path (const char **path)
   1162 {
   1163   DBusString str;
   1164   char *s;
   1165 
   1166   if (!_dbus_string_init (&str))
   1167     return NULL;
   1168 
   1169   if (path[0] == NULL)
   1170     {
   1171       if (!_dbus_string_append_byte (&str, '/'))
   1172         goto nomem;
   1173     }
   1174   else
   1175     {
   1176       int i;
   1177 
   1178       i = 0;
   1179       while (path[i])
   1180         {
   1181           if (!_dbus_string_append_byte (&str, '/'))
   1182             goto nomem;
   1183 
   1184           if (!_dbus_string_append (&str, path[i]))
   1185             goto nomem;
   1186 
   1187           ++i;
   1188         }
   1189     }
   1190 
   1191   if (!_dbus_string_steal_data (&str, &s))
   1192     goto nomem;
   1193 
   1194   _dbus_string_free (&str);
   1195 
   1196   return s;
   1197 
   1198  nomem:
   1199   _dbus_string_free (&str);
   1200   return NULL;
   1201 }
   1202 
   1203 
   1204 #ifdef DBUS_BUILD_TESTS
   1205 
   1206 #ifndef DOXYGEN_SHOULD_SKIP_THIS
   1207 
   1208 #include "dbus-test.h"
   1209 #include <stdio.h>
   1210 
   1211 typedef enum
   1212 {
   1213   STR_EQUAL,
   1214   STR_PREFIX,
   1215   STR_DIFFERENT
   1216 } StrComparison;
   1217 
   1218 /* Returns TRUE if container is a parent of child
   1219  */
   1220 static StrComparison
   1221 path_contains (const char **container,
   1222                const char **child)
   1223 {
   1224   int i;
   1225 
   1226   i = 0;
   1227   while (child[i] != NULL)
   1228     {
   1229       int v;
   1230 
   1231       if (container[i] == NULL)
   1232         return STR_PREFIX; /* container ran out, child continues;
   1233                             * thus the container is a parent of the
   1234                             * child.
   1235                             */
   1236 
   1237       _dbus_assert (container[i] != NULL);
   1238       _dbus_assert (child[i] != NULL);
   1239 
   1240       v = strcmp (container[i], child[i]);
   1241 
   1242       if (v != 0)
   1243         return STR_DIFFERENT; /* they overlap until here and then are different,
   1244                                * not overlapping
   1245                                */
   1246 
   1247       ++i;
   1248     }
   1249 
   1250   /* Child ran out; if container also did, they are equal;
   1251    * otherwise, the child is a parent of the container.
   1252    */
   1253   if (container[i] == NULL)
   1254     return STR_EQUAL;
   1255   else
   1256     return STR_DIFFERENT;
   1257 }
   1258 
   1259 #if 0
   1260 static void
   1261 spew_subtree_recurse (DBusObjectSubtree *subtree,
   1262                       int                indent)
   1263 {
   1264   int i;
   1265 
   1266   i = 0;
   1267   while (i < indent)
   1268     {
   1269       _dbus_verbose (" ");
   1270       ++i;
   1271     }
   1272 
   1273   _dbus_verbose ("%s (%d children)\n",
   1274                  subtree->name, subtree->n_subtrees);
   1275 
   1276   i = 0;
   1277   while (i < subtree->n_subtrees)
   1278     {
   1279       spew_subtree_recurse (subtree->subtrees[i], indent + 2);
   1280 
   1281       ++i;
   1282     }
   1283 }
   1284 
   1285 static void
   1286 spew_tree (DBusObjectTree *tree)
   1287 {
   1288   spew_subtree_recurse (tree->root, 0);
   1289 }
   1290 #endif
   1291 
   1292 /**
   1293  * Callback data used in tests
   1294  */
   1295 typedef struct
   1296 {
   1297   const char **path; /**< Path */
   1298   dbus_bool_t handler_fallback; /**< true if the handler may be called as fallback */
   1299   dbus_bool_t message_handled; /**< Gets set to true if message handler called */
   1300   dbus_bool_t handler_unregistered; /**< gets set to true if handler is unregistered */
   1301 } TreeTestData;
   1302 
   1303 
   1304 static void
   1305 test_unregister_function (DBusConnection  *connection,
   1306                           void            *user_data)
   1307 {
   1308   TreeTestData *ttd = user_data;
   1309 
   1310   ttd->handler_unregistered = TRUE;
   1311 }
   1312 
   1313 static DBusHandlerResult
   1314 test_message_function (DBusConnection  *connection,
   1315                        DBusMessage     *message,
   1316                        void            *user_data)
   1317 {
   1318   TreeTestData *ttd = user_data;
   1319 
   1320   ttd->message_handled = TRUE;
   1321 
   1322   return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
   1323 }
   1324 
   1325 static dbus_bool_t
   1326 do_register (DBusObjectTree *tree,
   1327              const char    **path,
   1328              dbus_bool_t     fallback,
   1329              int             i,
   1330              TreeTestData   *tree_test_data)
   1331 {
   1332   DBusObjectPathVTable vtable = { test_unregister_function,
   1333                                   test_message_function, NULL };
   1334 
   1335   tree_test_data[i].message_handled = FALSE;
   1336   tree_test_data[i].handler_unregistered = FALSE;
   1337   tree_test_data[i].handler_fallback = fallback;
   1338   tree_test_data[i].path = path;
   1339 
   1340   if (!_dbus_object_tree_register (tree, fallback, path,
   1341                                    &vtable,
   1342                                    &tree_test_data[i],
   1343                                    NULL))
   1344     return FALSE;
   1345 
   1346   _dbus_assert (_dbus_object_tree_get_user_data_unlocked (tree, path) ==
   1347                 &tree_test_data[i]);
   1348 
   1349   return TRUE;
   1350 }
   1351 
   1352 static dbus_bool_t
   1353 do_test_dispatch (DBusObjectTree *tree,
   1354                   const char    **path,
   1355                   int             i,
   1356                   TreeTestData   *tree_test_data,
   1357                   int             n_test_data)
   1358 {
   1359   DBusMessage *message;
   1360   int j;
   1361   DBusHandlerResult result;
   1362   char *flat;
   1363 
   1364   message = NULL;
   1365 
   1366   flat = flatten_path (path);
   1367   if (flat == NULL)
   1368     goto oom;
   1369 
   1370   message = dbus_message_new_method_call (NULL,
   1371                                           flat,
   1372                                           "org.freedesktop.TestInterface",
   1373                                           "Foo");
   1374   dbus_free (flat);
   1375   if (message == NULL)
   1376     goto oom;
   1377 
   1378   j = 0;
   1379   while (j < n_test_data)
   1380     {
   1381       tree_test_data[j].message_handled = FALSE;
   1382       ++j;
   1383     }
   1384 
   1385   result = _dbus_object_tree_dispatch_and_unlock (tree, message);
   1386   if (result == DBUS_HANDLER_RESULT_NEED_MEMORY)
   1387     goto oom;
   1388 
   1389   _dbus_assert (tree_test_data[i].message_handled);
   1390 
   1391   j = 0;
   1392   while (j < n_test_data)
   1393     {
   1394       if (tree_test_data[j].message_handled)
   1395 	{
   1396 	  if (tree_test_data[j].handler_fallback)
   1397 	    _dbus_assert (path_contains (tree_test_data[j].path,
   1398 					 path) != STR_DIFFERENT);
   1399 	  else
   1400 	    _dbus_assert (path_contains (tree_test_data[j].path, path) == STR_EQUAL);
   1401 	}
   1402       else
   1403 	{
   1404 	  if (tree_test_data[j].handler_fallback)
   1405 	    _dbus_assert (path_contains (tree_test_data[j].path,
   1406 					 path) == STR_DIFFERENT);
   1407 	  else
   1408 	    _dbus_assert (path_contains (tree_test_data[j].path, path) != STR_EQUAL);
   1409 	}
   1410 
   1411       ++j;
   1412     }
   1413 
   1414   dbus_message_unref (message);
   1415 
   1416   return TRUE;
   1417 
   1418  oom:
   1419   if (message)
   1420     dbus_message_unref (message);
   1421   return FALSE;
   1422 }
   1423 
   1424 static size_t
   1425 string_array_length (const char **array)
   1426 {
   1427   size_t i;
   1428   for (i = 0; array[i]; i++) ;
   1429   return i;
   1430 }
   1431 
   1432 typedef struct
   1433 {
   1434   const char *path;
   1435   const char *result[20];
   1436 } DecomposePathTest;
   1437 
   1438 static DecomposePathTest decompose_tests[] = {
   1439   { "/foo", { "foo", NULL } },
   1440   { "/foo/bar", { "foo", "bar", NULL } },
   1441   { "/", { NULL } },
   1442   { "/a/b", { "a", "b", NULL } },
   1443   { "/a/b/c", { "a", "b", "c", NULL } },
   1444   { "/a/b/c/d", { "a", "b", "c", "d", NULL } },
   1445   { "/foo/bar/q", { "foo", "bar", "q", NULL } },
   1446   { "/foo/bar/this/is/longer", { "foo", "bar", "this", "is", "longer", NULL } }
   1447 };
   1448 
   1449 static dbus_bool_t
   1450 run_decompose_tests (void)
   1451 {
   1452   int i;
   1453 
   1454   i = 0;
   1455   while (i < _DBUS_N_ELEMENTS (decompose_tests))
   1456     {
   1457       char **result;
   1458       int    result_len;
   1459       int    expected_len;
   1460 
   1461       if (!_dbus_decompose_path (decompose_tests[i].path,
   1462                                  strlen (decompose_tests[i].path),
   1463                                  &result, &result_len))
   1464         return FALSE;
   1465 
   1466       expected_len = string_array_length (decompose_tests[i].result);
   1467 
   1468       if (result_len != (int) string_array_length ((const char**)result) ||
   1469           expected_len != result_len ||
   1470           path_contains (decompose_tests[i].result,
   1471                          (const char**) result) != STR_EQUAL)
   1472         {
   1473           int real_len = string_array_length ((const char**)result);
   1474           _dbus_warn ("Expected decompose of %s to have len %d, returned %d, appears to have %d\n",
   1475                       decompose_tests[i].path, expected_len, result_len,
   1476                       real_len);
   1477           _dbus_warn ("Decompose resulted in elements: { ");
   1478           i = 0;
   1479           while (i < real_len)
   1480             {
   1481               _dbus_warn ("\"%s\"%s", result[i],
   1482                           (i + 1) == real_len ? "" : ", ");
   1483               ++i;
   1484             }
   1485           _dbus_warn ("}\n");
   1486           _dbus_assert_not_reached ("path decompose failed\n");
   1487         }
   1488 
   1489       dbus_free_string_array (result);
   1490 
   1491       ++i;
   1492     }
   1493 
   1494   return TRUE;
   1495 }
   1496 
   1497 static dbus_bool_t
   1498 object_tree_test_iteration (void *data)
   1499 {
   1500   const char *path0[] = { NULL };
   1501   const char *path1[] = { "foo", NULL };
   1502   const char *path2[] = { "foo", "bar", NULL };
   1503   const char *path3[] = { "foo", "bar", "baz", NULL };
   1504   const char *path4[] = { "foo", "bar", "boo", NULL };
   1505   const char *path5[] = { "blah", NULL };
   1506   const char *path6[] = { "blah", "boof", NULL };
   1507   const char *path7[] = { "blah", "boof", "this", "is", "really", "long", NULL };
   1508   const char *path8[] = { "childless", NULL };
   1509   DBusObjectTree *tree;
   1510   TreeTestData tree_test_data[9];
   1511   int i;
   1512   dbus_bool_t exact_match;
   1513 
   1514   if (!run_decompose_tests ())
   1515     return FALSE;
   1516 
   1517   tree = NULL;
   1518 
   1519   tree = _dbus_object_tree_new (NULL);
   1520   if (tree == NULL)
   1521     goto out;
   1522 
   1523   if (!do_register (tree, path0, TRUE, 0, tree_test_data))
   1524     goto out;
   1525 
   1526   _dbus_assert (find_subtree (tree, path0, NULL));
   1527   _dbus_assert (!find_subtree (tree, path1, NULL));
   1528   _dbus_assert (!find_subtree (tree, path2, NULL));
   1529   _dbus_assert (!find_subtree (tree, path3, NULL));
   1530   _dbus_assert (!find_subtree (tree, path4, NULL));
   1531   _dbus_assert (!find_subtree (tree, path5, NULL));
   1532   _dbus_assert (!find_subtree (tree, path6, NULL));
   1533   _dbus_assert (!find_subtree (tree, path7, NULL));
   1534   _dbus_assert (!find_subtree (tree, path8, NULL));
   1535 
   1536   _dbus_assert (find_handler (tree, path0, &exact_match) && exact_match);
   1537   _dbus_assert (find_handler (tree, path1, &exact_match) == tree->root && !exact_match);
   1538   _dbus_assert (find_handler (tree, path2, &exact_match) == tree->root && !exact_match);
   1539   _dbus_assert (find_handler (tree, path3, &exact_match) == tree->root && !exact_match);
   1540   _dbus_assert (find_handler (tree, path4, &exact_match) == tree->root && !exact_match);
   1541   _dbus_assert (find_handler (tree, path5, &exact_match) == tree->root && !exact_match);
   1542   _dbus_assert (find_handler (tree, path6, &exact_match) == tree->root && !exact_match);
   1543   _dbus_assert (find_handler (tree, path7, &exact_match) == tree->root && !exact_match);
   1544   _dbus_assert (find_handler (tree, path8, &exact_match) == tree->root && !exact_match);
   1545 
   1546   if (!do_register (tree, path1, TRUE, 1, tree_test_data))
   1547     goto out;
   1548 
   1549   _dbus_assert (find_subtree (tree, path0, NULL));
   1550   _dbus_assert (find_subtree (tree, path1, NULL));
   1551   _dbus_assert (!find_subtree (tree, path2, NULL));
   1552   _dbus_assert (!find_subtree (tree, path3, NULL));
   1553   _dbus_assert (!find_subtree (tree, path4, NULL));
   1554   _dbus_assert (!find_subtree (tree, path5, NULL));
   1555   _dbus_assert (!find_subtree (tree, path6, NULL));
   1556   _dbus_assert (!find_subtree (tree, path7, NULL));
   1557   _dbus_assert (!find_subtree (tree, path8, NULL));
   1558 
   1559   _dbus_assert (find_handler (tree, path0, &exact_match) &&  exact_match);
   1560   _dbus_assert (find_handler (tree, path1, &exact_match) &&  exact_match);
   1561   _dbus_assert (find_handler (tree, path2, &exact_match) && !exact_match);
   1562   _dbus_assert (find_handler (tree, path3, &exact_match) && !exact_match);
   1563   _dbus_assert (find_handler (tree, path4, &exact_match) && !exact_match);
   1564   _dbus_assert (find_handler (tree, path5, &exact_match) == tree->root && !exact_match);
   1565   _dbus_assert (find_handler (tree, path6, &exact_match) == tree->root && !exact_match);
   1566   _dbus_assert (find_handler (tree, path7, &exact_match) == tree->root && !exact_match);
   1567   _dbus_assert (find_handler (tree, path8, &exact_match) == tree->root && !exact_match);
   1568 
   1569   if (!do_register (tree, path2, TRUE, 2, tree_test_data))
   1570     goto out;
   1571 
   1572   _dbus_assert (find_subtree (tree, path1, NULL));
   1573   _dbus_assert (find_subtree (tree, path2, NULL));
   1574   _dbus_assert (!find_subtree (tree, path3, NULL));
   1575   _dbus_assert (!find_subtree (tree, path4, NULL));
   1576   _dbus_assert (!find_subtree (tree, path5, NULL));
   1577   _dbus_assert (!find_subtree (tree, path6, NULL));
   1578   _dbus_assert (!find_subtree (tree, path7, NULL));
   1579   _dbus_assert (!find_subtree (tree, path8, NULL));
   1580 
   1581   if (!do_register (tree, path3, TRUE, 3, tree_test_data))
   1582     goto out;
   1583 
   1584   _dbus_assert (find_subtree (tree, path0, NULL));
   1585   _dbus_assert (find_subtree (tree, path1, NULL));
   1586   _dbus_assert (find_subtree (tree, path2, NULL));
   1587   _dbus_assert (find_subtree (tree, path3, NULL));
   1588   _dbus_assert (!find_subtree (tree, path4, NULL));
   1589   _dbus_assert (!find_subtree (tree, path5, NULL));
   1590   _dbus_assert (!find_subtree (tree, path6, NULL));
   1591   _dbus_assert (!find_subtree (tree, path7, NULL));
   1592   _dbus_assert (!find_subtree (tree, path8, NULL));
   1593 
   1594   if (!do_register (tree, path4, TRUE, 4, tree_test_data))
   1595     goto out;
   1596 
   1597   _dbus_assert (find_subtree (tree, path0, NULL));
   1598   _dbus_assert (find_subtree (tree, path1, NULL));
   1599   _dbus_assert (find_subtree (tree, path2, NULL));
   1600   _dbus_assert (find_subtree (tree, path3, NULL));
   1601   _dbus_assert (find_subtree (tree, path4, NULL));
   1602   _dbus_assert (!find_subtree (tree, path5, NULL));
   1603   _dbus_assert (!find_subtree (tree, path6, NULL));
   1604   _dbus_assert (!find_subtree (tree, path7, NULL));
   1605   _dbus_assert (!find_subtree (tree, path8, NULL));
   1606 
   1607   if (!do_register (tree, path5, TRUE, 5, tree_test_data))
   1608     goto out;
   1609 
   1610   _dbus_assert (find_subtree (tree, path0, NULL));
   1611   _dbus_assert (find_subtree (tree, path1, NULL));
   1612   _dbus_assert (find_subtree (tree, path2, NULL));
   1613   _dbus_assert (find_subtree (tree, path3, NULL));
   1614   _dbus_assert (find_subtree (tree, path4, NULL));
   1615   _dbus_assert (find_subtree (tree, path5, NULL));
   1616   _dbus_assert (!find_subtree (tree, path6, NULL));
   1617   _dbus_assert (!find_subtree (tree, path7, NULL));
   1618   _dbus_assert (!find_subtree (tree, path8, NULL));
   1619 
   1620   _dbus_assert (find_handler (tree, path0, &exact_match) == tree->root &&  exact_match);
   1621   _dbus_assert (find_handler (tree, path1, &exact_match) != tree->root &&  exact_match);
   1622   _dbus_assert (find_handler (tree, path2, &exact_match) != tree->root &&  exact_match);
   1623   _dbus_assert (find_handler (tree, path3, &exact_match) != tree->root &&  exact_match);
   1624   _dbus_assert (find_handler (tree, path4, &exact_match) != tree->root &&  exact_match);
   1625   _dbus_assert (find_handler (tree, path5, &exact_match) != tree->root &&  exact_match);
   1626   _dbus_assert (find_handler (tree, path6, &exact_match) != tree->root && !exact_match);
   1627   _dbus_assert (find_handler (tree, path7, &exact_match) != tree->root && !exact_match);
   1628   _dbus_assert (find_handler (tree, path8, &exact_match) == tree->root && !exact_match);
   1629 
   1630   if (!do_register (tree, path6, TRUE, 6, tree_test_data))
   1631     goto out;
   1632 
   1633   _dbus_assert (find_subtree (tree, path0, NULL));
   1634   _dbus_assert (find_subtree (tree, path1, NULL));
   1635   _dbus_assert (find_subtree (tree, path2, NULL));
   1636   _dbus_assert (find_subtree (tree, path3, NULL));
   1637   _dbus_assert (find_subtree (tree, path4, NULL));
   1638   _dbus_assert (find_subtree (tree, path5, NULL));
   1639   _dbus_assert (find_subtree (tree, path6, NULL));
   1640   _dbus_assert (!find_subtree (tree, path7, NULL));
   1641   _dbus_assert (!find_subtree (tree, path8, NULL));
   1642 
   1643   if (!do_register (tree, path7, TRUE, 7, tree_test_data))
   1644     goto out;
   1645 
   1646   _dbus_assert (find_subtree (tree, path0, NULL));
   1647   _dbus_assert (find_subtree (tree, path1, NULL));
   1648   _dbus_assert (find_subtree (tree, path2, NULL));
   1649   _dbus_assert (find_subtree (tree, path3, NULL));
   1650   _dbus_assert (find_subtree (tree, path4, NULL));
   1651   _dbus_assert (find_subtree (tree, path5, NULL));
   1652   _dbus_assert (find_subtree (tree, path6, NULL));
   1653   _dbus_assert (find_subtree (tree, path7, NULL));
   1654   _dbus_assert (!find_subtree (tree, path8, NULL));
   1655 
   1656   if (!do_register (tree, path8, TRUE, 8, tree_test_data))
   1657     goto out;
   1658 
   1659   _dbus_assert (find_subtree (tree, path0, NULL));
   1660   _dbus_assert (find_subtree (tree, path1, NULL));
   1661   _dbus_assert (find_subtree (tree, path2, NULL));
   1662   _dbus_assert (find_subtree (tree, path3, NULL));
   1663   _dbus_assert (find_subtree (tree, path4, NULL));
   1664   _dbus_assert (find_subtree (tree, path5, NULL));
   1665   _dbus_assert (find_subtree (tree, path6, NULL));
   1666   _dbus_assert (find_subtree (tree, path7, NULL));
   1667   _dbus_assert (find_subtree (tree, path8, NULL));
   1668 
   1669   _dbus_assert (find_handler (tree, path0, &exact_match) == tree->root &&  exact_match);
   1670   _dbus_assert (find_handler (tree, path1, &exact_match) != tree->root && exact_match);
   1671   _dbus_assert (find_handler (tree, path2, &exact_match) != tree->root && exact_match);
   1672   _dbus_assert (find_handler (tree, path3, &exact_match) != tree->root && exact_match);
   1673   _dbus_assert (find_handler (tree, path4, &exact_match) != tree->root && exact_match);
   1674   _dbus_assert (find_handler (tree, path5, &exact_match) != tree->root && exact_match);
   1675   _dbus_assert (find_handler (tree, path6, &exact_match) != tree->root && exact_match);
   1676   _dbus_assert (find_handler (tree, path7, &exact_match) != tree->root && exact_match);
   1677   _dbus_assert (find_handler (tree, path8, &exact_match) != tree->root && exact_match);
   1678 
   1679   /* test the list_registered function */
   1680 
   1681   {
   1682     const char *root[] = { NULL };
   1683     char **child_entries;
   1684     int nb;
   1685 
   1686     _dbus_object_tree_list_registered_unlocked (tree, path1, &child_entries);
   1687     if (child_entries != NULL)
   1688       {
   1689 	nb = string_array_length ((const char**)child_entries);
   1690 	_dbus_assert (nb == 1);
   1691 	dbus_free_string_array (child_entries);
   1692       }
   1693 
   1694     _dbus_object_tree_list_registered_unlocked (tree, path2, &child_entries);
   1695     if (child_entries != NULL)
   1696       {
   1697 	nb = string_array_length ((const char**)child_entries);
   1698 	_dbus_assert (nb == 2);
   1699 	dbus_free_string_array (child_entries);
   1700       }
   1701 
   1702     _dbus_object_tree_list_registered_unlocked (tree, path8, &child_entries);
   1703     if (child_entries != NULL)
   1704       {
   1705 	nb = string_array_length ((const char**)child_entries);
   1706 	_dbus_assert (nb == 0);
   1707 	dbus_free_string_array (child_entries);
   1708       }
   1709 
   1710     _dbus_object_tree_list_registered_unlocked (tree, root, &child_entries);
   1711     if (child_entries != NULL)
   1712       {
   1713 	nb = string_array_length ((const char**)child_entries);
   1714 	_dbus_assert (nb == 3);
   1715 	dbus_free_string_array (child_entries);
   1716       }
   1717   }
   1718 
   1719   /* Check that destroying tree calls unregister funcs */
   1720   _dbus_object_tree_unref (tree);
   1721 
   1722   i = 0;
   1723   while (i < (int) _DBUS_N_ELEMENTS (tree_test_data))
   1724     {
   1725       _dbus_assert (tree_test_data[i].handler_unregistered);
   1726       _dbus_assert (!tree_test_data[i].message_handled);
   1727       ++i;
   1728     }
   1729 
   1730   /* Now start again and try the individual unregister function */
   1731   tree = _dbus_object_tree_new (NULL);
   1732   if (tree == NULL)
   1733     goto out;
   1734 
   1735   if (!do_register (tree, path0, TRUE, 0, tree_test_data))
   1736     goto out;
   1737   if (!do_register (tree, path1, TRUE, 1, tree_test_data))
   1738     goto out;
   1739   if (!do_register (tree, path2, TRUE, 2, tree_test_data))
   1740     goto out;
   1741   if (!do_register (tree, path3, TRUE, 3, tree_test_data))
   1742     goto out;
   1743   if (!do_register (tree, path4, TRUE, 4, tree_test_data))
   1744     goto out;
   1745   if (!do_register (tree, path5, TRUE, 5, tree_test_data))
   1746     goto out;
   1747   if (!do_register (tree, path6, TRUE, 6, tree_test_data))
   1748     goto out;
   1749   if (!do_register (tree, path7, TRUE, 7, tree_test_data))
   1750     goto out;
   1751   if (!do_register (tree, path8, TRUE, 8, tree_test_data))
   1752     goto out;
   1753 
   1754   _dbus_object_tree_unregister_and_unlock (tree, path0);
   1755   _dbus_assert (_dbus_object_tree_get_user_data_unlocked (tree, path0) == NULL);
   1756 
   1757   _dbus_assert (!find_subtree (tree, path0, NULL));
   1758   _dbus_assert (find_subtree (tree, path1, NULL));
   1759   _dbus_assert (find_subtree (tree, path2, NULL));
   1760   _dbus_assert (find_subtree (tree, path3, NULL));
   1761   _dbus_assert (find_subtree (tree, path4, NULL));
   1762   _dbus_assert (find_subtree (tree, path5, NULL));
   1763   _dbus_assert (find_subtree (tree, path6, NULL));
   1764   _dbus_assert (find_subtree (tree, path7, NULL));
   1765   _dbus_assert (find_subtree (tree, path8, NULL));
   1766 
   1767   _dbus_object_tree_unregister_and_unlock (tree, path1);
   1768   _dbus_assert (_dbus_object_tree_get_user_data_unlocked (tree, path1) == NULL);
   1769 
   1770   _dbus_assert (!find_subtree (tree, path0, NULL));
   1771   _dbus_assert (!find_subtree (tree, path1, NULL));
   1772   _dbus_assert (find_subtree (tree, path2, NULL));
   1773   _dbus_assert (find_subtree (tree, path3, NULL));
   1774   _dbus_assert (find_subtree (tree, path4, NULL));
   1775   _dbus_assert (find_subtree (tree, path5, NULL));
   1776   _dbus_assert (find_subtree (tree, path6, NULL));
   1777   _dbus_assert (find_subtree (tree, path7, NULL));
   1778   _dbus_assert (find_subtree (tree, path8, NULL));
   1779 
   1780   _dbus_object_tree_unregister_and_unlock (tree, path2);
   1781   _dbus_assert (_dbus_object_tree_get_user_data_unlocked (tree, path2) == NULL);
   1782 
   1783   _dbus_assert (!find_subtree (tree, path0, NULL));
   1784   _dbus_assert (!find_subtree (tree, path1, NULL));
   1785   _dbus_assert (!find_subtree (tree, path2, NULL));
   1786   _dbus_assert (find_subtree (tree, path3, NULL));
   1787   _dbus_assert (find_subtree (tree, path4, NULL));
   1788   _dbus_assert (find_subtree (tree, path5, NULL));
   1789   _dbus_assert (find_subtree (tree, path6, NULL));
   1790   _dbus_assert (find_subtree (tree, path7, NULL));
   1791   _dbus_assert (find_subtree (tree, path8, NULL));
   1792 
   1793   _dbus_object_tree_unregister_and_unlock (tree, path3);
   1794   _dbus_assert (_dbus_object_tree_get_user_data_unlocked (tree, path3) == NULL);
   1795 
   1796   _dbus_assert (!find_subtree (tree, path0, NULL));
   1797   _dbus_assert (!find_subtree (tree, path1, NULL));
   1798   _dbus_assert (!find_subtree (tree, path2, NULL));
   1799   _dbus_assert (!find_subtree (tree, path3, NULL));
   1800   _dbus_assert (find_subtree (tree, path4, NULL));
   1801   _dbus_assert (find_subtree (tree, path5, NULL));
   1802   _dbus_assert (find_subtree (tree, path6, NULL));
   1803   _dbus_assert (find_subtree (tree, path7, NULL));
   1804   _dbus_assert (find_subtree (tree, path8, NULL));
   1805 
   1806   _dbus_object_tree_unregister_and_unlock (tree, path4);
   1807   _dbus_assert (_dbus_object_tree_get_user_data_unlocked (tree, path4) == NULL);
   1808 
   1809   _dbus_assert (!find_subtree (tree, path0, NULL));
   1810   _dbus_assert (!find_subtree (tree, path1, NULL));
   1811   _dbus_assert (!find_subtree (tree, path2, NULL));
   1812   _dbus_assert (!find_subtree (tree, path3, NULL));
   1813   _dbus_assert (!find_subtree (tree, path4, NULL));
   1814   _dbus_assert (find_subtree (tree, path5, NULL));
   1815   _dbus_assert (find_subtree (tree, path6, NULL));
   1816   _dbus_assert (find_subtree (tree, path7, NULL));
   1817   _dbus_assert (find_subtree (tree, path8, NULL));
   1818 
   1819   _dbus_object_tree_unregister_and_unlock (tree, path5);
   1820   _dbus_assert (_dbus_object_tree_get_user_data_unlocked (tree, path5) == NULL);
   1821 
   1822   _dbus_assert (!find_subtree (tree, path0, NULL));
   1823   _dbus_assert (!find_subtree (tree, path1, NULL));
   1824   _dbus_assert (!find_subtree (tree, path2, NULL));
   1825   _dbus_assert (!find_subtree (tree, path3, NULL));
   1826   _dbus_assert (!find_subtree (tree, path4, NULL));
   1827   _dbus_assert (!find_subtree (tree, path5, NULL));
   1828   _dbus_assert (find_subtree (tree, path6, NULL));
   1829   _dbus_assert (find_subtree (tree, path7, NULL));
   1830   _dbus_assert (find_subtree (tree, path8, NULL));
   1831 
   1832   _dbus_object_tree_unregister_and_unlock (tree, path6);
   1833   _dbus_assert (_dbus_object_tree_get_user_data_unlocked (tree, path6) == NULL);
   1834 
   1835   _dbus_assert (!find_subtree (tree, path0, NULL));
   1836   _dbus_assert (!find_subtree (tree, path1, NULL));
   1837   _dbus_assert (!find_subtree (tree, path2, NULL));
   1838   _dbus_assert (!find_subtree (tree, path3, NULL));
   1839   _dbus_assert (!find_subtree (tree, path4, NULL));
   1840   _dbus_assert (!find_subtree (tree, path5, NULL));
   1841   _dbus_assert (!find_subtree (tree, path6, NULL));
   1842   _dbus_assert (find_subtree (tree, path7, NULL));
   1843   _dbus_assert (find_subtree (tree, path8, NULL));
   1844 
   1845   _dbus_object_tree_unregister_and_unlock (tree, path7);
   1846   _dbus_assert (_dbus_object_tree_get_user_data_unlocked (tree, path7) == NULL);
   1847 
   1848   _dbus_assert (!find_subtree (tree, path0, NULL));
   1849   _dbus_assert (!find_subtree (tree, path1, NULL));
   1850   _dbus_assert (!find_subtree (tree, path2, NULL));
   1851   _dbus_assert (!find_subtree (tree, path3, NULL));
   1852   _dbus_assert (!find_subtree (tree, path4, NULL));
   1853   _dbus_assert (!find_subtree (tree, path5, NULL));
   1854   _dbus_assert (!find_subtree (tree, path6, NULL));
   1855   _dbus_assert (!find_subtree (tree, path7, NULL));
   1856   _dbus_assert (find_subtree (tree, path8, NULL));
   1857 
   1858   _dbus_object_tree_unregister_and_unlock (tree, path8);
   1859   _dbus_assert (_dbus_object_tree_get_user_data_unlocked (tree, path8) == NULL);
   1860 
   1861   _dbus_assert (!find_subtree (tree, path0, NULL));
   1862   _dbus_assert (!find_subtree (tree, path1, NULL));
   1863   _dbus_assert (!find_subtree (tree, path2, NULL));
   1864   _dbus_assert (!find_subtree (tree, path3, NULL));
   1865   _dbus_assert (!find_subtree (tree, path4, NULL));
   1866   _dbus_assert (!find_subtree (tree, path5, NULL));
   1867   _dbus_assert (!find_subtree (tree, path6, NULL));
   1868   _dbus_assert (!find_subtree (tree, path7, NULL));
   1869   _dbus_assert (!find_subtree (tree, path8, NULL));
   1870 
   1871   i = 0;
   1872   while (i < (int) _DBUS_N_ELEMENTS (tree_test_data))
   1873     {
   1874       _dbus_assert (tree_test_data[i].handler_unregistered);
   1875       _dbus_assert (!tree_test_data[i].message_handled);
   1876       ++i;
   1877     }
   1878 
   1879   /* Register it all again, and test dispatch */
   1880 
   1881   if (!do_register (tree, path0, TRUE, 0, tree_test_data))
   1882     goto out;
   1883   if (!do_register (tree, path1, FALSE, 1, tree_test_data))
   1884     goto out;
   1885   if (!do_register (tree, path2, TRUE, 2, tree_test_data))
   1886     goto out;
   1887   if (!do_register (tree, path3, TRUE, 3, tree_test_data))
   1888     goto out;
   1889   if (!do_register (tree, path4, TRUE, 4, tree_test_data))
   1890     goto out;
   1891   if (!do_register (tree, path5, TRUE, 5, tree_test_data))
   1892     goto out;
   1893   if (!do_register (tree, path6, FALSE, 6, tree_test_data))
   1894     goto out;
   1895   if (!do_register (tree, path7, TRUE, 7, tree_test_data))
   1896     goto out;
   1897   if (!do_register (tree, path8, TRUE, 8, tree_test_data))
   1898     goto out;
   1899 
   1900 #if 0
   1901   spew_tree (tree);
   1902 #endif
   1903 
   1904   if (!do_test_dispatch (tree, path0, 0, tree_test_data, _DBUS_N_ELEMENTS (tree_test_data)))
   1905     goto out;
   1906   if (!do_test_dispatch (tree, path1, 1, tree_test_data, _DBUS_N_ELEMENTS (tree_test_data)))
   1907     goto out;
   1908   if (!do_test_dispatch (tree, path2, 2, tree_test_data, _DBUS_N_ELEMENTS (tree_test_data)))
   1909     goto out;
   1910   if (!do_test_dispatch (tree, path3, 3, tree_test_data, _DBUS_N_ELEMENTS (tree_test_data)))
   1911     goto out;
   1912   if (!do_test_dispatch (tree, path4, 4, tree_test_data, _DBUS_N_ELEMENTS (tree_test_data)))
   1913     goto out;
   1914   if (!do_test_dispatch (tree, path5, 5, tree_test_data, _DBUS_N_ELEMENTS (tree_test_data)))
   1915     goto out;
   1916   if (!do_test_dispatch (tree, path6, 6, tree_test_data, _DBUS_N_ELEMENTS (tree_test_data)))
   1917     goto out;
   1918   if (!do_test_dispatch (tree, path7, 7, tree_test_data, _DBUS_N_ELEMENTS (tree_test_data)))
   1919     goto out;
   1920   if (!do_test_dispatch (tree, path8, 8, tree_test_data, _DBUS_N_ELEMENTS (tree_test_data)))
   1921     goto out;
   1922 
   1923  out:
   1924   if (tree)
   1925     {
   1926       /* test ref */
   1927       _dbus_object_tree_ref (tree);
   1928       _dbus_object_tree_unref (tree);
   1929       _dbus_object_tree_unref (tree);
   1930     }
   1931 
   1932   return TRUE;
   1933 }
   1934 
   1935 /**
   1936  * @ingroup DBusObjectTree
   1937  * Unit test for DBusObjectTree
   1938  * @returns #TRUE on success.
   1939  */
   1940 dbus_bool_t
   1941 _dbus_object_tree_test (void)
   1942 {
   1943   _dbus_test_oom_handling ("object tree",
   1944                            object_tree_test_iteration,
   1945                            NULL);
   1946 
   1947   return TRUE;
   1948 }
   1949 
   1950 #endif /* !DOXYGEN_SHOULD_SKIP_THIS */
   1951 
   1952 #endif /* DBUS_BUILD_TESTS */
   1953