2003-08-25 Havoc Pennington <hp@pobox.com>

Just noticed that dbus_message_test is hosed, I wonder when I
	broke that. I thought make check was passing earlier...

	* dbus/dbus-object-tree.c: add new "object tree" to match DCOP
	container tree, will replace most of dbus-object-registry

	* dbus/dbus-string.c (_dbus_string_append_printf_valist): fix C99
	screwup
This commit is contained in:
Havoc Pennington 2003-08-25 14:56:53 +00:00
parent d0c588575e
commit 24f411a6a1
9 changed files with 727 additions and 2 deletions

View file

@ -1,3 +1,14 @@
2003-08-25 Havoc Pennington <hp@pobox.com>
Just noticed that dbus_message_test is hosed, I wonder when I
broke that. I thought make check was passing earlier...
* dbus/dbus-object-tree.c: add new "object tree" to match DCOP
container tree, will replace most of dbus-object-registry
* dbus/dbus-string.c (_dbus_string_append_printf_valist): fix C99
screwup
2003-08-19 Havoc Pennington <hp@pobox.com>
* dbus/dbus-message.c (decode_string_field): support FIELD_SENDER

View file

@ -49,6 +49,8 @@ DBUS_LIB_SOURCES= \
dbus-objectid.c \
dbus-object-registry.c \
dbus-object-registry.h \
dbus-object-tree.c \
dbus-object-tree.h \
dbus-pending-call.c \
dbus-resources.c \
dbus-resources.h \

View file

@ -198,6 +198,54 @@ void dbus_connection_send_preallocated (DBusConnection
dbus_uint32_t *client_serial);
/* Object tree functionality */
typedef struct DBusObjectTreeVTable DBusObjectTreeVTable;
typedef void (* DBusObjectTreeUnregisterFunction) (DBusConnection *connection,
const char **path,
void *user_data);
typedef DBusHandlerResult (* DBusObjectTreeMessageFunction) (DBusConnection *connection,
DBusMessage *message,
void *user_data);
typedef dbus_bool_t (* DBusObjectTreeSubdirsFunction) (DBusConnection *connection,
const char **path,
char ***subdirs,
int *n_subdirs,
void *user_data);
typedef dbus_bool_t (* DBusObjectTreeObjectsFunction) (DBusConnection *connection,
const char **path,
DBusObjectID **object_ids,
int *n_object_ids,
void *user_data);
typedef dbus_bool_t (* DBusObjectTreeMethodsFunction) (DBusConnection *connection,
const char **path,
DBusObjectID **object_ids,
int *n_object_ids,
void *user_data);
struct DBusObjectTreeVTable
{
DBusObjectTreeUnregisterFunction unregister_function;
DBusObjectTreeMessageFunction message_function;
DBusObjectTreeSubdirsFunction subdirs_function;
DBusObjectTreeObjectsFunction objects_function;
DBusObjectTreeMethodsFunction methods_function;
void (* dbus_internal_pad1) (void *);
void (* dbus_internal_pad2) (void *);
void (* dbus_internal_pad3) (void *);
void (* dbus_internal_pad4) (void *);
};
dbus_bool_t dbus_connection_register_object_tree (DBusConnection *connection,
const char **path,
const DBusObjectTreeVTable *vtable,
void *user_data);
void dbus_connection_unregister_object_tree (DBusConnection *connection,
const char **path);
DBUS_END_DECLS;
#endif /* DBUS_CONNECTION_H */

View file

@ -411,7 +411,7 @@ _dbus_message_data_load (DBusString *dest,
DBusString name;
int message_type;
if (_dbus_string_get_length (&line) < strlen ("VALID_HEADER "))
if (_dbus_string_get_length (&line) < (int) strlen ("VALID_HEADER "))
{
_dbus_warn ("no args to VALID_HEADER\n");
goto parse_failed;

609
dbus/dbus-object-tree.c Normal file
View file

@ -0,0 +1,609 @@
/* -*- mode: C; c-file-style: "gnu" -*- */
/* dbus-object-tree.c DBusObjectTree (internals of DBusConnection)
*
* Copyright (C) 2003 Red Hat Inc.
*
* Licensed under the Academic Free License version 1.2
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include "dbus-object-tree.h"
#include "dbus-connection-internal.h"
#include "dbus-internals.h"
#include "dbus-hash.h"
#include "dbus-protocol.h"
#include <string.h>
#include <stdlib.h>
/**
* @defgroup DBusObjectTree A hierarchy of objects with container-contained relationship
* @ingroup DBusInternals
* @brief DBusObjectTree is used by DBusConnection to track the object tree
*
* Types and functions related to DBusObjectTree. These
* are all internal.
*
* @{
*/
typedef struct DBusObjectSubtree DBusObjectSubtree;
DBusObjectSubtree* _dbus_object_subtree_new (const char **path,
const DBusObjectTreeVTable *vtable,
void *user_data);
void _dbus_object_subtree_ref (DBusObjectSubtree *subtree);
void _dbus_object_subtree_unref (DBusObjectSubtree *subtree);
struct DBusObjectTree
{
int refcount;
DBusConnection *connection;
/* Each subtree is a separate malloc block since that
* lets us refcount them and maybe helps with
* reentrancy issues when calling back to application code
*/
DBusObjectSubtree **subtrees;
int n_subtrees;
unsigned int subtrees_sorted : 1;
};
struct DBusObjectSubtree
{
int refcount;
char **path;
int n_path_elements;
DBusObjectTreeVTable vtable;
void *user_data;
};
DBusObjectTree*
_dbus_object_tree_new (DBusConnection *connection)
{
DBusObjectTree *tree;
/* the connection passed in here isn't fully constructed,
* so don't do anything more than store a pointer to
* it
*/
tree = dbus_new0 (DBusObjectTree, 1);
if (tree == NULL)
goto oom;
tree->refcount = 1;
tree->connection = connection;
return tree;
oom:
if (tree)
{
dbus_free (tree);
}
return NULL;
}
void
_dbus_object_tree_ref (DBusObjectTree *tree)
{
_dbus_assert (tree->refcount > 0);
tree->refcount += 1;
}
void
_dbus_object_tree_unref (DBusObjectTree *tree)
{
_dbus_assert (tree->refcount > 0);
tree->refcount -= 1;
if (tree->refcount == 0)
{
if (tree->subtrees)
{
int i;
i = 0;
while (i < tree->n_subtrees)
{
_dbus_object_subtree_unref (tree->subtrees[i]);
++i;
}
}
dbus_free (tree);
}
}
static int
path_cmp (const char **path_a,
const char **path_b)
{
/* The comparison is as if the path were flattened
* into a single string. strcmp() considers
* a shorter string less than a longer string
* if the shorter string is the initial part
* of the longer
*/
int i;
i = 0;
while (path_a[i] != NULL)
{
int v;
if (path_b[i] == NULL)
return 1; /* a is longer than b */
_dbus_assert (path_a[i] != NULL);
_dbus_assert (path_b[i] != NULL);
v = strcmp (path_a[i], path_b[i]);
if (v != 0)
return v;
++i;
}
_dbus_assert (path_a[i] == NULL);
if (path_b[i] == NULL)
return 0;
/* b is longer than a */
return -1;
}
static int
subtree_cmp (DBusObjectSubtree *subtree_a,
DBusObjectSubtree *subtree_b)
{
return path_cmp ((const char**) subtree_a->path,
(const char**) subtree_b->path);
}
static int
subtree_qsort_cmp (const void *a,
const void *b)
{
DBusObjectSubtree **subtree_a_p = (void*) a;
DBusObjectSubtree **subtree_b_p = (void*) b;
return subtree_cmp (*subtree_a_p, *subtree_b_p);
}
/* Returns TRUE if a is a subdir of b or vice
* versa. This is the case if one is a subpath
* of the other.
*/
static dbus_bool_t
path_overlaps (const char **path_a,
const char **path_b)
{
int i;
i = 0;
while (path_a[i] != NULL)
{
int v;
if (path_b[i] == NULL)
return TRUE; /* b is subpath of a */
_dbus_assert (path_a[i] != NULL);
_dbus_assert (path_b[i] != NULL);
v = strcmp (path_a[i], path_b[i]);
if (v != 0)
return FALSE; /* they overlap until here and then are different,
* not overlapping
*/
++i;
}
/* b is either the same as or a superset of a */
_dbus_assert (path_a[i] == NULL);
return TRUE;
}
static dbus_bool_t
find_subtree (DBusObjectTree *tree,
const char **path,
int *idx_p)
{
int i;
if (tree->subtrees == NULL)
return FALSE;
if (!tree->subtrees_sorted)
{
qsort (tree->subtrees,
tree->n_subtrees,
sizeof (DBusObjectSubtree*),
subtree_qsort_cmp);
tree->subtrees_sorted = TRUE;
}
/* FIXME this should be a binary search,
* as that's the whole point of the sorting
*/
i = 0;
while (i < tree->n_subtrees)
{
int v;
v = path_cmp (path,
(const char**) tree->subtrees[i]->path);
if (v == 0)
{
if (idx_p)
*idx_p = i;
return TRUE;
}
else if (v > 0)
return FALSE;
++i;
}
return FALSE;
}
#ifndef DBUS_DISABLE_CHECKS
static void
check_overlap (DBusObjectTree *tree,
const char **path)
{
int i;
i = 0;
while (i < tree->n_subtrees)
{
if (path_overlaps (path, (const char**) tree->subtrees[i]->path))
{
_dbus_warn ("New path (path[0] = %s) overlaps old path (path[0] = %s)\n",
path[0], tree->subtrees[i]->path[0]);
}
++i;
}
}
#endif
/**
* Registers a new subtree in the global object tree.
*
* @param tree the global object tree
* @param path NULL-terminated array of path elements giving path to subtree
* @param vtable the vtable used to traverse this subtree
* @param user_data user data to pass to methods in the vtable
* @returns #FALSE if not enough memory
*/
dbus_bool_t
_dbus_object_tree_register (DBusObjectTree *tree,
const char **path,
const DBusObjectTreeVTable *vtable,
void *user_data)
{
DBusObjectSubtree *subtree;
DBusObjectSubtree **new_subtrees;
int new_n_subtrees;
_dbus_assert (path != NULL);
#ifndef DBUS_DISABLE_CHECKS
check_overlap (tree, path);
#endif
_dbus_assert (path[0] != NULL);
subtree = _dbus_object_subtree_new (path, vtable, user_data);
if (subtree == NULL)
return FALSE;
/* FIXME we should do the "double alloc each time" standard thing */
new_n_subtrees = tree->n_subtrees + 1;
new_subtrees = dbus_realloc (tree->subtrees,
new_n_subtrees);
if (new_subtrees == NULL)
{
_DBUS_ZERO (subtree->vtable); /* to avoid assertion in unref() */
_dbus_object_subtree_unref (subtree);
return FALSE;
}
tree->subtrees[tree->n_subtrees] = subtree;
tree->subtrees_sorted = FALSE;
tree->n_subtrees = new_n_subtrees;
tree->subtrees = new_subtrees;
return TRUE;
}
/**
* Unregisters an object subtree that was registered with the
* same path.
*
* @param tree the global object tree
* @param path path to the subtree (same as the one passed to _dbus_object_tree_register())
*/
void
_dbus_object_tree_unregister_and_unlock (DBusObjectTree *tree,
const char **path)
{
int i;
DBusObjectSubtree *subtree;
_dbus_assert (path != NULL);
_dbus_assert (path[0] != NULL);
if (!find_subtree (tree, path, &i))
{
_dbus_warn ("Attempted to unregister subtree (path[0] = %s) which isn't registered\n",
path[0]);
return;
}
subtree = tree->subtrees[i];
/* assumes a 0-byte memmove is OK */
memmove (&tree->subtrees[i],
&tree->subtrees[i+1],
(tree->n_subtrees - i - 1) * sizeof (tree->subtrees[0]));
tree->n_subtrees -= 1;
_dbus_object_subtree_ref (subtree);
/* Unlock and call application code */
_dbus_connection_unlock (tree->connection);
if (subtree->vtable.unregister_function)
{
(* subtree->vtable.unregister_function) (tree->connection,
(const char**) subtree->path,
subtree->user_data);
_DBUS_ZERO (subtree->vtable);
}
}
/**
* Tries to dispatch a message by directing it to the object tree
* node listed in the message header, if any.
*
* @param tree the global object tree
* @param message the message to dispatch
* @returns whether message was handled successfully
*/
DBusHandlerResult
_dbus_object_tree_dispatch_and_unlock (DBusObjectTree *tree,
DBusMessage *message)
{
}
DBusObjectSubtree*
_dbus_object_subtree_new (const char **path,
const DBusObjectTreeVTable *vtable,
void *user_data)
{
DBusObjectSubtree *subtree;
subtree = dbus_new0 (DBusObjectSubtree, 1);
if (subtree == NULL)
goto oom;
_dbus_assert (path != NULL);
_dbus_assert (path[0] != NULL);
subtree->path = _dbus_dup_string_array (path);
if (subtree->path == NULL)
goto oom;
subtree->vtable = *vtable;
subtree->user_data = user_data;
subtree->refcount = 1;
/* count path elements */
while (subtree->path[subtree->n_path_elements])
subtree->n_path_elements += 1;
return subtree;
oom:
if (subtree)
{
dbus_free_string_array (subtree->path);
dbus_free (subtree);
}
return NULL;
}
void
_dbus_object_subtree_ref (DBusObjectSubtree *subtree)
{
_dbus_assert (subtree->refcount > 0);
subtree->refcount += 1;
}
void
_dbus_object_subtree_unref (DBusObjectSubtree *subtree)
{
_dbus_assert (subtree->refcount > 0);
subtree->refcount -= 1;
if (subtree->refcount == 0)
{
_dbus_assert (subtree->vtable.unregister_function == NULL);
dbus_free_string_array (subtree->path);
dbus_free (subtree);
}
}
/** @} */
#ifdef DBUS_BUILD_TESTS
#include "dbus-test.h"
#include <stdio.h>
static dbus_bool_t
test_subtree_cmp (const char **path1,
const char **path2,
int expected,
dbus_bool_t reverse)
{
DBusObjectSubtree *subtree1;
DBusObjectSubtree *subtree2;
dbus_bool_t retval;
DBusObjectTreeVTable vtable;
_DBUS_ZERO (vtable);
retval = FALSE;
subtree1 = _dbus_object_subtree_new (path1, &vtable, NULL);
subtree2 = _dbus_object_subtree_new (path2, &vtable, NULL);
if (subtree1 == NULL || subtree2 == NULL)
goto out;
_dbus_assert (subtree_cmp (subtree1, subtree2) == expected);
retval = TRUE;
out:
if (subtree1)
_dbus_object_subtree_unref (subtree1);
if (subtree2)
_dbus_object_subtree_unref (subtree2);
if (retval && reverse)
{
/* Verify that the reverse also holds */
if (expected > 0)
return test_subtree_cmp (path2, path1, -1, FALSE);
else if (expected < 0)
return test_subtree_cmp (path2, path1, 1, FALSE);
else
return test_subtree_cmp (path2, path1, 0, FALSE);
}
return retval;
}
static void
test_path_overlap (const char **path1,
const char **path2,
dbus_bool_t expected)
{
_dbus_assert (path_overlaps (path1, path2) == expected);
_dbus_assert (path_overlaps (path2, path1) == expected);
}
static dbus_bool_t
object_tree_test_iteration (void *data)
{
const char *path1[] = { "foo", NULL };
const char *path2[] = { "foo", "bar", NULL };
const char *path3[] = { "foo", "bar", "baz", NULL };
const char *path4[] = { "foo", "bar", "boo", NULL };
const char *path5[] = { "blah", NULL };
DBusObjectSubtree *subtree1;
DBusObjectSubtree *subtree2;
DBusObjectTree *tree;
tree = NULL;
subtree1 = NULL;
subtree2 = NULL;
test_path_overlap (path1, path1, TRUE);
test_path_overlap (path1, path2, TRUE);
test_path_overlap (path1, path3, TRUE);
test_path_overlap (path1, path4, TRUE);
test_path_overlap (path1, path5, FALSE);
test_path_overlap (path2, path2, TRUE);
test_path_overlap (path2, path3, TRUE);
test_path_overlap (path2, path4, TRUE);
test_path_overlap (path2, path5, FALSE);
test_path_overlap (path3, path3, TRUE);
test_path_overlap (path3, path4, FALSE);
test_path_overlap (path3, path5, FALSE);
test_path_overlap (path4, path4, TRUE);
test_path_overlap (path4, path5, FALSE);
test_path_overlap (path5, path5, TRUE);
if (!test_subtree_cmp (path1, path1, 0, TRUE))
goto out;
if (!test_subtree_cmp (path3, path3, 0, TRUE))
goto out;
/* When testing -1, the reverse also gets tested */
if (!test_subtree_cmp (path1, path2, -1, TRUE))
goto out;
if (!test_subtree_cmp (path1, path3, -1, TRUE))
goto out;
if (!test_subtree_cmp (path2, path3, -1, TRUE))
goto out;
if (!test_subtree_cmp (path2, path4, -1, TRUE))
goto out;
if (!test_subtree_cmp (path3, path4, -1, TRUE))
goto out;
if (!test_subtree_cmp (path5, path1, -1, TRUE))
goto out;
tree = _dbus_object_tree_new (NULL);
if (tree == NULL)
goto out;
out:
if (subtree1)
_dbus_object_subtree_unref (subtree1);
if (subtree2)
_dbus_object_subtree_unref (subtree2);
if (tree)
_dbus_object_tree_unref (tree);
return TRUE;
}
/**
* @ingroup DBusObjectTree
* Unit test for DBusObjectTree
* @returns #TRUE on success.
*/
dbus_bool_t
_dbus_object_tree_test (void)
{
_dbus_test_oom_handling ("object tree",
object_tree_test_iteration,
NULL);
return TRUE;
}
#endif /* DBUS_BUILD_TESTS */

48
dbus/dbus-object-tree.h Normal file
View file

@ -0,0 +1,48 @@
/* -*- mode: C; c-file-style: "gnu" -*- */
/* dbus-object-tree.h DBusObjectTree (internals of DBusConnection)
*
* Copyright (C) 2003 Red Hat Inc.
*
* Licensed under the Academic Free License version 1.2
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#ifndef DBUS_OBJECT_TREE_H
#define DBUS_OBJECT_TREE_H
#include <dbus/dbus-connection.h>
DBUS_BEGIN_DECLS;
typedef struct DBusObjectTree DBusObjectTree;
DBusObjectTree* _dbus_object_tree_new (DBusConnection *connection);
void _dbus_object_tree_ref (DBusObjectTree *tree);
void _dbus_object_tree_unref (DBusObjectTree *tree);
dbus_bool_t _dbus_object_tree_register (DBusObjectTree *tree,
const char **path,
const DBusObjectTreeVTable *vtable,
void *user_data);
void _dbus_object_tree_unregister_and_unlock (DBusObjectTree *tree,
const char **path);
DBusHandlerResult _dbus_object_tree_dispatch_and_unlock (DBusObjectTree *tree,
DBusMessage *message);
DBUS_END_DECLS;
#endif /* DBUS_OBJECT_TREE_H */

View file

@ -1002,9 +1002,9 @@ _dbus_string_append_printf_valist (DBusString *str,
const char *format,
va_list args)
{
DBUS_STRING_PREAMBLE (str);
int len;
char c;
DBUS_STRING_PREAMBLE (str);
/* Measure the message length without terminating nul */
len = vsnprintf (&c, 1, format, args);

View file

@ -112,6 +112,12 @@ dbus_internal_do_not_use_run_tests (const char *test_data_dir)
check_memleaks ();
printf ("%s: running object tree tests\n", "dbus-test");
if (!_dbus_object_tree_test ())
die ("object tree");
check_memleaks ();
printf ("%s: running object tests\n", "dbus-test");
if (!_dbus_object_test ())
die ("object");

View file

@ -56,6 +56,7 @@ dbus_bool_t _dbus_memory_test (void);
dbus_bool_t _dbus_object_test (void);
dbus_bool_t _dbus_object_id_test (void);
dbus_bool_t _dbus_object_registry_test (void);
dbus_bool_t _dbus_object_tree_test (void);
dbus_bool_t _dbus_pending_call_test (const char *test_data_dir);
void dbus_internal_do_not_use_run_tests (const char *test_data_dir);