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