Main Page | Modules | Data Structures | File List | Data Fields | Related Pages

dbus-object-tree.c

00001 /* -*- mode: C; c-file-style: "gnu" -*- */
00002 /* dbus-object-tree.c  DBusObjectTree (internals of DBusConnection)
00003  *
00004  * Copyright (C) 2003  Red Hat Inc.
00005  *
00006  * Licensed under the Academic Free License version 2.0
00007  *
00008  * This program is free software; you can redistribute it and/or modify
00009  * it under the terms of the GNU General Public License as published by
00010  * the Free Software Foundation; either version 2 of the License, or
00011  * (at your option) any later version.
00012  *
00013  * This program is distributed in the hope that it will be useful,
00014  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00015  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00016  * GNU General Public License for more details.
00017  *
00018  * You should have received a copy of the GNU General Public License
00019  * along with this program; if not, write to the Free Software
00020  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00021  *
00022  */
00023 #include "dbus-object-tree.h"
00024 #include "dbus-connection-internal.h"
00025 #include "dbus-internals.h"
00026 #include "dbus-hash.h"
00027 #include "dbus-protocol.h"
00028 #include "dbus-string.h"
00029 #include <string.h>
00030 #include <stdlib.h>
00031 
00044 typedef struct DBusObjectSubtree DBusObjectSubtree;
00045 
00046 static DBusObjectSubtree* _dbus_object_subtree_new   (const char                  *name,
00047                                                       const DBusObjectPathVTable  *vtable,
00048                                                       void                        *user_data);
00049 static DBusObjectSubtree* _dbus_object_subtree_ref   (DBusObjectSubtree           *subtree);
00050 static void               _dbus_object_subtree_unref (DBusObjectSubtree           *subtree);
00051 
00055 struct DBusObjectTree
00056 {
00057   int                 refcount;   
00058   DBusConnection     *connection; 
00060   DBusObjectSubtree  *root;       
00061 };
00062 
00068 struct DBusObjectSubtree
00069 {
00070   DBusAtomic                         refcount;            
00071   DBusObjectSubtree                 *parent;              
00072   DBusObjectPathUnregisterFunction   unregister_function; 
00073   DBusObjectPathMessageFunction      message_function;    
00074   void                              *user_data;           
00075   DBusObjectSubtree                **subtrees;            
00076   int                                n_subtrees;          
00077   unsigned int                       subtrees_sorted : 1; 
00078   unsigned int                       invoke_as_fallback : 1; 
00079   char                               name[1]; 
00080 };
00081 
00089 DBusObjectTree*
00090 _dbus_object_tree_new (DBusConnection *connection)
00091 {
00092   DBusObjectTree *tree;
00093 
00094   /* the connection passed in here isn't fully constructed,
00095    * so don't do anything more than store a pointer to
00096    * it
00097    */
00098 
00099   tree = dbus_new0 (DBusObjectTree, 1);
00100   if (tree == NULL)
00101     goto oom;
00102 
00103   tree->refcount = 1;
00104   tree->connection = connection;
00105   tree->root = _dbus_object_subtree_new ("/", NULL, NULL);
00106   if (tree->root == NULL)
00107     goto oom;
00108   tree->root->invoke_as_fallback = TRUE;
00109   
00110   return tree;
00111 
00112  oom:
00113   if (tree)
00114     {
00115       dbus_free (tree);
00116     }
00117 
00118   return NULL;
00119 }
00120 
00126 DBusObjectTree *
00127 _dbus_object_tree_ref (DBusObjectTree *tree)
00128 {
00129   _dbus_assert (tree->refcount > 0);
00130 
00131   tree->refcount += 1;
00132 
00133   return tree;
00134 }
00135 
00140 void
00141 _dbus_object_tree_unref (DBusObjectTree *tree)
00142 {
00143   _dbus_assert (tree->refcount > 0);
00144 
00145   tree->refcount -= 1;
00146 
00147   if (tree->refcount == 0)
00148     {
00149       _dbus_object_tree_free_all_unlocked (tree);
00150 
00151       dbus_free (tree);
00152     }
00153 }
00154 
00155 static int
00156 subtree_cmp (DBusObjectSubtree *subtree_a,
00157              DBusObjectSubtree *subtree_b)
00158 {
00159   return strcmp (subtree_a->name, subtree_b->name);
00160 }
00161 
00162 static int
00163 subtree_qsort_cmp (const void *a,
00164                    const void *b)
00165 {
00166   DBusObjectSubtree **subtree_a_p = (void*) a;
00167   DBusObjectSubtree **subtree_b_p = (void*) b;
00168 
00169   return subtree_cmp (*subtree_a_p, *subtree_b_p);
00170 }
00171 
00172 static void
00173 ensure_sorted (DBusObjectSubtree *subtree)
00174 {
00175   if (subtree->subtrees && !subtree->subtrees_sorted)
00176     {
00177       qsort (subtree->subtrees,
00178              subtree->n_subtrees,
00179              sizeof (DBusObjectSubtree*),
00180              subtree_qsort_cmp);
00181       subtree->subtrees_sorted = TRUE;
00182     }
00183 }
00184 
00188 #define VERBOSE_FIND 0
00189 
00190 static DBusObjectSubtree*
00191 find_subtree_recurse (DBusObjectSubtree  *subtree,
00192                       const char        **path,
00193                       dbus_bool_t         return_deepest_match,
00194                       dbus_bool_t         create_if_not_found,
00195                       int                *index_in_parent)
00196 {
00197   int i;
00198 
00199   _dbus_assert (!(return_deepest_match && create_if_not_found));
00200 
00201   if (path[0] == NULL)
00202     {
00203 #if VERBOSE_FIND
00204       _dbus_verbose ("  path exhausted, returning %s\n",
00205                      subtree->name);
00206 #endif
00207       return subtree;
00208     }
00209 
00210 #if VERBOSE_FIND
00211   _dbus_verbose ("  searching children of %s for %s\n",
00212                  subtree->name, path[0]);
00213 #endif
00214   
00215   ensure_sorted (subtree);
00216 
00217   /* FIXME we should do a binary search here instead
00218    * of O(n)
00219    */
00220 
00221   i = 0;
00222   while (i < subtree->n_subtrees)
00223     {
00224       int v;
00225 
00226       v = strcmp (path[0], subtree->subtrees[i]->name);
00227 
00228 #if VERBOSE_FIND
00229       _dbus_verbose ("  %s cmp %s = %d\n",
00230                      path[0], subtree->subtrees[i]->name,
00231                      v);
00232 #endif
00233       
00234       if (v == 0)
00235         {
00236           if (index_in_parent)
00237             {
00238 #if VERBOSE_FIND
00239               _dbus_verbose ("  storing parent index %d\n", i);
00240 #endif
00241               *index_in_parent = i;
00242             }
00243 
00244           if (return_deepest_match)
00245             {
00246               DBusObjectSubtree *next;
00247 
00248               next = find_subtree_recurse (subtree->subtrees[i],
00249                                            &path[1], return_deepest_match,
00250                                            create_if_not_found, index_in_parent);
00251               if (next == NULL &&
00252                   subtree->invoke_as_fallback)
00253                 {
00254 #if VERBOSE_FIND
00255                   _dbus_verbose ("  no deeper match found, returning %s\n",
00256                                  subtree->name);
00257 #endif
00258                   return subtree;
00259                 }
00260               else
00261                 return next;
00262             }
00263           else
00264             return find_subtree_recurse (subtree->subtrees[i],
00265                                          &path[1], return_deepest_match,
00266                                          create_if_not_found, index_in_parent);
00267         }
00268       else if (v < 0)
00269         {
00270           goto not_found;
00271         }
00272 
00273       ++i;
00274     }
00275 
00276  not_found:
00277 #if VERBOSE_FIND
00278   _dbus_verbose ("  no match found, current tree %s, create_if_not_found = %d\n",
00279                  subtree->name, create_if_not_found);
00280 #endif
00281   
00282   if (create_if_not_found)
00283     {
00284       DBusObjectSubtree* child;
00285       DBusObjectSubtree **new_subtrees;
00286       int new_n_subtrees;
00287 
00288 #if VERBOSE_FIND
00289       _dbus_verbose ("  creating subtree %s\n",
00290                      path[0]);
00291 #endif
00292       
00293       child = _dbus_object_subtree_new (path[0],
00294                                         NULL, NULL);
00295       if (child == NULL)
00296         return NULL;
00297 
00298       /* FIXME we should do the "double alloc each time" standard thing */
00299       new_n_subtrees = subtree->n_subtrees + 1;
00300       new_subtrees = dbus_realloc (subtree->subtrees,
00301                                    new_n_subtrees * sizeof (DBusObjectSubtree*));
00302       if (new_subtrees == NULL)
00303         {
00304           child->unregister_function = NULL;
00305           child->message_function = NULL;
00306           _dbus_object_subtree_unref (child);
00307           return FALSE;
00308         }
00309 
00310       new_subtrees[subtree->n_subtrees] = child;
00311       if (index_in_parent)
00312         *index_in_parent = subtree->n_subtrees;
00313       subtree->subtrees_sorted = FALSE;
00314       subtree->n_subtrees = new_n_subtrees;
00315       subtree->subtrees = new_subtrees;
00316 
00317       child->parent = subtree;
00318 
00319       return find_subtree_recurse (child,
00320                                    &path[1], return_deepest_match,
00321                                    create_if_not_found, index_in_parent);
00322     }
00323   else
00324     return (return_deepest_match && subtree->invoke_as_fallback) ? subtree : NULL;
00325 }
00326 
00327 static DBusObjectSubtree*
00328 find_subtree (DBusObjectTree *tree,
00329               const char    **path,
00330               int            *index_in_parent)
00331 {
00332   DBusObjectSubtree *subtree;
00333 
00334 #if VERBOSE_FIND
00335   _dbus_verbose ("Looking for exact registered subtree\n");
00336 #endif
00337   
00338   subtree = find_subtree_recurse (tree->root, path, FALSE, FALSE, index_in_parent);
00339 
00340   if (subtree && subtree->message_function == NULL)
00341     return NULL;
00342   else
00343     return subtree;
00344 }
00345 
00346 static DBusObjectSubtree*
00347 find_handler (DBusObjectTree *tree,
00348               const char    **path)
00349 {
00350 #if VERBOSE_FIND
00351   _dbus_verbose ("Looking for deepest handler\n");
00352 #endif
00353   return find_subtree_recurse (tree->root, path, TRUE, FALSE, NULL);
00354 }
00355 
00356 static DBusObjectSubtree*
00357 ensure_subtree (DBusObjectTree *tree,
00358                 const char    **path)
00359 {
00360 #if VERBOSE_FIND
00361   _dbus_verbose ("Ensuring subtree\n");
00362 #endif
00363   return find_subtree_recurse (tree->root, path, FALSE, TRUE, NULL);
00364 }
00365 
00376 dbus_bool_t
00377 _dbus_object_tree_register (DBusObjectTree              *tree,
00378                             dbus_bool_t                  fallback,
00379                             const char                 **path,
00380                             const DBusObjectPathVTable  *vtable,
00381                             void                        *user_data)
00382 {
00383   DBusObjectSubtree  *subtree;
00384 
00385   _dbus_assert (tree != NULL);
00386   _dbus_assert (vtable->message_function != NULL);
00387   _dbus_assert (path != NULL);
00388 
00389   subtree = ensure_subtree (tree, path);
00390   if (subtree == NULL)
00391     return FALSE;
00392 
00393 #ifndef DBUS_DISABLE_CHECKS
00394   if (subtree->message_function != NULL)
00395     {
00396       _dbus_warn ("A handler is already registered for the path starting with path[0] = \"%s\"\n",
00397                   path[0] ? path[0] : "null");
00398       return FALSE;
00399     }
00400 #else
00401   _dbus_assert (subtree->message_function == NULL);
00402 #endif
00403 
00404   subtree->message_function = vtable->message_function;
00405   subtree->unregister_function = vtable->unregister_function;
00406   subtree->user_data = user_data;
00407   subtree->invoke_as_fallback = fallback != FALSE;
00408   
00409   return TRUE;
00410 }
00411 
00419 void
00420 _dbus_object_tree_unregister_and_unlock (DBusObjectTree          *tree,
00421                                          const char             **path)
00422 {
00423   int i;
00424   DBusObjectSubtree *subtree;
00425   DBusObjectPathUnregisterFunction unregister_function;
00426   void *user_data;
00427   DBusConnection *connection;
00428 
00429   _dbus_assert (path != NULL);
00430 
00431   subtree = find_subtree (tree, path, &i);
00432 
00433 #ifndef DBUS_DISABLE_CHECKS
00434   if (subtree == NULL)
00435     {
00436       _dbus_warn ("Attempted to unregister path (path[0] = %s path[1] = %s) which isn't registered\n",
00437                   path[0] ? path[0] : "null",
00438                   path[1] ? path[1] : "null");
00439       return;
00440     }
00441 #else
00442   _dbus_assert (subtree != NULL);
00443 #endif
00444 
00445   _dbus_assert (subtree->parent == NULL ||
00446                 (i >= 0 && subtree->parent->subtrees[i] == subtree));
00447 
00448   subtree->message_function = NULL;
00449 
00450   unregister_function = subtree->unregister_function;
00451   user_data = subtree->user_data;
00452 
00453   subtree->unregister_function = NULL;
00454   subtree->user_data = NULL;
00455 
00456   /* If we have no subtrees of our own, remove from
00457    * our parent (FIXME could also be more aggressive
00458    * and remove our parent if it becomes empty)
00459    */
00460   if (subtree->parent && subtree->n_subtrees == 0)
00461     {
00462       /* assumes a 0-byte memmove is OK */
00463       memmove (&subtree->parent->subtrees[i],
00464                &subtree->parent->subtrees[i+1],
00465                (subtree->parent->n_subtrees - i - 1) *
00466                sizeof (subtree->parent->subtrees[0]));
00467       subtree->parent->n_subtrees -= 1;
00468 
00469       subtree->parent = NULL;
00470 
00471       _dbus_object_subtree_unref (subtree);
00472     }
00473   subtree = NULL;
00474 
00475   connection = tree->connection;
00476 
00477   /* Unlock and call application code */
00478 #ifdef DBUS_BUILD_TESTS
00479   if (connection)
00480 #endif
00481     {
00482       _dbus_connection_ref_unlocked (connection);
00483       _dbus_connection_unlock (connection);
00484     }
00485 
00486   if (unregister_function)
00487     (* unregister_function) (connection, user_data);
00488 
00489 #ifdef DBUS_BUILD_TESTS
00490   if (connection)
00491 #endif
00492     dbus_connection_unref (connection);
00493 }
00494 
00495 static void
00496 free_subtree_recurse (DBusConnection    *connection,
00497                       DBusObjectSubtree *subtree)
00498 {
00499   /* Delete them from the end, for slightly
00500    * more robustness against odd reentrancy.
00501    */
00502   while (subtree->n_subtrees > 0)
00503     {
00504       DBusObjectSubtree *child;
00505 
00506       child = subtree->subtrees[subtree->n_subtrees - 1];
00507       subtree->subtrees[subtree->n_subtrees - 1] = NULL;
00508       subtree->n_subtrees -= 1;
00509       child->parent = NULL;
00510 
00511       free_subtree_recurse (connection, child);
00512     }
00513 
00514   /* Call application code */
00515   if (subtree->unregister_function)
00516     {
00517       (* subtree->unregister_function) (connection,
00518                                         subtree->user_data);
00519       subtree->message_function = NULL;
00520       subtree->unregister_function = NULL;
00521       subtree->user_data = NULL;
00522     }
00523 
00524   /* Now free ourselves */
00525   _dbus_object_subtree_unref (subtree);
00526 }
00527 
00534 void
00535 _dbus_object_tree_free_all_unlocked (DBusObjectTree *tree)
00536 {
00537   if (tree->root)
00538     free_subtree_recurse (tree->connection,
00539                           tree->root);
00540   tree->root = NULL;
00541 }
00542 
00543 static dbus_bool_t
00544 _dbus_object_tree_list_registered_unlocked (DBusObjectTree *tree,
00545                                             const char    **parent_path,
00546                                             char         ***child_entries)
00547 {
00548   DBusObjectSubtree *subtree;
00549   char **retval;
00550   
00551   _dbus_assert (parent_path != NULL);
00552   _dbus_assert (child_entries != NULL);
00553 
00554   *child_entries = NULL;
00555   
00556   subtree = find_subtree (tree, parent_path, NULL);
00557   if (subtree == NULL)
00558     {
00559       retval = dbus_new0 (char *, 1);
00560       if (retval == NULL)
00561         goto out;
00562     }
00563   else
00564     {
00565       int i;
00566       retval = dbus_new0 (char*, subtree->n_subtrees + 1);
00567       if (retval == NULL)
00568         goto out;
00569       i = 0;
00570       while (i < subtree->n_subtrees)
00571         {
00572           retval[i] = _dbus_strdup (subtree->subtrees[i]->name);
00573           if (retval[i] == NULL)
00574             {
00575               dbus_free_string_array (retval);
00576               retval = NULL;
00577               goto out;
00578             }
00579           ++i;
00580         }
00581     }
00582 
00583  out:
00584     
00585   *child_entries = retval;
00586   return retval != NULL;
00587 }
00588 
00589 static DBusHandlerResult
00590 handle_default_introspect_unlocked (DBusObjectTree          *tree,
00591                                     DBusMessage             *message,
00592                                     const char             **path)
00593 {
00594   DBusString xml;
00595   DBusHandlerResult result;
00596   char **children;
00597   int i;
00598 
00599   if (!dbus_message_is_method_call (message,
00600                                     DBUS_INTERFACE_ORG_FREEDESKTOP_INTROSPECTABLE,
00601                                     "Introspect"))
00602     return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
00603   
00604   if (!_dbus_string_init (&xml))
00605     return DBUS_HANDLER_RESULT_NEED_MEMORY;
00606 
00607   result = DBUS_HANDLER_RESULT_NEED_MEMORY;
00608 
00609   children = NULL;
00610   if (!_dbus_object_tree_list_registered_unlocked (tree, path, &children))
00611     goto out;
00612 
00613   if (!_dbus_string_append (&xml, "<node>\n"))
00614     goto out;
00615 
00616   i = 0;
00617   while (children[i] != NULL)
00618     {
00619       if (!_dbus_string_append_printf (&xml, "  <node name=\"%s\"/>\n",
00620                                        children[i]))
00621         goto out;
00622 
00623       ++i;
00624     }
00625 
00626   if (!_dbus_string_append (&xml, "</node>\n"))
00627     goto out;
00628   
00629   result = DBUS_HANDLER_RESULT_HANDLED;
00630   
00631  out:
00632   _dbus_string_free (&xml);
00633   dbus_free_string_array (children);
00634   
00635   return result;
00636 }
00637 
00651 DBusHandlerResult
00652 _dbus_object_tree_dispatch_and_unlock (DBusObjectTree          *tree,
00653                                        DBusMessage             *message)
00654 {
00655   char **path;
00656   DBusList *list;
00657   DBusList *link;
00658   DBusHandlerResult result;
00659   DBusObjectSubtree *subtree;
00660   
00661 #if 0
00662   _dbus_verbose ("Dispatch of message by object path\n");
00663 #endif
00664   
00665   path = NULL;
00666   if (!dbus_message_get_path_decomposed (message, &path))
00667     {
00668 #ifdef DBUS_BUILD_TESTS
00669       if (tree->connection)
00670 #endif
00671         _dbus_connection_unlock (tree->connection);
00672       
00673       _dbus_verbose ("No memory to get decomposed path\n");
00674 
00675       return DBUS_HANDLER_RESULT_NEED_MEMORY;
00676     }
00677 
00678   if (path == NULL)
00679     {
00680 #ifdef DBUS_BUILD_TESTS
00681       if (tree->connection)
00682 #endif
00683         _dbus_connection_unlock (tree->connection);
00684       
00685       _dbus_verbose ("No path field in message\n");
00686       return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
00687     }
00688   
00689   /* Find the deepest path that covers the path in the message */
00690   subtree = find_handler (tree, (const char**) path);
00691   
00692   /* Build a list of all paths that cover the path in the message */
00693 
00694   list = NULL;
00695 
00696   while (subtree != NULL)
00697     {
00698       if (subtree->message_function != NULL)
00699         {
00700           _dbus_object_subtree_ref (subtree);
00701 
00702           /* run deepest paths first */
00703           if (!_dbus_list_append (&list, subtree))
00704             {
00705               result = DBUS_HANDLER_RESULT_NEED_MEMORY;
00706               _dbus_object_subtree_unref (subtree);
00707               goto free_and_return;
00708             }
00709         }
00710 
00711       subtree = subtree->parent;
00712     }
00713 
00714   _dbus_verbose ("%d handlers in the path tree for this message\n",
00715                  _dbus_list_get_length (&list));
00716 
00717   /* Invoke each handler in the list */
00718 
00719   result = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
00720 
00721   link = _dbus_list_get_first_link (&list);
00722   while (link != NULL)
00723     {
00724       DBusList *next = _dbus_list_get_next_link (&list, link);
00725       subtree = link->data;
00726 
00727       /* message_function is NULL if we're unregistered
00728        * due to reentrancy
00729        */
00730       if (subtree->message_function)
00731         {
00732           DBusObjectPathMessageFunction message_function;
00733           void *user_data;
00734 
00735           message_function = subtree->message_function;
00736           user_data = subtree->user_data;
00737 
00738 #if 0
00739           _dbus_verbose ("  (invoking a handler)\n");
00740 #endif
00741           
00742 #ifdef DBUS_BUILD_TESTS
00743           if (tree->connection)
00744 #endif
00745             _dbus_connection_unlock (tree->connection);
00746 
00747           /* FIXME you could unregister the subtree in another thread
00748            * before we invoke the callback, and I can't figure out a
00749            * good way to solve this.
00750            */
00751 
00752           result = (* message_function) (tree->connection,
00753                                          message,
00754                                          user_data);
00755 
00756 #ifdef DBUS_BUILD_TESTS
00757           if (tree->connection)
00758 #endif
00759             _dbus_connection_lock (tree->connection);
00760 
00761           if (result != DBUS_HANDLER_RESULT_NOT_YET_HANDLED)
00762             goto free_and_return;
00763         }
00764 
00765       link = next;
00766     }
00767 
00768  free_and_return:
00769 
00770   if (result == DBUS_HANDLER_RESULT_NOT_YET_HANDLED)
00771     {
00772       /* This hardcoded default handler does a minimal Introspect()
00773        */
00774       result = handle_default_introspect_unlocked (tree, message,
00775                                                    (const char**) path);
00776     }
00777 
00778 #ifdef DBUS_BUILD_TESTS
00779   if (tree->connection)
00780 #endif
00781     _dbus_connection_unlock (tree->connection);
00782   
00783   while (list != NULL)
00784     {
00785       link = _dbus_list_get_first_link (&list);
00786       _dbus_object_subtree_unref (link->data);
00787       _dbus_list_remove_link (&list, link);
00788     }
00789   
00790   dbus_free_string_array (path);
00791 
00792   return result;
00793 }
00794 
00801 static DBusObjectSubtree*
00802 allocate_subtree_object (const char *name)
00803 {
00804   int len;
00805   DBusObjectSubtree *subtree;
00806   const size_t front_padding = _DBUS_STRUCT_OFFSET (DBusObjectSubtree, name);
00807 
00808   _dbus_assert (name != NULL);
00809 
00810   len = strlen (name);
00811 
00812   subtree = dbus_malloc (front_padding + (len + 1));
00813 
00814   if (subtree == NULL)
00815     return NULL;
00816 
00817   memcpy (subtree->name, name, len + 1);
00818 
00819   return subtree;
00820 }
00821 
00822 static DBusObjectSubtree*
00823 _dbus_object_subtree_new (const char                  *name,
00824                           const DBusObjectPathVTable  *vtable,
00825                           void                        *user_data)
00826 {
00827   DBusObjectSubtree *subtree;
00828 
00829   subtree = allocate_subtree_object (name);
00830   if (subtree == NULL)
00831     goto oom;
00832 
00833   _dbus_assert (name != NULL);
00834 
00835   subtree->parent = NULL;
00836 
00837   if (vtable)
00838     {
00839       subtree->message_function = vtable->message_function;
00840       subtree->unregister_function = vtable->unregister_function;
00841     }
00842   else
00843     {
00844       subtree->message_function = NULL;
00845       subtree->unregister_function = NULL;
00846     }
00847 
00848   subtree->user_data = user_data;
00849   subtree->refcount.value = 1;
00850   subtree->subtrees = NULL;
00851   subtree->n_subtrees = 0;
00852   subtree->subtrees_sorted = TRUE;
00853   
00854   return subtree;
00855 
00856  oom:
00857   if (subtree)
00858     {
00859       dbus_free (subtree);
00860     }
00861 
00862   return NULL;
00863 }
00864 
00865 static DBusObjectSubtree *
00866 _dbus_object_subtree_ref (DBusObjectSubtree *subtree)
00867 {
00868   _dbus_assert (subtree->refcount.value > 0);
00869   _dbus_atomic_inc (&subtree->refcount);
00870 
00871   return subtree;
00872 }
00873 
00874 static void
00875 _dbus_object_subtree_unref (DBusObjectSubtree *subtree)
00876 {
00877   _dbus_assert (subtree->refcount.value > 0);
00878 
00879   if (_dbus_atomic_dec (&subtree->refcount) == 1)
00880     {
00881       _dbus_assert (subtree->unregister_function == NULL);
00882       _dbus_assert (subtree->message_function == NULL);
00883 
00884       dbus_free (subtree->subtrees);
00885       dbus_free (subtree);
00886     }
00887 }
00888 
00899 dbus_bool_t
00900 _dbus_object_tree_list_registered_and_unlock (DBusObjectTree *tree,
00901                                               const char    **parent_path,
00902                                               char         ***child_entries)
00903 {
00904   dbus_bool_t result;
00905 
00906   result = _dbus_object_tree_list_registered_unlocked (tree,
00907                                                        parent_path,
00908                                                        child_entries);
00909   
00910 #ifdef DBUS_BUILD_TESTS
00911   if (tree->connection)
00912 #endif
00913     _dbus_connection_unlock (tree->connection);
00914 
00915   return result;
00916 }
00917      
00920 #ifdef DBUS_BUILD_TESTS
00921 #include "dbus-test.h"
00922 #include <stdio.h>
00923 
00924 static char*
00925 flatten_path (const char **path)
00926 {
00927   DBusString str;
00928   int i;
00929   char *s;
00930 
00931   if (!_dbus_string_init (&str))
00932     return NULL;
00933 
00934   i = 0;
00935   while (path[i])
00936     {
00937       if (!_dbus_string_append_byte (&str, '/'))
00938         goto nomem;
00939 
00940       if (!_dbus_string_append (&str, path[i]))
00941         goto nomem;
00942 
00943       ++i;
00944     }
00945 
00946   if (!_dbus_string_steal_data (&str, &s))
00947     goto nomem;
00948 
00949   _dbus_string_free (&str);
00950 
00951   return s;
00952 
00953  nomem:
00954   _dbus_string_free (&str);
00955   return NULL;
00956 }
00957 
00958 /* Returns TRUE if container is a parent of child
00959  */
00960 static dbus_bool_t
00961 path_contains (const char **container,
00962                const char **child)
00963 {
00964   int i;
00965 
00966   i = 0;
00967   while (child[i] != NULL)
00968     {
00969       int v;
00970 
00971       if (container[i] == NULL)
00972         return TRUE; /* container ran out, child continues;
00973                       * thus the container is a parent of the
00974                       * child.
00975                       */
00976 
00977       _dbus_assert (container[i] != NULL);
00978       _dbus_assert (child[i] != NULL);
00979 
00980       v = strcmp (container[i], child[i]);
00981 
00982       if (v != 0)
00983         return FALSE; /* they overlap until here and then are different,
00984                        * not overlapping
00985                        */
00986 
00987       ++i;
00988     }
00989 
00990   /* Child ran out; if container also did, they are equal;
00991    * otherwise, the child is a parent of the container.
00992    */
00993   if (container[i] == NULL)
00994     return TRUE; /* equal is counted as containing */
00995   else
00996     return FALSE;
00997 }
00998 
00999 static void
01000 spew_subtree_recurse (DBusObjectSubtree *subtree,
01001                       int                indent)
01002 {
01003   int i;
01004 
01005   i = 0;
01006   while (i < indent)
01007     {
01008       _dbus_verbose (" ");
01009       ++i;
01010     }
01011 
01012   _dbus_verbose ("%s (%d children)\n",
01013                  subtree->name, subtree->n_subtrees);
01014 
01015   i = 0;
01016   while (i < subtree->n_subtrees)
01017     {
01018       spew_subtree_recurse (subtree->subtrees[i], indent + 2);
01019 
01020       ++i;
01021     }
01022 }
01023 
01024 static void
01025 spew_tree (DBusObjectTree *tree)
01026 {
01027   spew_subtree_recurse (tree->root, 0);
01028 }
01029 
01033 typedef struct
01034 {
01035   const char **path; 
01036   dbus_bool_t message_handled; 
01037   dbus_bool_t handler_unregistered; 
01039 } TreeTestData;
01040 
01041 
01042 static void
01043 test_unregister_function (DBusConnection  *connection,
01044                           void            *user_data)
01045 {
01046   TreeTestData *ttd = user_data;
01047 
01048   ttd->handler_unregistered = TRUE;
01049 }
01050 
01051 static DBusHandlerResult
01052 test_message_function (DBusConnection  *connection,
01053                        DBusMessage     *message,
01054                        void            *user_data)
01055 {
01056   TreeTestData *ttd = user_data;
01057 
01058   ttd->message_handled = TRUE;
01059 
01060   return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
01061 }
01062 
01063 static dbus_bool_t
01064 do_register (DBusObjectTree *tree,
01065              const char    **path,
01066              int             i,
01067              TreeTestData   *tree_test_data)
01068 {
01069   DBusObjectPathVTable vtable = { test_unregister_function,
01070                                   test_message_function, NULL };
01071 
01072   tree_test_data[i].message_handled = FALSE;
01073   tree_test_data[i].handler_unregistered = FALSE;
01074   tree_test_data[i].path = path;
01075 
01076   if (!_dbus_object_tree_register (tree, TRUE, path,
01077                                    &vtable,
01078                                    &tree_test_data[i]))
01079     return FALSE;
01080 
01081   return TRUE;
01082 }
01083 
01084 static dbus_bool_t
01085 do_test_dispatch (DBusObjectTree *tree,
01086                   const char    **path,
01087                   int             i,
01088                   TreeTestData   *tree_test_data,
01089                   int             n_test_data)
01090 {
01091   DBusMessage *message;
01092   int j;
01093   DBusHandlerResult result;
01094   char *flat;
01095 
01096   message = NULL;
01097   
01098   flat = flatten_path (path);
01099   if (flat == NULL)
01100     goto oom;
01101 
01102   message = dbus_message_new_method_call (NULL,
01103                                           flat,
01104                                           "org.freedesktop.TestInterface",
01105                                           "Foo");
01106   dbus_free (flat);
01107   if (message == NULL)
01108     goto oom;
01109 
01110   j = 0;
01111   while (j < n_test_data)
01112     {
01113       tree_test_data[j].message_handled = FALSE;
01114       ++j;
01115     }
01116 
01117   result = _dbus_object_tree_dispatch_and_unlock (tree, message);
01118   if (result == DBUS_HANDLER_RESULT_NEED_MEMORY)
01119     goto oom;
01120 
01121   _dbus_assert (tree_test_data[i].message_handled);
01122 
01123   j = 0;
01124   while (j < n_test_data)
01125     {
01126       if (tree_test_data[j].message_handled)
01127         _dbus_assert (path_contains (tree_test_data[j].path,
01128                                      path));
01129       else
01130         _dbus_assert (!path_contains (tree_test_data[j].path,
01131                                       path));
01132 
01133       ++j;
01134     }
01135 
01136   dbus_message_unref (message);
01137 
01138   return TRUE;
01139 
01140  oom:
01141   if (message)
01142     dbus_message_unref (message);
01143   return FALSE;
01144 }
01145 
01146 static dbus_bool_t
01147 object_tree_test_iteration (void *data)
01148 {
01149   const char *path1[] = { "foo", NULL };
01150   const char *path2[] = { "foo", "bar", NULL };
01151   const char *path3[] = { "foo", "bar", "baz", NULL };
01152   const char *path4[] = { "foo", "bar", "boo", NULL };
01153   const char *path5[] = { "blah", NULL };
01154   const char *path6[] = { "blah", "boof", NULL };
01155   const char *path7[] = { "blah", "boof", "this", "is", "really", "long", NULL };
01156   const char *path8[] = { "childless", NULL };
01157   DBusObjectTree *tree;
01158   TreeTestData tree_test_data[8];
01159   int i;
01160 
01161   tree = NULL;
01162 
01163   tree = _dbus_object_tree_new (NULL);
01164   if (tree == NULL)
01165     goto out;
01166 
01167   if (!do_register (tree, path1, 0, tree_test_data))
01168     goto out;
01169 
01170   _dbus_assert (find_subtree (tree, path1, NULL));
01171   _dbus_assert (!find_subtree (tree, path2, NULL));
01172   _dbus_assert (!find_subtree (tree, path3, NULL));
01173   _dbus_assert (!find_subtree (tree, path4, NULL));
01174   _dbus_assert (!find_subtree (tree, path5, NULL));
01175   _dbus_assert (!find_subtree (tree, path6, NULL));
01176   _dbus_assert (!find_subtree (tree, path7, NULL));
01177   _dbus_assert (!find_subtree (tree, path8, NULL));
01178 
01179   _dbus_assert (find_handler (tree, path1));
01180   _dbus_assert (find_handler (tree, path2));
01181   _dbus_assert (find_handler (tree, path3));
01182   _dbus_assert (find_handler (tree, path4));
01183   _dbus_assert (find_handler (tree, path5) == tree->root);
01184   _dbus_assert (find_handler (tree, path6) == tree->root);
01185   _dbus_assert (find_handler (tree, path7) == tree->root);
01186   _dbus_assert (find_handler (tree, path8) == tree->root);
01187 
01188   if (!do_register (tree, path2, 1, tree_test_data))
01189     goto out;
01190 
01191   _dbus_assert (find_subtree (tree, path1, NULL));
01192   _dbus_assert (find_subtree (tree, path2, NULL));
01193   _dbus_assert (!find_subtree (tree, path3, NULL));
01194   _dbus_assert (!find_subtree (tree, path4, NULL));
01195   _dbus_assert (!find_subtree (tree, path5, NULL));
01196   _dbus_assert (!find_subtree (tree, path6, NULL));
01197   _dbus_assert (!find_subtree (tree, path7, NULL));
01198   _dbus_assert (!find_subtree (tree, path8, NULL));
01199 
01200   if (!do_register (tree, path3, 2, tree_test_data))
01201     goto out;
01202 
01203   _dbus_assert (find_subtree (tree, path1, NULL));
01204   _dbus_assert (find_subtree (tree, path2, NULL));
01205   _dbus_assert (find_subtree (tree, path3, NULL));
01206   _dbus_assert (!find_subtree (tree, path4, NULL));
01207   _dbus_assert (!find_subtree (tree, path5, NULL));
01208   _dbus_assert (!find_subtree (tree, path6, NULL));
01209   _dbus_assert (!find_subtree (tree, path7, NULL));
01210   _dbus_assert (!find_subtree (tree, path8, NULL));
01211   
01212   if (!do_register (tree, path4, 3, tree_test_data))
01213     goto out;
01214 
01215   _dbus_assert (find_subtree (tree, path1, NULL));
01216   _dbus_assert (find_subtree (tree, path2, NULL));
01217   _dbus_assert (find_subtree (tree, path3, NULL));  
01218   _dbus_assert (find_subtree (tree, path4, NULL));
01219   _dbus_assert (!find_subtree (tree, path5, NULL));
01220   _dbus_assert (!find_subtree (tree, path6, NULL));
01221   _dbus_assert (!find_subtree (tree, path7, NULL));
01222   _dbus_assert (!find_subtree (tree, path8, NULL));
01223   
01224   if (!do_register (tree, path5, 4, tree_test_data))
01225     goto out;
01226 
01227   _dbus_assert (find_subtree (tree, path1, NULL));
01228   _dbus_assert (find_subtree (tree, path2, NULL));
01229   _dbus_assert (find_subtree (tree, path3, NULL));
01230   _dbus_assert (find_subtree (tree, path4, NULL));
01231   _dbus_assert (find_subtree (tree, path5, NULL));
01232   _dbus_assert (!find_subtree (tree, path6, NULL));
01233   _dbus_assert (!find_subtree (tree, path7, NULL));
01234   _dbus_assert (!find_subtree (tree, path8, NULL));
01235   
01236   _dbus_assert (find_handler (tree, path1) != tree->root);
01237   _dbus_assert (find_handler (tree, path2) != tree->root);
01238   _dbus_assert (find_handler (tree, path3) != tree->root);
01239   _dbus_assert (find_handler (tree, path4) != tree->root);
01240   _dbus_assert (find_handler (tree, path5) != tree->root);
01241   _dbus_assert (find_handler (tree, path6) != tree->root);
01242   _dbus_assert (find_handler (tree, path7) != tree->root);
01243   _dbus_assert (find_handler (tree, path8) == tree->root);
01244 
01245   if (!do_register (tree, path6, 5, tree_test_data))
01246     goto out;
01247 
01248   _dbus_assert (find_subtree (tree, path1, NULL));
01249   _dbus_assert (find_subtree (tree, path2, NULL));
01250   _dbus_assert (find_subtree (tree, path3, NULL));
01251   _dbus_assert (find_subtree (tree, path4, NULL));
01252   _dbus_assert (find_subtree (tree, path5, NULL));
01253   _dbus_assert (find_subtree (tree, path6, NULL));
01254   _dbus_assert (!find_subtree (tree, path7, NULL));
01255   _dbus_assert (!find_subtree (tree, path8, NULL));
01256 
01257   if (!do_register (tree, path7, 6, tree_test_data))
01258     goto out;
01259 
01260   _dbus_assert (find_subtree (tree, path1, NULL));
01261   _dbus_assert (find_subtree (tree, path2, NULL));
01262   _dbus_assert (find_subtree (tree, path3, NULL));
01263   _dbus_assert (find_subtree (tree, path4, NULL));
01264   _dbus_assert (find_subtree (tree, path5, NULL));
01265   _dbus_assert (find_subtree (tree, path6, NULL));
01266   _dbus_assert (find_subtree (tree, path7, NULL));
01267   _dbus_assert (!find_subtree (tree, path8, NULL));
01268 
01269   if (!do_register (tree, path8, 7, tree_test_data))
01270     goto out;
01271 
01272   _dbus_assert (find_subtree (tree, path1, NULL));
01273   _dbus_assert (find_subtree (tree, path2, NULL));
01274   _dbus_assert (find_subtree (tree, path3, NULL));
01275   _dbus_assert (find_subtree (tree, path4, NULL));
01276   _dbus_assert (find_subtree (tree, path5, NULL));
01277   _dbus_assert (find_subtree (tree, path6, NULL));
01278   _dbus_assert (find_subtree (tree, path7, NULL));
01279   _dbus_assert (find_subtree (tree, path8, NULL));
01280   
01281   _dbus_assert (find_handler (tree, path1) != tree->root);
01282   _dbus_assert (find_handler (tree, path2) != tree->root);
01283   _dbus_assert (find_handler (tree, path3) != tree->root);
01284   _dbus_assert (find_handler (tree, path4) != tree->root);
01285   _dbus_assert (find_handler (tree, path5) != tree->root);
01286   _dbus_assert (find_handler (tree, path6) != tree->root);
01287   _dbus_assert (find_handler (tree, path7) != tree->root);
01288   _dbus_assert (find_handler (tree, path8) != tree->root);
01289   
01290   /* Check that destroying tree calls unregister funcs */
01291   _dbus_object_tree_unref (tree);
01292 
01293   i = 0;
01294   while (i < (int) _DBUS_N_ELEMENTS (tree_test_data))
01295     {
01296       _dbus_assert (tree_test_data[i].handler_unregistered);
01297       _dbus_assert (!tree_test_data[i].message_handled);
01298       ++i;
01299     }
01300 
01301   /* Now start again and try the individual unregister function */
01302   tree = _dbus_object_tree_new (NULL);
01303   if (tree == NULL)
01304     goto out;
01305 
01306   if (!do_register (tree, path1, 0, tree_test_data))
01307     goto out;
01308   if (!do_register (tree, path2, 1, tree_test_data))
01309     goto out;
01310   if (!do_register (tree, path3, 2, tree_test_data))
01311     goto out;
01312   if (!do_register (tree, path4, 3, tree_test_data))
01313     goto out;
01314   if (!do_register (tree, path5, 4, tree_test_data))
01315     goto out;
01316   if (!do_register (tree, path6, 5, tree_test_data))
01317     goto out;
01318   if (!do_register (tree, path7, 6, tree_test_data))
01319     goto out;
01320   if (!do_register (tree, path8, 7, tree_test_data))
01321     goto out;
01322   
01323   _dbus_object_tree_unregister_and_unlock (tree, path1);
01324 
01325   _dbus_assert (!find_subtree (tree, path1, NULL));
01326   _dbus_assert (find_subtree (tree, path2, NULL));
01327   _dbus_assert (find_subtree (tree, path3, NULL));
01328   _dbus_assert (find_subtree (tree, path4, NULL));
01329   _dbus_assert (find_subtree (tree, path5, NULL));
01330   _dbus_assert (find_subtree (tree, path6, NULL));
01331   _dbus_assert (find_subtree (tree, path7, NULL));
01332   _dbus_assert (find_subtree (tree, path8, NULL));
01333 
01334   _dbus_object_tree_unregister_and_unlock (tree, path2);
01335 
01336   _dbus_assert (!find_subtree (tree, path1, NULL));
01337   _dbus_assert (!find_subtree (tree, path2, NULL));
01338   _dbus_assert (find_subtree (tree, path3, NULL));
01339   _dbus_assert (find_subtree (tree, path4, NULL));
01340   _dbus_assert (find_subtree (tree, path5, NULL));
01341   _dbus_assert (find_subtree (tree, path6, NULL));
01342   _dbus_assert (find_subtree (tree, path7, NULL));
01343   _dbus_assert (find_subtree (tree, path8, NULL));
01344   
01345   _dbus_object_tree_unregister_and_unlock (tree, path3);
01346 
01347   _dbus_assert (!find_subtree (tree, path1, NULL));
01348   _dbus_assert (!find_subtree (tree, path2, NULL));
01349   _dbus_assert (!find_subtree (tree, path3, NULL));
01350   _dbus_assert (find_subtree (tree, path4, NULL));
01351   _dbus_assert (find_subtree (tree, path5, NULL));
01352   _dbus_assert (find_subtree (tree, path6, NULL));
01353   _dbus_assert (find_subtree (tree, path7, NULL));
01354   _dbus_assert (find_subtree (tree, path8, NULL));
01355   
01356   _dbus_object_tree_unregister_and_unlock (tree, path4);
01357 
01358   _dbus_assert (!find_subtree (tree, path1, NULL));
01359   _dbus_assert (!find_subtree (tree, path2, NULL));
01360   _dbus_assert (!find_subtree (tree, path3, NULL));
01361   _dbus_assert (!find_subtree (tree, path4, NULL));
01362   _dbus_assert (find_subtree (tree, path5, NULL));
01363   _dbus_assert (find_subtree (tree, path6, NULL));
01364   _dbus_assert (find_subtree (tree, path7, NULL));
01365   _dbus_assert (find_subtree (tree, path8, NULL));
01366   
01367   _dbus_object_tree_unregister_and_unlock (tree, path5);
01368 
01369   _dbus_assert (!find_subtree (tree, path1, NULL));
01370   _dbus_assert (!find_subtree (tree, path2, NULL));
01371   _dbus_assert (!find_subtree (tree, path3, NULL));
01372   _dbus_assert (!find_subtree (tree, path4, NULL));
01373   _dbus_assert (!find_subtree (tree, path5, NULL));
01374   _dbus_assert (find_subtree (tree, path6, NULL));
01375   _dbus_assert (find_subtree (tree, path7, NULL));
01376   _dbus_assert (find_subtree (tree, path8, NULL));
01377   
01378   _dbus_object_tree_unregister_and_unlock (tree, path6);
01379 
01380   _dbus_assert (!find_subtree (tree, path1, NULL));
01381   _dbus_assert (!find_subtree (tree, path2, NULL));
01382   _dbus_assert (!find_subtree (tree, path3, NULL));
01383   _dbus_assert (!find_subtree (tree, path4, NULL));
01384   _dbus_assert (!find_subtree (tree, path5, NULL));
01385   _dbus_assert (!find_subtree (tree, path6, NULL));
01386   _dbus_assert (find_subtree (tree, path7, NULL));
01387   _dbus_assert (find_subtree (tree, path8, NULL));
01388 
01389   _dbus_object_tree_unregister_and_unlock (tree, path7);
01390 
01391   _dbus_assert (!find_subtree (tree, path1, NULL));
01392   _dbus_assert (!find_subtree (tree, path2, NULL));
01393   _dbus_assert (!find_subtree (tree, path3, NULL));
01394   _dbus_assert (!find_subtree (tree, path4, NULL));
01395   _dbus_assert (!find_subtree (tree, path5, NULL));
01396   _dbus_assert (!find_subtree (tree, path6, NULL));
01397   _dbus_assert (!find_subtree (tree, path7, NULL));
01398   _dbus_assert (find_subtree (tree, path8, NULL));
01399 
01400   _dbus_object_tree_unregister_and_unlock (tree, path8);
01401 
01402   _dbus_assert (!find_subtree (tree, path1, NULL));
01403   _dbus_assert (!find_subtree (tree, path2, NULL));
01404   _dbus_assert (!find_subtree (tree, path3, NULL));
01405   _dbus_assert (!find_subtree (tree, path4, NULL));
01406   _dbus_assert (!find_subtree (tree, path5, NULL));
01407   _dbus_assert (!find_subtree (tree, path6, NULL));
01408   _dbus_assert (!find_subtree (tree, path7, NULL));
01409   _dbus_assert (!find_subtree (tree, path8, NULL));
01410   
01411   i = 0;
01412   while (i < (int) _DBUS_N_ELEMENTS (tree_test_data))
01413     {
01414       _dbus_assert (tree_test_data[i].handler_unregistered);
01415       _dbus_assert (!tree_test_data[i].message_handled);
01416       ++i;
01417     }
01418 
01419   /* Register it all again, and test dispatch */
01420 
01421   if (!do_register (tree, path1, 0, tree_test_data))
01422     goto out;
01423   if (!do_register (tree, path2, 1, tree_test_data))
01424     goto out;
01425   if (!do_register (tree, path3, 2, tree_test_data))
01426     goto out;
01427   if (!do_register (tree, path4, 3, tree_test_data))
01428     goto out;
01429   if (!do_register (tree, path5, 4, tree_test_data))
01430     goto out;
01431   if (!do_register (tree, path6, 5, tree_test_data))
01432     goto out;
01433   if (!do_register (tree, path7, 6, tree_test_data))
01434     goto out;
01435   if (!do_register (tree, path8, 7, tree_test_data))
01436     goto out;
01437 
01438 #if 0
01439   spew_tree (tree);
01440 #endif
01441   
01442   if (!do_test_dispatch (tree, path1, 0, tree_test_data, _DBUS_N_ELEMENTS (tree_test_data)))
01443     goto out;
01444   if (!do_test_dispatch (tree, path2, 1, tree_test_data, _DBUS_N_ELEMENTS (tree_test_data)))
01445     goto out;
01446   if (!do_test_dispatch (tree, path3, 2, tree_test_data, _DBUS_N_ELEMENTS (tree_test_data)))
01447     goto out;
01448   if (!do_test_dispatch (tree, path4, 3, tree_test_data, _DBUS_N_ELEMENTS (tree_test_data)))
01449     goto out;
01450   if (!do_test_dispatch (tree, path5, 4, tree_test_data, _DBUS_N_ELEMENTS (tree_test_data)))
01451     goto out;
01452   if (!do_test_dispatch (tree, path6, 5, tree_test_data, _DBUS_N_ELEMENTS (tree_test_data)))
01453     goto out;
01454   if (!do_test_dispatch (tree, path7, 6, tree_test_data, _DBUS_N_ELEMENTS (tree_test_data)))
01455     goto out;
01456   if (!do_test_dispatch (tree, path8, 7, tree_test_data, _DBUS_N_ELEMENTS (tree_test_data)))
01457     goto out;
01458   
01459  out:
01460   if (tree)
01461     {
01462       /* test ref */
01463       _dbus_object_tree_ref (tree);
01464       _dbus_object_tree_unref (tree);
01465       _dbus_object_tree_unref (tree);
01466     }
01467 
01468   return TRUE;
01469 }
01470 
01476 dbus_bool_t
01477 _dbus_object_tree_test (void)
01478 {
01479   _dbus_test_oom_handling ("object tree",
01480                            object_tree_test_iteration,
01481                            NULL);
01482 
01483   return TRUE;
01484 }
01485 
01486 #endif /* DBUS_BUILD_TESTS */

Generated on Sun Mar 21 03:52:05 2004 for D-BUS by doxygen 1.3.6-20040222